← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~alisonken1/openlp/pjlink2-n into lp:openlp

 

Ken Roberts has proposed merging lp:~alisonken1/openlp/pjlink2-n into lp:openlp.

Commit message:
PJLink 2 update N

Requested reviews:
  OpenLP Core (openlp-core)

For more details, see:
https://code.launchpad.net/~alisonken1/openlp/pjlink2-n/+merge/335587

Jenkins cli at home dies at '02b-macOS-tests' [waiting], then timesout.

Passes local tests (pytest, nose2, pep8) with exception of
test_bible_gateway_extract_books_support_redirect

Large diff fixing test_projector_pjlink_commands_01 file

- Update some comments
- Some PJLink code fixes/restructuring
- Restructure some projector tests for proper mocking
- Fix manager.py and pjlink.py for changes to constants.py
- Fix test_projector_pjlink_routing.py for proper mocking and restructuring
- Renamed test_process_command_invalid to test_get_data_unknown_command
- Added test to verify assigned E_*/S_* codes are in STATUS_CODE and STATUS_MSG
- Added missing status/error codes to STATUS_MSG
- Added error checks to process_inpt
- Deleted unneeded test(s)

Updates to constants.py include:
    - Reordered dictionaries and lists into alphabetical order
    - Added PJLINK_ERST_LIST
    - Added S_NETWORK_IDLE code (for future)
    - Added PJLINK_STATUS list
    - Move S_QSOCKET_STATUS to QSOCKET_STATE
    - Move STATUS_STRING to STATUS_CODE
    - Move PJLINK_CLASS, PJLINK_SUFFIX, and PJLINK_PREFIX from pjlink.py to constants.py
    - Change CONNECTION_ERRORS from a dictionary to a list
    - Change QSOCKET_STATE to map QAbstractSocket.state() to local status codes
    - Merge ERROR_MSG with STATUS_MSG
    - Merge ERROR_STRING into STATUS_CODE
    - Clean out unused portion of PJLINK_ERST_STATUS
    - Clean out unused portion of PJLINK_POWR_STATUS
    - Clean out unused portion of QSOCKET_STATUS

-- 
Your team OpenLP Core is requested to review the proposed merge of lp:~alisonken1/openlp/pjlink2-n into lp:openlp.
=== modified file 'openlp/core/projectors/constants.py'
--- openlp/core/projectors/constants.py	2017-12-09 11:17:05 +0000
+++ openlp/core/projectors/constants.py	2017-12-25 08:57:51 +0000
@@ -32,9 +32,128 @@
 # Set common constants.
 CR = chr(0x0D)  # \r
 LF = chr(0x0A)  # \n
+PJLINK_CLASS = '1'  # Default to class 1 until we query the projector
+PJLINK_MAX_PACKET = 136
+PJLINK_PREFIX = '%'
 PJLINK_PORT = 4352
-TIMEOUT = 30.0
-PJLINK_MAX_PACKET = 136
+PJLINK_SUFFIX = CR
+PJLINK_TIMEOUT = 30.0
+
+# Error and status codes
+S_OK = E_OK = 0  # E_OK included since I sometimes forget
+
+# Error codes. Start at 200 so we don't duplicate system error codes.
+E_GENERAL = 200  # Unknown error
+E_NOT_CONNECTED = 201
+E_UNDEFINED = 202           # PJLink ERR1
+E_PARAMETER = 203           # PJLink ERR2
+E_UNAVAILABLE = 204         # PJLink ERR3
+E_PROJECTOR = 205           # PJLink ERR4
+E_AUTHENTICATION = 206      # PJLink ERRA
+E_NO_AUTHENTICATION = 207   # PJLink authentication mismatch between projector and program
+E_PREFIX = 208              # PJLink invalid prefix for packet
+E_CLASS = 209               # PJLink class version mismatch
+E_INVALID_DATA = 210
+E_WARN = 211
+E_ERROR = 212
+E_FAN = 213
+E_LAMP = 214
+E_TEMP = 215
+E_COVER = 216
+E_FILTER = 217
+E_UNKNOWN = 218
+
+# Remap Qt socket error codes to local error codes
+E_CONNECTION_REFUSED = 230
+E_REMOTE_HOST_CLOSED_CONNECTION = 231
+E_HOST_NOT_FOUND = 232
+E_SOCKET_ACCESS = 233
+E_SOCKET_RESOURCE = 234
+E_SOCKET_TIMEOUT = 235
+E_DATAGRAM_TOO_LARGE = 236
+E_NETWORK = 237
+E_ADDRESS_IN_USE = 238
+E_SOCKET_ADDRESS_NOT_AVAILABLE = 239
+E_UNSUPPORTED_SOCKET_OPERATION = 240
+E_PROXY_AUTHENTICATION_REQUIRED = 241
+E_SLS_HANDSHAKE_FAILED = 242
+E_UNFINISHED_SOCKET_OPERATION = 243
+E_PROXY_CONNECTION_REFUSED = 244
+E_PROXY_CONNECTION_CLOSED = 245
+E_PROXY_CONNECTION_TIMEOUT = 246
+E_PROXY_NOT_FOUND = 247
+E_PROXY_PROTOCOL = 248
+E_UNKNOWN_SOCKET_ERROR = 249
+
+# Status codes start at 300
+
+# Remap Qt socket states to local status codes
+S_NOT_CONNECTED = 300
+S_HOST_LOOKUP = 301
+S_CONNECTING = 302
+S_CONNECTED = 303
+S_BOUND = 304
+S_LISTENING = 305  # Listed as internal use only in QAbstractSocket
+S_CLOSING = 306
+
+# Projector states
+S_INITIALIZE = 310
+S_STATUS = 311
+S_OFF = 312
+S_STANDBY = 313
+S_WARMUP = 314
+S_ON = 315
+S_COOLDOWN = 316
+S_INFO = 317
+
+# Information that does not affect status
+S_NETWORK_IDLE = 400
+S_NETWORK_SENDING = 401
+S_NETWORK_RECEIVING = 402
+
+# Map PJlink errors to local status
+PJLINK_ERRORS = {
+    'ERRA': E_AUTHENTICATION,   # Authentication error
+    'ERR1': E_UNDEFINED,        # Undefined command error
+    'ERR2': E_PARAMETER,        # Invalid parameter error
+    'ERR3': E_UNAVAILABLE,      # Projector busy
+    'ERR4': E_PROJECTOR,        # Projector or display failure
+    E_AUTHENTICATION: 'ERRA',
+    E_UNDEFINED: 'ERR1',
+    E_PARAMETER: 'ERR2',
+    E_UNAVAILABLE: 'ERR3',
+    E_PROJECTOR: 'ERR4'
+}
+
+# Map QAbstractSocketState enums to local status
+QSOCKET_STATE = {
+    0: S_NOT_CONNECTED,     # 'UnconnectedState',
+    1: S_HOST_LOOKUP,       # 'HostLookupState',
+    2: S_CONNECTING,        # 'ConnectingState',
+    3: S_CONNECTED,         # 'ConnectedState',
+    4: S_BOUND,             # 'BoundState',
+    5: S_LISTENING,         # 'ListeningState' -  Noted as "Internal Use Only" on Qt website
+    6: S_CLOSING,           # 'ClosingState',
+    S_NOT_CONNECTED: 0,
+    S_HOST_LOOKUP: 1,
+    S_CONNECTING: 2,
+    S_CONNECTED: 3,
+    S_BOUND: 4,
+    S_LISTENING: 5,
+    S_CLOSING: 6
+}
+
+PROJECTOR_STATE = [
+    S_INITIALIZE,
+    S_STATUS,
+    S_OFF,
+    S_STANDBY,
+    S_WARMUP,
+    S_ON,
+    S_COOLDOWN,
+    S_INFO
+]
+
 # NOTE: Changed format to account for some commands are both class 1 and 2
 PJLINK_VALID_CMD = {
     'ACKN': {'version': ['2', ],
@@ -144,227 +263,140 @@
              }
 }
 
-# QAbstractSocketState enums converted to string
-S_QSOCKET_STATE = {
-    0: 'QSocketState - UnconnectedState',
-    1: 'QSocketState - HostLookupState',
-    2: 'QSocketState - ConnectingState',
-    3: 'QSocketState - ConnectedState',
-    4: 'QSocketState - BoundState',
-    5: 'QSocketState - ListeningState (internal use only)',
-    6: 'QSocketState - ClosingState',
-    'UnconnectedState': 0,
-    'HostLookupState': 1,
-    'ConnectingState': 2,
-    'ConnectedState': 3,
-    'BoundState': 4,
-    'ListeningState': 5,
-    'ClosingState': 6
-}
-
-# Error and status codes
-S_OK = E_OK = 0  # E_OK included since I sometimes forget
-# Error codes. Start at 200 so we don't duplicate system error codes.
-E_GENERAL = 200  # Unknown error
-E_NOT_CONNECTED = 201
-E_FAN = 202
-E_LAMP = 203
-E_TEMP = 204
-E_COVER = 205
-E_FILTER = 206
-E_NO_AUTHENTICATION = 207  # PIN set and no authentication set on projector
-E_UNDEFINED = 208       # ERR1
-E_PARAMETER = 209       # ERR2
-E_UNAVAILABLE = 210     # ERR3
-E_PROJECTOR = 211       # ERR4
-E_INVALID_DATA = 212
-E_WARN = 213
-E_ERROR = 214
-E_AUTHENTICATION = 215  # ERRA
-E_CLASS = 216
-E_PREFIX = 217
-
-# Remap Qt socket error codes to projector error codes
-E_CONNECTION_REFUSED = 230
-E_REMOTE_HOST_CLOSED_CONNECTION = 231
-E_HOST_NOT_FOUND = 232
-E_SOCKET_ACCESS = 233
-E_SOCKET_RESOURCE = 234
-E_SOCKET_TIMEOUT = 235
-E_DATAGRAM_TOO_LARGE = 236
-E_NETWORK = 237
-E_ADDRESS_IN_USE = 238
-E_SOCKET_ADDRESS_NOT_AVAILABLE = 239
-E_UNSUPPORTED_SOCKET_OPERATION = 240
-E_PROXY_AUTHENTICATION_REQUIRED = 241
-E_SLS_HANDSHAKE_FAILED = 242
-E_UNFINISHED_SOCKET_OPERATION = 243
-E_PROXY_CONNECTION_REFUSED = 244
-E_PROXY_CONNECTION_CLOSED = 245
-E_PROXY_CONNECTION_TIMEOUT = 246
-E_PROXY_NOT_FOUND = 247
-E_PROXY_PROTOCOL = 248
-E_UNKNOWN_SOCKET_ERROR = -1
-
-# Status codes start at 300
-S_NOT_CONNECTED = 300
-S_CONNECTING = 301
-S_CONNECTED = 302
-S_INITIALIZE = 303
-S_STATUS = 304
-S_OFF = 305
-S_STANDBY = 306
-S_WARMUP = 307
-S_ON = 308
-S_COOLDOWN = 309
-S_INFO = 310
-
-# Information that does not affect status
-S_NETWORK_SENDING = 400
-S_NETWORK_RECEIVED = 401
-
-CONNECTION_ERRORS = {
-    E_NOT_CONNECTED, E_NO_AUTHENTICATION, E_AUTHENTICATION, E_CLASS,
-    E_PREFIX, E_CONNECTION_REFUSED, E_REMOTE_HOST_CLOSED_CONNECTION,
-    E_HOST_NOT_FOUND, E_SOCKET_ACCESS, E_SOCKET_RESOURCE, E_SOCKET_TIMEOUT,
-    E_DATAGRAM_TOO_LARGE, E_NETWORK, E_ADDRESS_IN_USE, E_SOCKET_ADDRESS_NOT_AVAILABLE,
-    E_UNSUPPORTED_SOCKET_OPERATION, E_PROXY_AUTHENTICATION_REQUIRED,
-    E_SLS_HANDSHAKE_FAILED, E_UNFINISHED_SOCKET_OPERATION, E_PROXY_CONNECTION_REFUSED,
-    E_PROXY_CONNECTION_CLOSED, E_PROXY_CONNECTION_TIMEOUT, E_PROXY_NOT_FOUND,
-    E_PROXY_PROTOCOL, E_UNKNOWN_SOCKET_ERROR
-}
-
-PJLINK_ERRORS = {
-    'ERRA': E_AUTHENTICATION,   # Authentication error
-    'ERR1': E_UNDEFINED,        # Undefined command error
-    'ERR2': E_PARAMETER,        # Invalid parameter error
-    'ERR3': E_UNAVAILABLE,      # Projector busy
-    'ERR4': E_PROJECTOR,        # Projector or display failure
-    E_AUTHENTICATION: 'ERRA',
-    E_UNDEFINED: 'ERR1',
-    E_PARAMETER: 'ERR2',
-    E_UNAVAILABLE: 'ERR3',
-    E_PROJECTOR: 'ERR4'
-}
-
-# Map error/status codes to string
-ERROR_STRING = {
-    0: 'S_OK',
+CONNECTION_ERRORS = [
+    E_ADDRESS_IN_USE,
+    E_CONNECTION_REFUSED,
+    E_DATAGRAM_TOO_LARGE,
+    E_HOST_NOT_FOUND,
+    E_NETWORK,
+    E_NOT_CONNECTED,
+    E_PROXY_AUTHENTICATION_REQUIRED,
+    E_PROXY_CONNECTION_CLOSED,
+    E_PROXY_CONNECTION_REFUSED,
+    E_PROXY_CONNECTION_TIMEOUT,
+    E_PROXY_NOT_FOUND,
+    E_PROXY_PROTOCOL,
+    E_REMOTE_HOST_CLOSED_CONNECTION,
+    E_SLS_HANDSHAKE_FAILED,
+    E_SOCKET_ACCESS,
+    E_SOCKET_ADDRESS_NOT_AVAILABLE,
+    E_SOCKET_RESOURCE,
+    E_SOCKET_TIMEOUT,
+    E_UNFINISHED_SOCKET_OPERATION,
+    E_UNKNOWN_SOCKET_ERROR,
+    E_UNSUPPORTED_SOCKET_OPERATION
+]
+
+PROJECTOR_ERRORS = [
+    E_AUTHENTICATION,
+    E_CLASS,
+    E_INVALID_DATA,
+    E_NO_AUTHENTICATION,
+    E_PARAMETER,
+    E_PREFIX,
+    E_PROJECTOR,
+    E_UNAVAILABLE,
+    E_UNDEFINED,
+    E_UNKNOWN
+]
+
+# Show status code as string
+STATUS_CODE = {
+    E_ADDRESS_IN_USE: 'E_ADDRESS_IN_USE',
+    E_AUTHENTICATION: 'E_AUTHENTICATION',
+    E_CLASS: 'E_CLASS',
+    E_CONNECTION_REFUSED: 'E_CONNECTION_REFUSED',
+    E_COVER: 'E_COVER',
+    E_DATAGRAM_TOO_LARGE: 'E_DATAGRAM_TOO_LARGE',
+    E_ERROR: 'E_ERROR',
+    E_FAN: 'E_FAN',
+    E_FILTER: 'E_FILTER',
     E_GENERAL: 'E_GENERAL',
-    E_NOT_CONNECTED: 'E_NOT_CONNECTED',
-    E_FAN: 'E_FAN',
+    E_HOST_NOT_FOUND: 'E_HOST_NOT_FOUND',
+    E_INVALID_DATA: 'E_INVALID_DATA',
     E_LAMP: 'E_LAMP',
-    E_TEMP: 'E_TEMP',
-    E_COVER: 'E_COVER',
-    E_FILTER: 'E_FILTER',
-    E_AUTHENTICATION: 'E_AUTHENTICATION',
+    E_NETWORK: 'E_NETWORK',
     E_NO_AUTHENTICATION: 'E_NO_AUTHENTICATION',
-    E_UNDEFINED: 'E_UNDEFINED',
+    E_NOT_CONNECTED: 'E_NOT_CONNECTED',
     E_PARAMETER: 'E_PARAMETER',
-    E_UNAVAILABLE: 'E_UNAVAILABLE',
+    E_PREFIX: 'E_PREFIX',
     E_PROJECTOR: 'E_PROJECTOR',
-    E_INVALID_DATA: 'E_INVALID_DATA',
-    E_WARN: 'E_WARN',
-    E_ERROR: 'E_ERROR',
-    E_CLASS: 'E_CLASS',
-    E_PREFIX: 'E_PREFIX',  # Last projector error
-    E_CONNECTION_REFUSED: 'E_CONNECTION_REFUSED',  # First QtSocket error
+    E_PROXY_AUTHENTICATION_REQUIRED: 'E_PROXY_AUTHENTICATION_REQUIRED',
+    E_PROXY_CONNECTION_CLOSED: 'E_PROXY_CONNECTION_CLOSED',
+    E_PROXY_CONNECTION_REFUSED: 'E_PROXY_CONNECTION_REFUSED',
+    E_PROXY_CONNECTION_TIMEOUT: 'E_PROXY_CONNECTION_TIMEOUT',
+    E_PROXY_NOT_FOUND: 'E_PROXY_NOT_FOUND',
+    E_PROXY_PROTOCOL: 'E_PROXY_PROTOCOL',
     E_REMOTE_HOST_CLOSED_CONNECTION: 'E_REMOTE_HOST_CLOSED_CONNECTION',
-    E_HOST_NOT_FOUND: 'E_HOST_NOT_FOUND',
+    E_SLS_HANDSHAKE_FAILED: 'E_SLS_HANDSHAKE_FAILED',
     E_SOCKET_ACCESS: 'E_SOCKET_ACCESS',
+    E_SOCKET_ADDRESS_NOT_AVAILABLE: 'E_SOCKET_ADDRESS_NOT_AVAILABLE',
     E_SOCKET_RESOURCE: 'E_SOCKET_RESOURCE',
     E_SOCKET_TIMEOUT: 'E_SOCKET_TIMEOUT',
-    E_DATAGRAM_TOO_LARGE: 'E_DATAGRAM_TOO_LARGE',
-    E_NETWORK: 'E_NETWORK',
-    E_ADDRESS_IN_USE: 'E_ADDRESS_IN_USE',
-    E_SOCKET_ADDRESS_NOT_AVAILABLE: 'E_SOCKET_ADDRESS_NOT_AVAILABLE',
+    E_TEMP: 'E_TEMP',
+    E_UNAVAILABLE: 'E_UNAVAILABLE',
+    E_UNDEFINED: 'E_UNDEFINED',
+    E_UNFINISHED_SOCKET_OPERATION: 'E_UNFINISHED_SOCKET_OPERATION',
+    E_UNKNOWN: 'E_UNKNOWN',
+    E_UNKNOWN_SOCKET_ERROR: 'E_UNKNOWN_SOCKET_ERROR',
     E_UNSUPPORTED_SOCKET_OPERATION: 'E_UNSUPPORTED_SOCKET_OPERATION',
-    E_PROXY_AUTHENTICATION_REQUIRED: 'E_PROXY_AUTHENTICATION_REQUIRED',
-    E_SLS_HANDSHAKE_FAILED: 'E_SLS_HANDSHAKE_FAILED',
-    E_UNFINISHED_SOCKET_OPERATION: 'E_UNFINISHED_SOCKET_OPERATION',
-    E_PROXY_CONNECTION_REFUSED: 'E_PROXY_CONNECTION_REFUSED',
-    E_PROXY_CONNECTION_CLOSED: 'E_PROXY_CONNECTION_CLOSED',
-    E_PROXY_CONNECTION_TIMEOUT: 'E_PROXY_CONNECTION_TIMEOUT',
-    E_PROXY_NOT_FOUND: 'E_PROXY_NOT_FOUND',
-    E_PROXY_PROTOCOL: 'E_PROXY_PROTOCOL',
-    E_UNKNOWN_SOCKET_ERROR: 'E_UNKNOWN_SOCKET_ERROR'
-}
-
-STATUS_STRING = {
+    E_WARN: 'E_WARN',
+    S_BOUND: 'S_BOUND',
+    S_COOLDOWN: 'S_COOLDOWN',
+    S_CLOSING: 'S_CLOSING',
+    S_CONNECTED: 'S_CONNECTED',
+    S_CONNECTING: 'S_CONNECTING',
+    S_HOST_LOOKUP: 'S_HOST_LOOKUP',
+    S_INFO: 'S_INFO',
+    S_INITIALIZE: 'S_INITIALIZE',
+    S_LISTENING: 'S_LISTENING',
+    S_NETWORK_RECEIVING: 'S_NETWORK_RECEIVING',
+    S_NETWORK_SENDING: 'S_NETWORK_SENDING',
+    S_NETWORK_IDLE: 'S_NETWORK_IDLE',
     S_NOT_CONNECTED: 'S_NOT_CONNECTED',
-    S_CONNECTING: 'S_CONNECTING',
-    S_CONNECTED: 'S_CONNECTED',
-    S_STATUS: 'S_STATUS',
     S_OFF: 'S_OFF',
-    S_INITIALIZE: 'S_INITIALIZE',
+    S_OK: 'S_OK',  # S_OK or E_OK
+    S_ON: 'S_ON',
     S_STANDBY: 'S_STANDBY',
+    S_STATUS: 'S_STATUS',
     S_WARMUP: 'S_WARMUP',
-    S_ON: 'S_ON',
-    S_COOLDOWN: 'S_COOLDOWN',
-    S_INFO: 'S_INFO',
-    S_NETWORK_SENDING: 'S_NETWORK_SENDING',
-    S_NETWORK_RECEIVED: 'S_NETWORK_RECEIVED'
 }
 
-# Map error/status codes to message strings
-ERROR_MSG = {
-    E_OK: translate('OpenLP.ProjectorConstants', 'OK'),  # E_OK | S_OK
-    E_GENERAL: translate('OpenLP.ProjectorConstants', 'General projector error'),
-    E_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Not connected error'),
-    E_LAMP: translate('OpenLP.ProjectorConstants', 'Lamp error'),
-    E_FAN: translate('OpenLP.ProjectorConstants', 'Fan error'),
-    E_TEMP: translate('OpenLP.ProjectorConstants', 'High temperature detected'),
-    E_COVER: translate('OpenLP.ProjectorConstants', 'Cover open detected'),
-    E_FILTER: translate('OpenLP.ProjectorConstants', 'Check filter'),
-    E_AUTHENTICATION: translate('OpenLP.ProjectorConstants', 'Authentication Error'),
-    E_UNDEFINED: translate('OpenLP.ProjectorConstants', 'Undefined Command'),
-    E_PARAMETER: translate('OpenLP.ProjectorConstants', 'Invalid Parameter'),
-    E_UNAVAILABLE: translate('OpenLP.ProjectorConstants', 'Projector Busy'),
-    E_PROJECTOR: translate('OpenLP.ProjectorConstants', 'Projector/Display Error'),
-    E_INVALID_DATA: translate('OpenLP.ProjectorConstants', 'Invalid packet received'),
-    E_WARN: translate('OpenLP.ProjectorConstants', 'Warning condition detected'),
-    E_ERROR: translate('OpenLP.ProjectorConstants', 'Error condition detected'),
-    E_CLASS: translate('OpenLP.ProjectorConstants', 'PJLink class not supported'),
-    E_PREFIX: translate('OpenLP.ProjectorConstants', 'Invalid prefix character'),
+# Map status codes to message strings
+STATUS_MSG = {
+    E_ADDRESS_IN_USE: translate('OpenLP.ProjectorConstants',
+                                'The address specified with socket.bind() '
+                                'is already in use and was set to be exclusive'),
+    E_AUTHENTICATION: translate('OpenLP.ProjectorConstants', 'PJLink returned "ERRA: Authentication Error"'),
     E_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants',
                                     'The connection was refused by the peer (or timed out)'),
-    E_REMOTE_HOST_CLOSED_CONNECTION: translate('OpenLP.ProjectorConstants',
-                                               'The remote host closed the connection'),
+    E_COVER: translate('OpenLP.ProjectorConstants', 'Projector cover open detected'),
+    E_CLASS: translate('OpenLP.ProjectorConstants', 'PJLink class not supported'),
+    E_DATAGRAM_TOO_LARGE: translate('OpenLP.ProjectorConstants',
+                                    "The datagram was larger than the operating system's limit"),
+    E_ERROR: translate('OpenLP.ProjectorConstants', 'Error condition detected'),
+    E_FAN: translate('OpenLP.ProjectorConstants', 'Projector fan error'),
+    E_FILTER: translate('OpenLP.ProjectorConstants', 'Projector check filter'),
+    E_GENERAL: translate('OpenLP.ProjectorConstants', 'General projector error'),
     E_HOST_NOT_FOUND: translate('OpenLP.ProjectorConstants', 'The host address was not found'),
-    E_SOCKET_ACCESS: translate('OpenLP.ProjectorConstants',
-                               'The socket operation failed because the application '
-                               'lacked the required privileges'),
-    E_SOCKET_RESOURCE: translate('OpenLP.ProjectorConstants',
-                                 'The local system ran out of resources (e.g., too many sockets)'),
-    E_SOCKET_TIMEOUT: translate('OpenLP.ProjectorConstants',
-                                'The socket operation timed out'),
-    E_DATAGRAM_TOO_LARGE: translate('OpenLP.ProjectorConstants',
-                                    'The datagram was larger than the operating system\'s limit'),
+    E_INVALID_DATA: translate('OpenLP.ProjectorConstants', 'PJLink invalid packet received'),
+    E_LAMP: translate('OpenLP.ProjectorConstants', 'Projector lamp error'),
     E_NETWORK: translate('OpenLP.ProjectorConstants',
                          'An error occurred with the network (Possibly someone pulled the plug?)'),
-    E_ADDRESS_IN_USE: translate('OpenLP.ProjectorConstants',
-                                'The address specified with socket.bind() '
-                                'is already in use and was set to be exclusive'),
-    E_SOCKET_ADDRESS_NOT_AVAILABLE: translate('OpenLP.ProjectorConstants',
-                                              'The address specified to socket.bind() '
-                                              'does not belong to the host'),
-    E_UNSUPPORTED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants',
-                                              'The requested socket operation is not supported by the local '
-                                              'operating system (e.g., lack of IPv6 support)'),
+    E_NO_AUTHENTICATION: translate('OpenLP.ProjectorConstants', 'PJlink authentication Mismatch Error'),
+    E_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Projector not connected error'),
+    E_PARAMETER: translate('OpenLP.ProjectorConstants', 'PJLink returned "ERR2: Invalid Parameter"'),
+    E_PREFIX: translate('OpenLP.ProjectorConstants', 'PJLink Invalid prefix character'),
+    E_PROJECTOR: translate('OpenLP.ProjectorConstants', 'PJLink returned "ERR4: Projector/Display Error"'),
     E_PROXY_AUTHENTICATION_REQUIRED: translate('OpenLP.ProjectorConstants',
                                                'The socket is using a proxy, '
                                                'and the proxy requires authentication'),
-    E_SLS_HANDSHAKE_FAILED: translate('OpenLP.ProjectorConstants',
-                                      'The SSL/TLS handshake failed'),
-    E_UNFINISHED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants',
-                                             'The last operation attempted has not finished yet '
-                                             '(still in progress in the background)'),
+    E_PROXY_CONNECTION_CLOSED: translate('OpenLP.ProjectorConstants',
+                                         'The connection to the proxy server was closed unexpectedly '
+                                         '(before the connection to the final peer was established)'),
     E_PROXY_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants',
                                           'Could not contact the proxy server because the connection '
                                           'to that server was denied'),
-    E_PROXY_CONNECTION_CLOSED: translate('OpenLP.ProjectorConstants',
-                                         'The connection to the proxy server was closed unexpectedly '
-                                         '(before the connection to the final peer was established)'),
     E_PROXY_CONNECTION_TIMEOUT: translate('OpenLP.ProjectorConstants',
                                           'The connection to the proxy server timed out or the proxy '
                                           'server stopped responding in the authentication phase.'),
@@ -373,51 +405,91 @@
     E_PROXY_PROTOCOL: translate('OpenLP.ProjectorConstants',
                                 'The connection negotiation with the proxy server failed because the '
                                 'response from the proxy server could not be understood'),
-    E_UNKNOWN_SOCKET_ERROR: translate('OpenLP.ProjectorConstants', 'An unidentified error occurred'),
-    S_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Not connected'),
+    E_REMOTE_HOST_CLOSED_CONNECTION: translate('OpenLP.ProjectorConstants',
+                                               'The remote host closed the connection'),
+    E_SLS_HANDSHAKE_FAILED: translate('OpenLP.ProjectorConstants',
+                                      'The SSL/TLS handshake failed'),
+    E_SOCKET_ADDRESS_NOT_AVAILABLE: translate('OpenLP.ProjectorConstants',
+                                              'The address specified to socket.bind() '
+                                              'does not belong to the host'),
+    E_SOCKET_ACCESS: translate('OpenLP.ProjectorConstants',
+                               'The socket operation failed because the application '
+                               'lacked the required privileges'),
+    E_SOCKET_RESOURCE: translate('OpenLP.ProjectorConstants',
+                                 'The local system ran out of resources (e.g., too many sockets)'),
+    E_SOCKET_TIMEOUT: translate('OpenLP.ProjectorConstants',
+                                'The socket operation timed out'),
+    E_TEMP: translate('OpenLP.ProjectorConstants', 'Projector high temperature detected'),
+    E_UNAVAILABLE: translate('OpenLP.ProjectorConstants', 'PJLink returned "ERR3: Busy"'),
+    E_UNDEFINED: translate('OpenLP.ProjectorConstants', 'PJLink returned "ERR1: Undefined Command"'),
+    E_UNFINISHED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants',
+                                             'The last operation attempted has not finished yet '
+                                             '(still in progress in the background)'),
+    E_UNKNOWN: translate('OpenLP.ProjectorConstants', 'Unknown condiction detected'),
+    E_UNKNOWN_SOCKET_ERROR: translate('OpenLP.ProjectorConstants', 'An unidentified socket error occurred'),
+    E_UNSUPPORTED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants',
+                                              'The requested socket operation is not supported by the local '
+                                              'operating system (e.g., lack of IPv6 support)'),
+    E_WARN: translate('OpenLP.ProjectorConstants', 'Warning condition detected'),
+    S_BOUND: translate('OpenLP.ProjectorConstants', 'Socket is bount to an address or port'),
+    S_CLOSING: translate('OpenLP.ProjectorConstants', 'Socket is about to close'),
+    S_CONNECTED: translate('OpenLP.ProjectorConstants', 'Connected'),
     S_CONNECTING: translate('OpenLP.ProjectorConstants', 'Connecting'),
-    S_CONNECTED: translate('OpenLP.ProjectorConstants', 'Connected'),
-    S_STATUS: translate('OpenLP.ProjectorConstants', 'Getting status'),
+    S_COOLDOWN: translate('OpenLP.ProjectorConstants', 'Cooldown in progress'),
+    S_HOST_LOOKUP: translate('OpenLP.ProjectorConstants', 'Performing a host name lookup'),
+    S_INFO: translate('OpenLP.ProjectorConstants', 'Projector Information available'),
+    S_INITIALIZE: translate('OpenLP.ProjectorConstants', 'Initialize in progress'),
+    S_LISTENING: translate('OpenLP.ProjectorConstants', 'Socket it listening (internal use only)'),
+    S_NETWORK_IDLE: translate('OpenLP.ProjectorConstants', 'No network activity at this time'),
+    S_NETWORK_RECEIVING: translate('OpenLP.ProjectorConstants', 'Received data'),
+    S_NETWORK_SENDING: translate('OpenLP.ProjectorConstants', 'Sending data'),
+    S_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Not Connected'),
     S_OFF: translate('OpenLP.ProjectorConstants', 'Off'),
-    S_INITIALIZE: translate('OpenLP.ProjectorConstants', 'Initialize in progress'),
+    S_OK: translate('OpenLP.ProjectorConstants', 'OK'),
+    S_ON: translate('OpenLP.ProjectorConstants', 'Power is on'),
     S_STANDBY: translate('OpenLP.ProjectorConstants', 'Power in standby'),
+    S_STATUS: translate('OpenLP.ProjectorConstants', 'Getting status'),
     S_WARMUP: translate('OpenLP.ProjectorConstants', 'Warmup in progress'),
-    S_ON: translate('OpenLP.ProjectorConstants', 'Power is on'),
-    S_COOLDOWN: translate('OpenLP.ProjectorConstants', 'Cooldown in progress'),
-    S_INFO: translate('OpenLP.ProjectorConstants', 'Projector Information available'),
-    S_NETWORK_SENDING: translate('OpenLP.ProjectorConstants', 'Sending data'),
-    S_NETWORK_RECEIVED: translate('OpenLP.ProjectorConstants', 'Received data')
-}
-
-# Map ERST return code positions to equipment
+}
+
+# Map ERST reply positions to equipment
+PJLINK_ERST_LIST = {
+    "FAN": translate('OpenLP.PJLink', 'Fan'),
+    "LAMP": translate('OpenLP.PJLink', 'Lamp'),
+    "TEMP": translate('OpenLP.PJLink', 'Temperature'),
+    "COVER": translate('OpenLP.PJLink', 'Cover'),
+    "FILTER": translate('OpenLP.PJLink', 'Filter'),
+    "OTHER": translate('OpenPL.PJLink', 'Other')
+}
+
+# Map projector item to ERST data position
 PJLINK_ERST_DATA = {
-    'DATA_LENGTH': 6,
+    'DATA_LENGTH': 6,  # Zero based so enums are 0-5
+    'FAN': 0,
+    'LAMP': 1,
+    'TEMP': 2,
+    'COVER': 3,
+    'FILTER': 4,
+    'OTHER': 5,
     0: 'FAN',
     1: 'LAMP',
     2: 'TEMP',
     3: 'COVER',
     4: 'FILTER',
-    5: 'OTHER',
-    'FAN': 0,
-    'LAMP': 1,
-    'TEMP': 2,
-    'COVER': 3,
-    'FILTER': 4,
-    'OTHER': 5
+    5: 'OTHER'
 }
 
-# Map for ERST return codes to string
+# Map ERST reply codes to string
 PJLINK_ERST_STATUS = {
-    '0': 'OK',
-    '1': ERROR_STRING[E_WARN],
-    '2': ERROR_STRING[E_ERROR],
-    'OK': '0',
-    E_OK: '0',
+    '0': S_OK,
+    '1': E_WARN,
+    '2': E_ERROR,
+    S_OK: '0',
     E_WARN: '1',
     E_ERROR: '2'
 }
 
-# Map for POWR return codes to status code
+# Map POWR return codes to status code
 PJLINK_POWR_STATUS = {
     '0': S_STANDBY,
     '1': S_ON,
@@ -485,3 +557,7 @@
         label = "{source}{item}".format(source=source, item=item)
         PJLINK_DEFAULT_CODES[label] = "{source} {item}".format(source=PJLINK_DEFAULT_SOURCES[source],
                                                                item=PJLINK_DEFAULT_ITEMS[item])
+# Remove temp variables so they don't become part of the module dict
+del(source)
+del(item)
+del(label)

=== modified file 'openlp/core/projectors/manager.py'
--- openlp/core/projectors/manager.py	2017-11-24 19:08:23 +0000
+++ openlp/core/projectors/manager.py	2017-12-25 08:57:51 +0000
@@ -35,9 +35,25 @@
 from openlp.core.common.settings import Settings
 from openlp.core.lib.ui import create_widget_action
 from openlp.core.projectors import DialogSourceStyle
-from openlp.core.projectors.constants import ERROR_MSG, ERROR_STRING, E_AUTHENTICATION, E_ERROR, \
-    E_NETWORK, E_NOT_CONNECTED, E_UNKNOWN_SOCKET_ERROR, STATUS_STRING, S_CONNECTED, S_CONNECTING, S_COOLDOWN, \
-    S_INITIALIZE, S_NOT_CONNECTED, S_OFF, S_ON, S_STANDBY, S_WARMUP
+from openlp.core.projectors.constants import \
+    E_AUTHENTICATION, \
+    E_ERROR, \
+    E_NETWORK, \
+    E_NOT_CONNECTED, \
+    E_UNKNOWN_SOCKET_ERROR, \
+    S_CONNECTED, \
+    S_CONNECTING, \
+    S_COOLDOWN, \
+    S_INITIALIZE, \
+    S_NOT_CONNECTED, \
+    S_OFF, \
+    S_ON, \
+    S_STANDBY, \
+    S_WARMUP,    \
+    STATUS_CODE, \
+    STATUS_MSG, \
+    QSOCKET_STATE
+
 from openlp.core.projectors.db import ProjectorDB
 from openlp.core.projectors.pjlink import PJLink, PJLinkUDP
 from openlp.core.projectors.editform import ProjectorEditForm
@@ -440,11 +456,12 @@
         :param opt: Needed by PyQt5
         """
         projector = item.data(QtCore.Qt.UserRole)
-        if projector.link.state() != projector.link.ConnectedState:
+        if QSOCKET_STATE[projector.link.state()] != S_CONNECTED:
             try:
+                log.debug('ProjectorManager: Calling connect_to_host() on "{ip}"'.format(ip=projector.link.ip))
                 projector.link.connect_to_host()
             except:
-                pass
+                log.debug('ProjectorManager: "{ip}" already connected - skipping'.format(ip=projector.link.ip))
         return
 
     def on_connect_projector(self, opt=None):
@@ -647,7 +664,7 @@
                                                                                    'Other info'),
                                                                    data=projector.link.other_info)
             message += '<b>{title}</b>: {data}<br />'.format(title=translate('OpenLP.ProjectorManager', 'Power status'),
-                                                             data=ERROR_MSG[projector.link.power])
+                                                             data=STATUS_MSG[projector.link.power])
             message += '<b>{title}</b>: {data}<br />'.format(title=translate('OpenLP.ProjectorManager', 'Shutter is'),
                                                              data=translate('OpenLP.ProjectorManager', 'Closed')
                                                              if projector.link.shutter
@@ -692,7 +709,7 @@
             else:
                 message += '<b>{data}</b>'.format(data=translate('OpenLP.ProjectorManager', 'Current errors/warnings'))
                 for (key, val) in projector.link.projector_errors.items():
-                    message += '<b>{key}</b>: {data}<br />'.format(key=key, data=ERROR_MSG[val])
+                    message += '<b>{key}</b>: {data}<br />'.format(key=key, data=STATUS_MSG[val])
         QtWidgets.QMessageBox.information(self, translate('OpenLP.ProjectorManager', 'Projector Information'), message)
 
     def _add_projector(self, projector):
@@ -817,31 +834,18 @@
             if ip == list_item.link.ip:
                 item = list_item
                 break
-        message = translate('OpenLP.ProjectorManager', 'No message') if msg is None else msg
-        if status in STATUS_STRING:
-            status_code = STATUS_STRING[status]
-            message = ERROR_MSG[status] if msg is None else msg
-        elif status in ERROR_STRING:
-            status_code = ERROR_STRING[status]
-            message = ERROR_MSG[status] if msg is None else msg
-        else:
-            status_code = status
-            message = ERROR_MSG[status] if msg is None else msg
-        log.debug('({name}) updateStatus(status={status}) message: "{message}"'.format(name=item.link.name,
-                                                                                       status=status_code,
-                                                                                       message=message))
-        if status in STATUS_ICONS:
-            if item.status == status:
-                return
-            item.status = status
-            item.icon = QtGui.QIcon(QtGui.QPixmap(STATUS_ICONS[status]))
-            if status in ERROR_STRING:
-                status_code = ERROR_STRING[status]
-            elif status in STATUS_STRING:
-                status_code = STATUS_STRING[status]
-            log.debug('({name}) Updating icon with {code}'.format(name=item.link.name, code=status_code))
-            item.widget.setIcon(item.icon)
-            self.update_icons()
+        if item is None:
+            log.error('ProjectorManager: Unknown item "{ip}" - not updating status'.format(ip=ip))
+            return
+        elif item.status == status:
+            log.debug('ProjectorManager: No status change for "{ip}" - not updating status'.format(ip=ip))
+            return
+
+        item.status = status
+        item.icon = QtGui.QIcon(QtGui.QPixmap(STATUS_ICONS[status]))
+        log.debug('({name}) Updating icon with {code}'.format(name=item.link.name, code=STATUS_CODE[status]))
+        item.widget.setIcon(item.icon)
+        return self.update_icons()
 
     def get_toolbar_item(self, name, enabled=False, hidden=False):
         item = self.one_toolbar.findChild(QtWidgets.QAction, name)
@@ -877,7 +881,7 @@
             self.get_toolbar_item('show_projector_multiple', hidden=True)
         elif count == 1:
             projector = self.projector_list_widget.selectedItems()[0].data(QtCore.Qt.UserRole)
-            connected = projector.link.state() == projector.link.ConnectedState
+            connected = QSOCKET_STATE[projector.link.state()] == S_CONNECTED
             power = projector.link.power == S_ON
             self.get_toolbar_item('connect_projector_multiple', hidden=True)
             self.get_toolbar_item('disconnect_projector_multiple', hidden=True)

=== modified file 'openlp/core/projectors/pjlink.py'
--- openlp/core/projectors/pjlink.py	2017-12-09 11:17:05 +0000
+++ openlp/core/projectors/pjlink.py	2017-12-25 08:57:51 +0000
@@ -54,11 +54,12 @@
 
 from openlp.core.common import qmd5_hash
 from openlp.core.common.i18n import translate
-from openlp.core.projectors.constants import CONNECTION_ERRORS, CR, ERROR_MSG, ERROR_STRING, \
-    E_AUTHENTICATION, E_CONNECTION_REFUSED, E_GENERAL, E_INVALID_DATA, E_NETWORK, E_NOT_CONNECTED, E_OK, \
-    E_PARAMETER, E_PROJECTOR, E_SOCKET_TIMEOUT, E_UNAVAILABLE, E_UNDEFINED, PJLINK_ERRORS, PJLINK_ERST_DATA, \
-    PJLINK_ERST_STATUS, PJLINK_MAX_PACKET, PJLINK_PORT, PJLINK_POWR_STATUS, PJLINK_VALID_CMD, \
-    STATUS_STRING, S_CONNECTED, S_CONNECTING, S_INFO, S_NOT_CONNECTED, S_OFF, S_OK, S_ON, S_QSOCKET_STATE, S_STATUS
+from openlp.core.projectors.constants import CONNECTION_ERRORS, PJLINK_CLASS, PJLINK_DEFAULT_CODES, PJLINK_ERRORS, \
+    PJLINK_ERST_DATA, PJLINK_ERST_STATUS, PJLINK_MAX_PACKET, PJLINK_PREFIX, PJLINK_PORT, PJLINK_POWR_STATUS, \
+    PJLINK_SUFFIX, PJLINK_VALID_CMD, PROJECTOR_STATE, STATUS_CODE, STATUS_MSG, QSOCKET_STATE, \
+    E_AUTHENTICATION, E_CONNECTION_REFUSED, E_GENERAL, E_INVALID_DATA, E_NETWORK, E_NOT_CONNECTED, \
+    E_OK, E_SOCKET_TIMEOUT, \
+    S_CONNECTED, S_CONNECTING, S_INFO, S_NOT_CONNECTED, S_OFF, S_OK, S_ON, S_STATUS
 
 log = logging.getLogger(__name__)
 log.debug('pjlink loaded')
@@ -69,12 +70,9 @@
 SocketError = QtNetwork.QAbstractSocket.SocketError
 SocketSTate = QtNetwork.QAbstractSocket.SocketState
 
-PJLINK_PREFIX = '%'
-PJLINK_CLASS = '1'  # Default to class 1 until we query the projector
 # Add prefix here, but defer linkclass expansion until later when we have the actual
 # PJLink class for the command
 PJLINK_HEADER = '{prefix}{{linkclass}}'.format(prefix=PJLINK_PREFIX)
-PJLINK_SUFFIX = CR
 
 
 class PJLinkUDP(QtNetwork.QUdpSocket):
@@ -136,8 +134,9 @@
         """
         Initialize instance variables. Also used to reset projector-specific information to default.
         """
+        conn_state = STATUS_CODE[QSOCKET_STATE[self.state()]]
         log.debug('({ip}) reset_information() connect status is {state}'.format(ip=self.ip,
-                                                                                state=S_QSOCKET_STATE[self.state()]))
+                                                                                state=conn_state))
         self.fan = None  # ERST
         self.filter_time = None  # FILT
         self.lamp = None  # LAMP
@@ -183,42 +182,25 @@
         # Due to some replies should stay as mixed-case, validate using separate uppercase check
         _data = data.upper()
         # Check if we have a future command not available yet
-        if cmd not in PJLINK_VALID_CMD:
-            log.error('({ip}) Ignoring command="{cmd}" (Invalid/Unknown)'.format(ip=self.ip, cmd=cmd))
+        if cmd not in self.pjlink_functions:
+            log.warning('({ip}) Unable to process command="{cmd}" (Future option?)'.format(ip=self.ip, cmd=cmd))
             return
         elif _data == 'OK':
             log.debug('({ip}) Command "{cmd}" returned OK'.format(ip=self.ip, cmd=cmd))
             # A command returned successfully, so do a query on command to verify status
             return self.send_command(cmd=cmd)
-        elif cmd not in self.pjlink_functions:
-            log.warning('({ip}) Unable to process command="{cmd}" (Future option?)'.format(ip=self.ip, cmd=cmd))
-            return
         elif _data in PJLINK_ERRORS:
             # Oops - projector error
-            log.error('({ip}) Projector returned error "{data}"'.format(ip=self.ip, data=data))
-            if _data == PJLINK_ERRORS[E_AUTHENTICATION]:
-                # Authentication error
+            log.error('({ip}) {cmd}: {err}'.format(ip=self.ip,
+                                                   cmd=cmd,
+                                                   err=STATUS_MSG[PJLINK_ERRORS[_data]]))
+            if PJLINK_ERRORS[_data] == E_AUTHENTICATION:
                 self.disconnect_from_host()
-                self.change_status(E_AUTHENTICATION)
-                log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.ip))
                 self.projectorAuthentication.emit(self.name)
-            elif _data == PJLINK_ERRORS[E_UNDEFINED]:
-                # Projector does not recognize command
-                self.change_status(E_UNDEFINED, '{error}: "{data}"'.format(error=ERROR_MSG[E_UNDEFINED],
-                                                                           data=cmd))
-            elif _data == PJLINK_ERRORS[E_PARAMETER]:
-                # Invalid parameter
-                self.change_status(E_PARAMETER)
-            elif _data == PJLINK_ERRORS[E_UNAVAILABLE]:
-                # Projector busy
-                self.change_status(E_UNAVAILABLE)
-            elif _data == PJLINK_ERRORS[E_PROJECTOR]:
-                # Projector/display error
-                self.change_status(E_PROJECTOR)
-            return
+                return self.change_status(status=E_AUTHENTICATION)
         # Command checks already passed
         log.debug('({ip}) Calling function for {cmd}'.format(ip=self.ip, cmd=cmd))
-        self.pjlink_functions[cmd](data)
+        self.pjlink_functions[cmd](data=data)
 
     def process_avmt(self, data):
         """
@@ -313,22 +295,22 @@
                                                data[PJLINK_ERST_DATA['COVER']],
                                                data[PJLINK_ERST_DATA['FILTER']],
                                                data[PJLINK_ERST_DATA['OTHER']])
-        if fan != PJLINK_ERST_STATUS[E_OK]:
+        if fan != PJLINK_ERST_STATUS[S_OK]:
             self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Fan')] = \
                 PJLINK_ERST_STATUS[fan]
-        if lamp != PJLINK_ERST_STATUS[E_OK]:
+        if lamp != PJLINK_ERST_STATUS[S_OK]:
             self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Lamp')] =  \
                 PJLINK_ERST_STATUS[lamp]
-        if temp != PJLINK_ERST_STATUS[E_OK]:
+        if temp != PJLINK_ERST_STATUS[S_OK]:
             self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Temperature')] =  \
                 PJLINK_ERST_STATUS[temp]
-        if cover != PJLINK_ERST_STATUS[E_OK]:
+        if cover != PJLINK_ERST_STATUS[S_OK]:
             self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Cover')] =  \
                 PJLINK_ERST_STATUS[cover]
-        if filt != PJLINK_ERST_STATUS[E_OK]:
+        if filt != PJLINK_ERST_STATUS[S_OK]:
             self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Filter')] =  \
                 PJLINK_ERST_STATUS[filt]
-        if other != PJLINK_ERST_STATUS[E_OK]:
+        if other != PJLINK_ERST_STATUS[S_OK]:
             self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Other')] =  \
                 PJLINK_ERST_STATUS[other]
         return
@@ -373,8 +355,18 @@
 
         :param data: Currently selected source
         """
+        # First, see if we have a valid input based on what is installed (if available)
+        if self.source_available is not None:
+            # We have available inputs, so verify it's in the list
+            if data not in self.source_available:
+                log.warn('({ip}) Input source not listed in available sources - ignoring'.format(ip=self.ip))
+                return
+        elif data not in PJLINK_DEFAULT_CODES:
+            # Hmm - no sources available yet, so check with PJLink defaults
+            log.warn('({ip}) Input source not listed as a PJLink available source - ignoring'.format(ip=self.ip))
+            return
         self.source = data
-        log.info('({ip}) Setting data source to "{data}"'.format(ip=self.ip, data=self.source))
+        log.debug('({ip}) Setting data source to "{data}"'.format(ip=self.ip, data=self.source))
         return
 
     def process_inst(self, data):
@@ -390,7 +382,6 @@
             sources.append(source)
         sources.sort()
         self.source_available = sources
-        self.projectorUpdateIcons.emit()
         log.debug('({ip}) Setting projector sources_available to "{data}"'.format(ip=self.ip,
                                                                                   data=self.source_available))
         return
@@ -551,17 +542,15 @@
             return
         elif self.sw_version is None:
             log.debug('({ip}) Setting projector software version to "{data}"'.format(ip=self.ip, data=data))
-            self.sw_version = data
-            self.db_update = True
         else:
-            # Compare software version and see if we got the same projector
-            if self.serial_no != data:
+            if self.sw_version != data:
                 log.warning('({ip}) Projector software version does not match saved '
                             'software version'.format(ip=self.ip))
                 log.warning('({ip}) Saved:    "{old}"'.format(ip=self.ip, old=self.sw_version))
                 log.warning('({ip}) Received: "{new}"'.format(ip=self.ip, new=data))
-                log.warning('({ip}) Saving new serial number as sw_version_received'.format(ip=self.ip))
-                self.sw_version_received = data
+                log.warning('({ip}) Updating software version'.format(ip=self.ip))
+        self.sw_version = data
+        self.db_update = True
 
 
 class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
@@ -678,7 +667,7 @@
         Retrieve information from projector that changes.
         Normally called by timer().
         """
-        if self.state() != S_QSOCKET_STATE['ConnectedState']:
+        if QSOCKET_STATE[self.state()] != S_CONNECTED:
             log.warning('({ip}) poll_loop(): Not connected - returning'.format(ip=self.ip))
             return
         log.debug('({ip}) poll_loop(): Updating projector status'.format(ip=self.ip))
@@ -688,13 +677,8 @@
             self.timer.setInterval(self.poll_time)
         # Restart timer
         self.timer.start()
-        # These commands may change during connection
-        check_list = ['POWR', 'ERST', 'LAMP', 'AVMT', 'INPT']
-        if self.pjlink_class == '2':
-            check_list.extend(['FILT', 'FREZ'])
-        for command in check_list:
-            self.send_command(command)
         # The following commands do not change, so only check them once
+        # Call them first in case other functions rely on something here
         if self.power == S_ON and self.source_available is None:
             self.send_command('INST')
         if self.other_info is None:
@@ -715,22 +699,28 @@
                 self.send_command('RFIL')
             if self.model_lamp is None:
                 self.send_command('RLMP')
+        # These commands may change during connection
+        check_list = ['POWR', 'ERST', 'LAMP', 'AVMT', 'INPT']
+        if self.pjlink_class == '2':
+            check_list.extend(['FILT', 'FREZ'])
+        for command in check_list:
+            self.send_command(command)
 
     def _get_status(self, status):
         """
         Helper to retrieve status/error codes and convert to strings.
 
         :param status: Status/Error code
-        :returns: (Status/Error code, String)
+        :returns: tuple (-1 if code not INT, None)
+        :returns: tuple (string: code as string, None if no description)
+        :returns: tuple (string: code as string, string: Status/Error description)
         """
         if not isinstance(status, int):
-            return -1, 'Invalid status code'
-        elif status in ERROR_STRING:
-            return ERROR_STRING[status], ERROR_MSG[status]
-        elif status in STATUS_STRING:
-            return STATUS_STRING[status], ERROR_MSG[status]
+            return -1, None
+        elif status not in STATUS_MSG:
+            return None, None
         else:
-            return status, translate('OpenLP.PJLink', 'Unknown status')
+            return STATUS_CODE[status], STATUS_MSG[status]
 
     def change_status(self, status, msg=None):
         """
@@ -740,19 +730,27 @@
         :param status: Status code
         :param msg: Optional message
         """
-        message = translate('OpenLP.PJLink', 'No message') if msg is None else msg
-        (code, message) = self._get_status(status)
-        if msg is not None:
-            message = msg
+        if status in STATUS_CODE:
+            log.debug('({ip}) Changing status to {status} '
+                      '"{msg}"'.format(ip=self.ip,
+                                       status=STATUS_CODE[status],
+                                       msg=msg if msg is not None else STATUS_MSG[status]))
+        else:
+            log.warning('({ip}) Unknown status change code: {code}'.format(ip=self.ip,
+                                                                           code=status))
+            return
         if status in CONNECTION_ERRORS:
-            # Projector, connection state
-            self.projector_status = self.error_status = self.status_connect = E_NOT_CONNECTED
-        elif status >= S_NOT_CONNECTED and status < S_STATUS:
+            # Connection state error affects both socket and projector
+            self.error_status = status
+            self.status_connect = E_NOT_CONNECTED
+        elif status >= S_NOT_CONNECTED and status in QSOCKET_STATE:
+            # Socket connection status update
             self.status_connect = status
-            self.projector_status = S_NOT_CONNECTED
-        elif status <= S_INFO:
-            self.status_connect = S_CONNECTED
+        elif status >= S_NOT_CONNECTED and status in PROJECTOR_STATE:
+            # Only affects the projector status
             self.projector_status = status
+
+        # These log entries are for troubleshooting only
         (status_code, status_message) = self._get_status(self.status_connect)
         log.debug('({ip}) status_connect: {code}: "{message}"'.format(ip=self.ip,
                                                                       code=status_code,
@@ -765,6 +763,15 @@
         log.debug('({ip}) error_status: {code}: "{message}"'.format(ip=self.ip,
                                                                     code=status_code,
                                                                     message=status_message if msg is None else msg))
+
+        # Now that we logged extra information for debugging, broadcast the original change/message
+        (code, message) = self._get_status(status)
+        if msg is not None:
+            message = msg
+        elif message is None:
+            # No message for status code
+            message = translate('OpenLP.PJLink', 'No message') if msg is None else msg
+
         self.changeStatus.emit(self.ip, status, message)
         self.projectorUpdateIcons.emit()
 
@@ -794,7 +801,7 @@
             data = decode(read, 'utf-8')
             # Possibility of extraneous data on input when reading.
             # Clean out extraneous characters in buffer.
-            self.readLine(self.max_size)
+            self.read(2048)
             log.debug('({ip}) check_login() read "{data}"'.format(ip=self.ip, data=data.strip()))
         # At this point, we should only have the initial login prompt with
         # possible authentication
@@ -805,6 +812,7 @@
             # Invalid initial packet - close socket
             log.error('({ip}) Invalid initial packet received - closing socket'.format(ip=self.ip))
             return self.disconnect_from_host()
+        # Convert the initial login prompt with the expected PJLink normal command format for processing
         log.debug('({ip}) check_login(): Formatting initial connection prompt to PJLink packet'.format(ip=self.ip))
         return self.get_data('{start}{clss}{data}'.format(start=PJLINK_PREFIX,
                                                           clss='1',
@@ -895,7 +903,7 @@
         Get data from TCP socket.
         """
         log.debug('({ip}) get_socket(): Reading data'.format(ip=self.ip))
-        if self.state() != self.ConnectedState:
+        if QSOCKET_STATE[self.state()] != S_CONNECTED:
             log.debug('({ip}) get_socket(): Not connected - returning'.format(ip=self.ip))
             self.send_busy = False
             return
@@ -907,8 +915,7 @@
             log.debug('({ip}) get_socket(): No data available (-1)'.format(ip=self.ip))
             return self.receive_data_signal()
         self.socket_timer.stop()
-        self.get_data(buff=read, ip=self.ip)
-        return self.receive_data_signal()
+        return self.get_data(buff=read, ip=self.ip)
 
     def get_data(self, buff, ip=None):
         """
@@ -927,13 +934,17 @@
         data = data_in.strip()
         # Initial packet checks
         if (len(data) < 7):
-            return self._trash_buffer(msg='get_data(): Invalid packet - length')
+            self._trash_buffer(msg='get_data(): Invalid packet - length')
+            return self.receive_data_signal()
         elif len(data) > self.max_size:
-            return self._trash_buffer(msg='get_data(): Invalid packet - too long')
+            self._trash_buffer(msg='get_data(): Invalid packet - too long')
+            return self.receive_data_signal()
         elif not data.startswith(PJLINK_PREFIX):
-            return self._trash_buffer(msg='get_data(): Invalid packet - PJLink prefix missing')
+            self._trash_buffer(msg='get_data(): Invalid packet - PJLink prefix missing')
+            return self.receive_data_signal()
         elif '=' not in data:
-            return self._trash_buffer(msg='get_data(): Invalid reply - Does not have "="')
+            self._trash_buffer(msg='get_data(): Invalid reply - Does not have "="')
+            return self.receive_data_signal()
         log.debug('({ip}) get_data(): Checking new data "{data}"'.format(ip=self.ip, data=data))
         header, data = data.split('=')
         # At this point, the header should contain:
@@ -947,15 +958,17 @@
         except ValueError as e:
             self.change_status(E_INVALID_DATA)
             log.warning('({ip}) get_data(): Received data: "{data}"'.format(ip=self.ip, data=data_in))
-            return self._trash_buffer('get_data(): Expected header + command + data')
+            self._trash_buffer('get_data(): Expected header + command + data')
+            return self.receive_data_signal()
         if cmd not in PJLINK_VALID_CMD:
             log.warning('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd))
-            return self._trash_buffer(msg='get_data(): Unknown command "{data}"'.format(data=cmd))
+            self._trash_buffer(msg='get_data(): Unknown command "{data}"'.format(data=cmd))
+            return self.receive_data_signal()
         if int(self.pjlink_class) < int(version):
             log.warning('({ip}) get_data(): Projector returned class reply higher '
                         'than projector stated class'.format(ip=self.ip))
-        self.send_busy = False
-        return self.process_command(cmd, data)
+        self.process_command(cmd, data)
+        return self.receive_data_signal()
 
     @QtCore.pyqtSlot(QtNetwork.QAbstractSocket.SocketError)
     def get_error(self, err):
@@ -993,7 +1006,7 @@
         :param salt: Optional  salt for md5 hash initial authentication
         :param priority: Option to send packet now rather than queue it up
         """
-        if self.state() != self.ConnectedState:
+        if QSOCKET_STATE[self.state()] != S_CONNECTED:
             log.warning('({ip}) send_command(): Not connected - returning'.format(ip=self.ip))
             return self.reset_information()
         if cmd not in PJLINK_VALID_CMD:
@@ -1018,7 +1031,7 @@
                                                                  header=header,
                                                                  command=cmd,
                                                                  options=opts,
-                                                                 suffix=CR)
+                                                                 suffix=PJLINK_SUFFIX)
         if out in self.priority_queue:
             log.debug('({ip}) send_command(): Already in priority queue - skipping'.format(ip=self.ip))
         elif out in self.send_queue:
@@ -1044,9 +1057,10 @@
         """
         # Funny looking data check, but it's a quick check for data=None
         log.debug('({ip}) _send_command(data="{data}")'.format(ip=self.ip, data=data.strip() if data else data))
+        conn_state = STATUS_CODE[QSOCKET_STATE[self.state()]]
         log.debug('({ip}) _send_command(): Connection status: {data}'.format(ip=self.ip,
-                                                                             data=S_QSOCKET_STATE[self.state()]))
-        if self.state() != self.ConnectedState:
+                                                                             data=conn_state))
+        if QSOCKET_STATE[self.state()] != S_CONNECTED:
             log.debug('({ip}) _send_command() Not connected - abort'.format(ip=self.ip))
             self.send_busy = False
             return self.disconnect_from_host()
@@ -1088,10 +1102,11 @@
         """
         Initiate connection to projector.
         """
-        log.debug('{ip}) connect_to_host(): Starting connection'.format(ip=self.ip))
-        if self.state() == self.ConnectedState:
+        log.debug('({ip}) connect_to_host(): Starting connection'.format(ip=self.ip))
+        if QSOCKET_STATE[self.state()] == S_CONNECTED:
             log.warning('({ip}) connect_to_host(): Already connected - returning'.format(ip=self.ip))
             return
+        self.error_status = S_OK
         self.change_status(S_CONNECTING)
         self.connectToHost(self.ip, self.port if isinstance(self.port, int) else int(self.port))
 
@@ -1100,12 +1115,13 @@
         """
         Close socket and cleanup.
         """
-        if abort or self.state() != self.ConnectedState:
+        if abort or QSOCKET_STATE[self.state()] != S_NOT_CONNECTED:
             if abort:
                 log.warning('({ip}) disconnect_from_host(): Aborting connection'.format(ip=self.ip))
+                self.abort()
             else:
                 log.warning('({ip}) disconnect_from_host(): Not connected'.format(ip=self.ip))
-        self.disconnectFromHost()
+            self.disconnectFromHost()
         try:
             self.readyRead.disconnect(self.get_socket)
         except TypeError:
@@ -1201,7 +1217,7 @@
         elif src not in self.source_available:
             return
         log.debug('({ip}) Setting input source to "{data}"'.format(ip=self.ip, data=src))
-        self.send_command(cmd='INPT', opts=src)
+        self.send_command(cmd='INPT', opts=src, priority=True)
         self.poll_loop()
 
     def set_power_on(self):
@@ -1209,7 +1225,7 @@
         Send command to turn power to on.
         """
         log.debug('({ip}) Setting POWR to 1 (on)'.format(ip=self.ip))
-        self.send_command(cmd='POWR', opts='1')
+        self.send_command(cmd='POWR', opts='1', priority=True)
         self.poll_loop()
 
     def set_power_off(self):
@@ -1217,7 +1233,7 @@
         Send command to turn power to standby.
         """
         log.debug('({ip}) Setting POWR to 0 (standby)'.format(ip=self.ip))
-        self.send_command(cmd='POWR', opts='0')
+        self.send_command(cmd='POWR', opts='0', priority=True)
         self.poll_loop()
 
     def set_shutter_closed(self):
@@ -1225,7 +1241,7 @@
         Send command to set shutter to closed position.
         """
         log.debug('({ip}) Setting AVMT to 11 (shutter closed)'.format(ip=self.ip))
-        self.send_command(cmd='AVMT', opts='11')
+        self.send_command(cmd='AVMT', opts='11', priority=True)
         self.poll_loop()
 
     def set_shutter_open(self):
@@ -1233,8 +1249,9 @@
         Send command to set shutter to open position.
         """
         log.debug('({ip}) Setting AVMT to "10" (shutter open)'.format(ip=self.ip))
-        self.send_command(cmd='AVMT', opts='10')
+        self.send_command(cmd='AVMT', opts='10', priority=True)
         self.poll_loop()
+        self.projectorUpdateIcons.emit()
 
     def receive_data_signal(self):
         """

=== modified file 'tests/functional/openlp_core/projectors/test_projector_constants.py'
--- tests/functional/openlp_core/projectors/test_projector_constants.py	2017-11-10 11:59:38 +0000
+++ tests/functional/openlp_core/projectors/test_projector_constants.py	2017-12-25 08:57:51 +0000
@@ -23,12 +23,51 @@
 Package to test the openlp.core.projectors.constants module.
 """
 from unittest import TestCase
+from openlp.core.projectors import constants
+from openlp.core.projectors.constants import STATUS_CODE, STATUS_MSG
 
 
 class TestProjectorConstants(TestCase):
     """
     Test specific functions in the projector constants module.
     """
+    def test_defined_codes_in_status_code(self):
+        """
+        Test defined status/error codes have equivalent strings
+        """
+        check = []
+        missing_str = []
+
+        # GIVEN: List of defined E_* and S_* items defined in constants
+        for item in constants.__dict__:
+            if item.startswith('E_') or item.startswith('S_'):
+                check.append(item)
+
+        # WHEN: Verify defined list against STATUS_STR
+        for item in check:
+            if constants.__dict__[item] not in STATUS_CODE:
+                missing_str.append(item)
+
+        # THEN: There should be no missing items
+        assert 0 == len(missing_str), 'Status string missing: {msg}'.format(msg=missing_str)
+
+    def test_status_code_in_status_message(self):
+        """
+        Test if entries in STATUS_CODE have equivalent descriptions in STATUS_MSG
+        """
+        missing_msg = []
+
+        # GIVEN: Test items
+        check = STATUS_CODE
+
+        # WHEN: Verify each entry in STATUS_MSG against STATUS_CODE
+        for item in check:
+            if item not in STATUS_MSG:
+                missing_msg.append(item)
+
+        # THEN: There should be no missing items
+        assert 0 == len(missing_msg), 'Status message missing: {msg}'.format(msg=missing_msg)
+
     def test_build_pjlink_video_label(self):
         """
         Test building PJLINK_DEFAULT_CODES dictionary

=== modified file 'tests/functional/openlp_core/projectors/test_projector_pjlink_base.py'
--- tests/functional/openlp_core/projectors/test_projector_pjlink_base.py	2017-12-04 00:24:47 +0000
+++ tests/functional/openlp_core/projectors/test_projector_pjlink_base.py	2017-12-25 08:57:51 +0000
@@ -25,7 +25,7 @@
 from unittest import TestCase
 from unittest.mock import call, patch, MagicMock
 
-from openlp.core.projectors.constants import E_PARAMETER, ERROR_STRING, S_ON, S_CONNECTED, S_QSOCKET_STATE
+from openlp.core.projectors.constants import E_PARAMETER, STATUS_CODE, S_ON, S_CONNECTED, QSOCKET_STATE
 from openlp.core.projectors.db import Projector
 from openlp.core.projectors.pjlink import PJLink
 
@@ -65,7 +65,7 @@
         #       as first parameter
         mock_change_status.called_with(E_PARAMETER,
                                        'change_status should have been called with "{}"'.format(
-                                           ERROR_STRING[E_PARAMETER]))
+                                           STATUS_CODE[E_PARAMETER]))
 
     @patch.object(pjlink_test, 'disconnect_from_host')
     def test_socket_abort(self, mock_disconnect):
@@ -104,7 +104,7 @@
         """
         # GIVEN: Mocks and test data
         mock_state = patch.object(self.pjlink_test, 'state').start()
-        mock_state.return_value = S_QSOCKET_STATE['ConnectedState']
+        mock_state.return_value = QSOCKET_STATE[S_CONNECTED]
         mock_timer = patch.object(self.pjlink_test, 'timer').start()
         mock_timer.interval.return_value = 10
         mock_send_command = patch.object(self.pjlink_test, 'send_command').start()
@@ -117,7 +117,6 @@
         pjlink.manufacturer = None
         pjlink.model = None
         pjlink.pjlink_name = None
-        pjlink.ConnectedState = S_CONNECTED
         call_list = [
             call('POWR'),
             call('ERST'),

=== modified file 'tests/functional/openlp_core/projectors/test_projector_pjlink_cmd_routing.py'
--- tests/functional/openlp_core/projectors/test_projector_pjlink_cmd_routing.py	2017-12-09 11:17:05 +0000
+++ tests/functional/openlp_core/projectors/test_projector_pjlink_cmd_routing.py	2017-12-25 08:57:51 +0000
@@ -24,209 +24,222 @@
 """
 
 from unittest import TestCase
-from unittest.mock import patch, MagicMock
+from unittest.mock import MagicMock, call, patch
 
 import openlp.core.projectors.pjlink
-from openlp.core.projectors.constants import PJLINK_ERRORS, \
+from openlp.core.projectors.constants import PJLINK_ERRORS, PJLINK_PREFIX, STATUS_MSG, \
     E_AUTHENTICATION, E_PARAMETER, E_PROJECTOR, E_UNAVAILABLE, E_UNDEFINED
 from openlp.core.projectors.db import Projector
 from openlp.core.projectors.pjlink import PJLink
 
-'''
-from openlp.core.projectors.constants import ERROR_STRING, PJLINK_ERST_DATA, PJLINK_ERST_STATUS, \
-    PJLINK_POWR_STATUS, PJLINK_VALID_CMD, E_WARN, E_ERROR, S_OFF, S_STANDBY, S_ON
-'''
-from tests.resources.projector.data import TEST_PIN, TEST1_DATA
-
-pjlink_test = PJLink(Projector(**TEST1_DATA), pin=TEST_PIN, no_poll=True)
-pjlink_test.ip = '127.0.0.1'
+from tests.resources.projector.data import TEST1_DATA
 
 
 class TestPJLinkRouting(TestCase):
     """
     Tests for the PJLink module command routing
     """
-    def setUp(self):
-        '''
-        TestPJLinkCommands part 2 initialization
-        '''
-        self.pjlink_test = PJLink(Projector(**TEST1_DATA), no_poll=True)
-
-    def tearDown(self):
-        '''
-        TestPJLinkCommands part 2 cleanups
-        '''
-        self.pjlink_test = None
-
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_process_command_call_clss(self, mock_log):
+    def test_get_data_unknown_command(self):
+        """
+        Test not a valid command
+        """
+        # GIVEN: Test object
+        log_warning_text = [call('(111.111.111.111) get_data(): Invalid packet - unknown command "UNK"')]
+        log_debug_text = [call('(111.111.111.111) get_data(ip="111.111.111.111" buffer="b\'%1UNK=Huh?\'"'),
+                          call('(111.111.111.111) get_data(): Checking new data "%1UNK=Huh?"')]
+
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, '_trash_buffer') as mock_buffer:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.pjlink_functions = MagicMock()
+
+            # WHEN: get_data called with an unknown command
+            pjlink.get_data(buff='{prefix}1UNK=Huh?'.format(prefix=PJLINK_PREFIX).encode('utf-8'))
+
+            # THEN: Appropriate log entries should have been made and methods called/not called
+            mock_log.debug.assert_has_calls(log_debug_text)
+            mock_log.warning.assert_has_calls(log_warning_text)
+            self.assertFalse(pjlink.pjlink_functions.called, 'Should not have accessed pjlink_functions')
+            self.assertTrue(mock_buffer.called, 'Should have called _trash_buffer')
+
+    def test_process_command_call_clss(self):
         """
         Test process_command calls proper function
         """
+        # GIVEN: Test object and mocks
+        log_debug_calls = [call('(111.111.111.111) Processing command "CLSS" with data "1"'),
+                           call('(111.111.111.111) Calling function for CLSS')]
+
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: process_command is called with valid function and data
+            pjlink.process_command(cmd='CLSS', data='1')
+
+            # THEN: Appropriate log entries should have been made and methods called
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            mock_process_clss.assert_called_once_with(data='1')
+
+    def test_process_command_erra(self):
+        """
+        Test ERRA - Authentication Error
+        """
         # GIVEN: Test object
-        pjlink = pjlink_test
-        log_text = '(127.0.0.1) Calling function for CLSS'
-        mock_log.reset_mock()
-        mock_process_clss = MagicMock()
-        pjlink.pjlink_functions['CLSS'] = mock_process_clss
-
-        # WHEN: process_command is called with valid function and data
-        pjlink.process_command(cmd='CLSS', data='1')
-
-        # THEN: Process method should have been called properly
-        mock_log.debug.assert_called_with(log_text)
-        mock_process_clss.assert_called_with('1')
-
-    @patch.object(pjlink_test, 'change_status')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_process_command_err1(self, mock_log, mock_change_status):
+        log_error_calls = [call('(111.111.111.111) PJLINK: {msg}'.format(msg=STATUS_MSG[E_AUTHENTICATION]))]
+        log_debug_calls = [call('(111.111.111.111) Processing command "PJLINK" with data "ERRA"')]
+
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'process_pjlink') as mock_process_pjlink, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status') as mock_change_status, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'disconnect_from_host') as mock_disconnect, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorAuthentication') as mock_authentication:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: process_command called with ERRA
+            pjlink.process_command(cmd='PJLINK', data=PJLINK_ERRORS[E_AUTHENTICATION])
+
+            # THEN: Appropriate log entries should have been made and methods called/not called
+            mock_log.error.assert_has_calls(log_error_calls)
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            self.assertTrue(mock_disconnect.called, 'disconnect_from_host should have been called')
+            mock_change_status.assert_called_once_with(status=E_AUTHENTICATION)
+            mock_authentication.emit.assert_called_once_with(pjlink.name)
+            mock_process_pjlink.assert_not_called()
+
+    def test_process_command_err1(self):
         """
         Test ERR1 - Undefined projector function
         """
         # GIVEN: Test object
-        pjlink = pjlink_test
-        log_text = '(127.0.0.1) Projector returned error "ERR1"'
-        mock_change_status.reset_mock()
-        mock_log.reset_mock()
-
-        # WHEN: process_command called with ERR1
-        pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_UNDEFINED])
-
-        # THEN: Error should be logged and status_change should be called
-        mock_change_status.assert_called_once_with(E_UNDEFINED, 'Undefined Command: "CLSS"')
-        mock_log.error.assert_called_with(log_text)
-
-    @patch.object(pjlink_test, 'change_status')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_process_command_err2(self, mock_log, mock_change_status):
+        log_error_text = [call('(111.111.111.111) CLSS: {msg}'.format(msg=STATUS_MSG[E_UNDEFINED]))]
+        log_debug_text = [call('(111.111.111.111) Processing command "CLSS" with data "ERR1"'),
+                          call('(111.111.111.111) Calling function for CLSS')]
+
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: process_command called with ERR1
+            pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_UNDEFINED])
+
+            # THEN: Appropriate log entries should have been made and methods called
+            mock_log.error.assert_has_calls(log_error_text)
+            mock_log.debug.assert_has_calls(log_debug_text)
+            mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_UNDEFINED])
+
+    def test_process_command_err2(self):
         """
         Test ERR2 - Parameter Error
         """
         # GIVEN: Test object
-        pjlink = pjlink_test
-        log_text = '(127.0.0.1) Projector returned error "ERR2"'
-        mock_change_status.reset_mock()
-        mock_log.reset_mock()
-
-        # WHEN: process_command called with ERR2
-        pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_PARAMETER])
-
-        # THEN: Error should be logged and status_change should be called
-        mock_change_status.assert_called_once_with(E_PARAMETER)
-        mock_log.error.assert_called_with(log_text)
-
-    @patch.object(pjlink_test, 'change_status')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_process_command_err3(self, mock_log, mock_change_status):
-        """
-        Test ERR3 - Unavailable error
-        """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        log_text = '(127.0.0.1) Projector returned error "ERR3"'
-        mock_change_status.reset_mock()
-        mock_log.reset_mock()
-
-        # WHEN: process_command called with ERR3
-        pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_UNAVAILABLE])
-
-        # THEN: Error should be logged and status_change should be called
-        mock_change_status.assert_called_once_with(E_UNAVAILABLE)
-        mock_log.error.assert_called_with(log_text)
-
-    @patch.object(pjlink_test, 'change_status')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_process_command_err4(self, mock_log, mock_change_status):
-        """
-        Test ERR3 - Unavailable error
-        """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        log_text = '(127.0.0.1) Projector returned error "ERR4"'
-        mock_change_status.reset_mock()
-        mock_log.reset_mock()
-
-        # WHEN: process_command called with ERR3
-        pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_PROJECTOR])
-
-        # THEN: Error should be logged and status_change should be called
-        mock_change_status.assert_called_once_with(E_PROJECTOR)
-        mock_log.error.assert_called_with(log_text)
-
-    @patch.object(pjlink_test, 'projectorAuthentication')
-    @patch.object(pjlink_test, 'change_status')
-    @patch.object(pjlink_test, 'disconnect_from_host')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_process_command_erra(self, mock_log, mock_disconnect, mock_change_status, mock_err_authenticate):
-        """
-        Test ERRA - Authentication Error
-        """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        log_text = '(127.0.0.1) Projector returned error "ERRA"'
-        mock_change_status.reset_mock()
-        mock_log.reset_mock()
-
-        # WHEN: process_command called with ERRA
-        pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_AUTHENTICATION])
-
-        # THEN: Error should be logged and several methods should be called
-        self.assertTrue(mock_disconnect.called, 'disconnect_from_host should have been called')
-        mock_change_status.assert_called_once_with(E_AUTHENTICATION)
-        mock_log.error.assert_called_with(log_text)
+        log_error_text = [call('(111.111.111.111) CLSS: {msg}'.format(msg=STATUS_MSG[E_PARAMETER]))]
+        log_debug_text = [call('(111.111.111.111) Processing command "CLSS" with data "ERR2"'),
+                          call('(111.111.111.111) Calling function for CLSS')]
+
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: process_command called with ERR2
+            pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_PARAMETER])
+
+            # THEN: Appropriate log entries should have been made and methods called/not called
+            mock_log.error.assert_has_calls(log_error_text)
+            mock_log.debug.assert_has_calls(log_debug_text)
+            mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_PARAMETER])
+
+    def test_process_command_err3(self):
+        """
+        Test ERR3 - Unavailable error
+        """
+        # GIVEN: Test object
+        log_error_text = [call('(111.111.111.111) CLSS: {msg}'.format(msg=STATUS_MSG[E_UNAVAILABLE]))]
+        log_debug_text = [call('(111.111.111.111) Processing command "CLSS" with data "ERR3"'),
+                          call('(111.111.111.111) Calling function for CLSS')]
+
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: process_command called with ERR3
+            pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_UNAVAILABLE])
+
+            # THEN: Appropriate log entries should have been made and methods called
+            mock_log.error.assert_has_calls(log_error_text)
+            mock_log.debug.assert_has_calls(log_debug_text)
+            mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_UNAVAILABLE])
+
+    def test_process_command_err4(self):
+        """
+        Test ERR3 - Unavailable error
+        """
+        # GIVEN: Test object
+        log_error_text = [call('(111.111.111.111) CLSS: {msg}'.format(msg=STATUS_MSG[E_PROJECTOR]))]
+        log_debug_text = [call('(111.111.111.111) Processing command "CLSS" with data "ERR4"'),
+                          call('(111.111.111.111) Calling function for CLSS')]
+
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: process_command called with ERR4
+            pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_PROJECTOR])
+
+            # THEN: Appropriate log entries should have been made and methods called
+            mock_log.error.assert_has_calls(log_error_text)
+            mock_log.debug.assert_has_calls(log_debug_text)
+            mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_PROJECTOR])
 
     def test_process_command_future(self):
         """
         Test command valid but no method to process yet
         """
-        # GIVEN: Initial mocks and data
-        mock_log = patch.object(openlp.core.projectors.pjlink, 'log').start()
-        mock_functions = patch.object(self.pjlink_test, 'pjlink_functions').start()
-        mock_functions.return_value = []
-
-        pjlink = self.pjlink_test
-        log_text = '(111.111.111.111) Unable to process command="CLSS" (Future option?)'
-
-        # WHEN: process_command called with an unknown command
-        pjlink.process_command(cmd='CLSS', data='DONT CARE')
-
-        # THEN: Error should be logged and no command called
-        self.assertFalse(mock_functions.called, 'Should not have gotten to the end of the method')
-        mock_log.warning.assert_called_once_with(log_text)
-
-    @patch.object(pjlink_test, 'pjlink_functions')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_process_command_invalid(self, mock_log, mock_functions):
-        """
-        Test not a valid command
-        """
         # GIVEN: Test object
-        pjlink = pjlink_test
-        mock_functions.reset_mock()
-        mock_log.reset_mock()
-
-        # WHEN: process_command called with an unknown command
-        pjlink.process_command(cmd='Unknown', data='Dont Care')
-        log_text = '(127.0.0.1) Ignoring command="Unknown" (Invalid/Unknown)'
-
-        # THEN: Error should be logged and no command called
-        self.assertFalse(mock_functions.called, 'Should not have gotten to the end of the method')
-        mock_log.error.assert_called_once_with(log_text)
+        log_warning_text = [call('(111.111.111.111) Unable to process command="CLSS" (Future option?)')]
+        log_debug_text = [call('(111.111.111.111) Processing command "CLSS" with data "Huh?"')]
+
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.pjlink_functions = MagicMock()
+
+            # WHEN: Processing a possible future command
+            pjlink.process_command(cmd='CLSS', data="Huh?")
+
+            # THEN: Appropriate log entries should have been made and methods called/not called
+            mock_log.debug.assert_has_calls(log_debug_text)
+            mock_log.warning.assert_has_calls(log_warning_text)
+            self.assertFalse(pjlink.pjlink_functions.called, 'Should not have accessed pjlink_functions')
+            self.assertFalse(mock_process_clss.called, 'Should not have called process_clss')
 
     def test_process_command_ok(self):
         """
         Test command returned success
         """
         # GIVEN: Initial mocks and data
-        mock_log = patch.object(openlp.core.projectors.pjlink, 'log').start()
-        mock_send_command = patch.object(self.pjlink_test, 'send_command').start()
-
-        pjlink = self.pjlink_test
-        log_text = '(111.111.111.111) Command "POWR" returned OK'
-
-        # WHEN: process_command called with a command that returns OK
-        pjlink.process_command(cmd='POWR', data='OK')
-
-        # THEN: Appropriate calls should have been made
-        mock_log.debug.assert_called_with(log_text)
-        mock_send_command.assert_called_once_with(cmd='POWR')
+        # GIVEN: Test object and mocks
+        log_debug_calls = [call('(111.111.111.111) Processing command "CLSS" with data "OK"'),
+                           call('(111.111.111.111) Command "CLSS" returned OK')]
+
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: process_command is called with valid function and data
+            pjlink.process_command(cmd='CLSS', data='OK')
+
+            # THEN: Appropriate log entries should have been made and methods called
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            mock_send_command.assert_called_once_with(cmd='CLSS')
+            mock_process_clss.assert_not_called()

=== modified file 'tests/functional/openlp_core/projectors/test_projector_pjlink_commands_01.py'
--- tests/functional/openlp_core/projectors/test_projector_pjlink_commands_01.py	2017-12-09 11:17:05 +0000
+++ tests/functional/openlp_core/projectors/test_projector_pjlink_commands_01.py	2017-12-25 08:57:51 +0000
@@ -23,372 +23,402 @@
 Package to test the openlp.core.projectors.pjlink commands package.
 """
 from unittest import TestCase
-from unittest.mock import patch
+from unittest.mock import call, patch
 
 import openlp.core.projectors.pjlink
-from openlp.core.projectors.constants import ERROR_STRING, PJLINK_ERST_DATA, PJLINK_ERST_STATUS, \
-    PJLINK_POWR_STATUS, \
-    E_ERROR, E_NOT_CONNECTED, E_SOCKET_ADDRESS_NOT_AVAILABLE, E_UNKNOWN_SOCKET_ERROR, E_WARN, \
-    S_CONNECTED, S_OFF, S_ON, S_NOT_CONNECTED, S_CONNECTING, S_STANDBY
+from openlp.core.projectors.constants import PJLINK_ERST_DATA, PJLINK_ERST_STATUS, PJLINK_POWR_STATUS, \
+    STATUS_CODE, STATUS_MSG, E_ERROR, E_NOT_CONNECTED, E_UNKNOWN_SOCKET_ERROR, E_WARN, \
+    S_CONNECTED, S_CONNECTING, S_OFF, S_OK, S_ON, S_NOT_CONNECTED, S_STANDBY
 from openlp.core.projectors.db import Projector
 from openlp.core.projectors.pjlink import PJLink
 
-from tests.resources.projector.data import TEST_PIN, TEST1_DATA
-
-pjlink_test = PJLink(Projector(**TEST1_DATA), pin=TEST_PIN, no_poll=True)
-pjlink_test.ip = '127.0.0.1'
-
-# Create a list of ERST positional data so we don't have to redo the same buildup multiple times
-PJLINK_ERST_POSITIONS = []
-for pos in range(0, len(PJLINK_ERST_DATA)):
-    if pos in PJLINK_ERST_DATA:
-        PJLINK_ERST_POSITIONS.append(PJLINK_ERST_DATA[pos])
+from tests.resources.projector.data import TEST1_DATA
 
 
 class TestPJLinkCommands(TestCase):
     """
     Tests for the PJLinkCommands class part 1
     """
-    @patch.object(pjlink_test, 'changeStatus')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_change_status_connection_error(self, mock_log, mock_change_status):
+    def test_projector_change_status_unknown_socket_error(self):
         """
         Test change_status with connection error
         """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.projector_status = 0
-        pjlink.status_connect = 0
-        test_code = E_UNKNOWN_SOCKET_ERROR
-        mock_change_status.reset_mock()
-        mock_log.reset_mock()
-
-        # WHEN: change_status called with unknown socket error
-        pjlink.change_status(status=test_code, msg=None)
-
-        # THEN: Proper settings should change and signals sent
-        self.assertEqual(pjlink.projector_status, E_NOT_CONNECTED, 'Projector status should be NOT CONNECTED')
-        self.assertEqual(pjlink.status_connect, E_NOT_CONNECTED, 'Status connect should be NOT CONNECTED')
-        mock_change_status.emit.assert_called_once_with(pjlink.ip, E_UNKNOWN_SOCKET_ERROR,
-                                                        'An unidentified error occurred')
-        self.assertEqual(mock_log.debug.call_count, 3, 'Debug log should have been called 3 times')
-
-    @patch.object(pjlink_test, 'changeStatus')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_change_status_connection_status_connecting(self, mock_log, mock_change_status):
-        """
-        Test change_status with connection status
-        """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.projector_status = 0
-        pjlink.status_connect = 0
-        test_code = S_CONNECTING
-        mock_change_status.reset_mock()
-        mock_log.reset_mock()
-
-        # WHEN: change_status called with unknown socket error
-        pjlink.change_status(status=test_code, msg=None)
-
-        # THEN: Proper settings should change and signals sent
-        self.assertEqual(pjlink.projector_status, S_NOT_CONNECTED, 'Projector status should be NOT CONNECTED')
-        self.assertEqual(pjlink.status_connect, S_CONNECTING, 'Status connect should be CONNECTING')
-        mock_change_status.emit.assert_called_once_with(pjlink.ip, S_CONNECTING, 'Connecting')
-        self.assertEqual(mock_log.debug.call_count, 3, 'Debug log should have been called 3 times')
-
-    @patch.object(pjlink_test, 'changeStatus')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_change_status_connection_status_connected(self, mock_log, mock_change_status):
-        """
-        Test change_status with connection status
-        """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.projector_status = 0
-        pjlink.status_connect = 0
-        test_code = S_ON
-        mock_change_status.reset_mock()
-        mock_log.reset_mock()
-
-        # WHEN: change_status called with unknown socket error
-        pjlink.change_status(status=test_code, msg=None)
-
-        # THEN: Proper settings should change and signals sent
-        self.assertEqual(pjlink.projector_status, S_ON, 'Projector status should be ON')
-        self.assertEqual(pjlink.status_connect, S_CONNECTED, 'Status connect should be CONNECTED')
-        mock_change_status.emit.assert_called_once_with(pjlink.ip, S_ON, 'Power is on')
-        self.assertEqual(mock_log.debug.call_count, 3, 'Debug log should have been called 3 times')
-
-    @patch.object(pjlink_test, 'changeStatus')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_change_status_connection_status_with_message(self, mock_log, mock_change_status):
-        """
-        Test change_status with connection status
-        """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.projector_status = 0
-        pjlink.status_connect = 0
+        log_debug_calls = [
+            call('(111.111.111.111) Changing status to '
+                 '{status} "{msg}"'.format(status=STATUS_CODE[E_UNKNOWN_SOCKET_ERROR],
+                                           msg=STATUS_MSG[E_UNKNOWN_SOCKET_ERROR])),
+            call('(111.111.111.111) status_connect: '
+                 '{code}: "{msg}"'.format(code=STATUS_CODE[E_NOT_CONNECTED],
+                                          msg=STATUS_MSG[E_NOT_CONNECTED])),
+            call('(111.111.111.111) projector_status: '
+                 '{code}: "{msg}"'.format(code=STATUS_CODE[S_OK],
+                                          msg=STATUS_MSG[S_OK])),
+            call('(111.111.111.111) error_status: '
+                 '{code}: "{msg}"'.format(code=STATUS_CODE[E_UNKNOWN_SOCKET_ERROR],
+                                          msg=STATUS_MSG[E_UNKNOWN_SOCKET_ERROR]))]
+
+        # GIVEN: Test object and mocks
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'changeStatus') as mock_changeStatus, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.projector_status = 0
+            pjlink.status_connect = 0
+
+            # WHEN: change_status called with unknown socket error
+            pjlink.change_status(status=E_UNKNOWN_SOCKET_ERROR)
+
+            # THEN: Proper settings should change and signals sent
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            self.assertEqual(pjlink.projector_status, S_OK, 'Projector status should not have changed')
+            self.assertEqual(pjlink.status_connect, E_NOT_CONNECTED,
+                             'Status connect should be NOT CONNECTED')
+            self.assertTrue(mock_UpdateIcons.emit.called, 'Should have called UpdateIcons')
+            mock_changeStatus.emit.assert_called_once_with(pjlink.ip, E_UNKNOWN_SOCKET_ERROR,
+                                                           STATUS_MSG[E_UNKNOWN_SOCKET_ERROR])
+
+    def test_projector_change_status_connection_status_connecting(self):
+        """
+        Test change_status with connecting status
+        """
+        log_debug_calls = [
+            call('(111.111.111.111) Changing status to '
+                 '{status} "{msg}"'.format(status=STATUS_CODE[S_CONNECTING],
+                                           msg=STATUS_MSG[S_CONNECTING])),
+            call('(111.111.111.111) status_connect: '
+                 '{code}: "{msg}"'.format(code=STATUS_CODE[S_CONNECTING],
+                                          msg=STATUS_MSG[S_CONNECTING])),
+            call('(111.111.111.111) projector_status: '
+                 '{code}: "{msg}"'.format(code=STATUS_CODE[S_OK],
+                                          msg=STATUS_MSG[S_OK])),
+            call('(111.111.111.111) error_status: '
+                 '{code}: "{msg}"'.format(code=STATUS_CODE[S_OK],
+                                          msg=STATUS_MSG[S_OK]))]
+
+        # GIVEN: Test object and mocks
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'changeStatus') as mock_changeStatus, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.projector_status = 0
+            pjlink.status_connect = 0
+
+            # WHEN: change_status called with CONNECTING
+            pjlink.change_status(status=S_CONNECTING)
+
+            # THEN: Proper settings should change and signals sent
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            self.assertEqual(pjlink.projector_status, S_OK, 'Projector status should not have changed')
+            self.assertEqual(pjlink.status_connect, S_CONNECTING, 'Status connect should be CONNECTING')
+            self.assertTrue(mock_UpdateIcons.emit.called, 'Should have called UpdateIcons')
+            mock_changeStatus.emit.assert_called_once_with(pjlink.ip, S_CONNECTING, STATUS_MSG[S_CONNECTING])
+
+    def test_projector_change_status_connection_status_connected(self):
+        """
+        Test change_status with connected status
+        """
+        log_debug_calls = [
+            call('(111.111.111.111) Changing status to '
+                 '{status} "{msg}"'.format(status=STATUS_CODE[S_CONNECTED],
+                                           msg=STATUS_MSG[S_CONNECTED])),
+            call('(111.111.111.111) status_connect: '
+                 '{code}: "{msg}"'.format(code=STATUS_CODE[S_CONNECTED],
+                                          msg=STATUS_MSG[S_CONNECTED])),
+            call('(111.111.111.111) projector_status: '
+                 '{code}: "{msg}"'.format(code=STATUS_CODE[S_OK],
+                                          msg=STATUS_MSG[S_OK])),
+            call('(111.111.111.111) error_status: '
+                 '{code}: "{msg}"'.format(code=STATUS_CODE[S_OK],
+                                          msg=STATUS_MSG[S_OK]))]
+
+        # GIVEN: Test object and mocks
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'changeStatus') as mock_changeStatus:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.projector_status = 0
+            pjlink.status_connect = 0
+
+            # WHEN: change_status called with CONNECTED
+            pjlink.change_status(status=S_CONNECTED)
+
+            # THEN: Proper settings should change and signals sent
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            self.assertEqual(pjlink.projector_status, S_OK, 'Projector status should not have changed')
+            self.assertEqual(pjlink.status_connect, S_CONNECTED, 'Status connect should be CONNECTED')
+            mock_changeStatus.emit.assert_called_once_with(pjlink.ip, S_CONNECTED, 'Connected')
+
+    def test_projector_change_status_connection_status_with_message(self):
+        """
+        Test change_status with connection status
+        """
         test_message = 'Different Status Message than default'
-        test_code = S_ON
-        mock_change_status.reset_mock()
-        mock_log.reset_mock()
-
-        # WHEN: change_status called with unknown socket error
-        pjlink.change_status(status=test_code, msg=test_message)
-
-        # THEN: Proper settings should change and signals sent
-        self.assertEqual(pjlink.projector_status, S_ON, 'Projector status should be ON')
-        self.assertEqual(pjlink.status_connect, S_CONNECTED, 'Status connect should be CONNECTED')
-        mock_change_status.emit.assert_called_once_with(pjlink.ip, S_ON, test_message)
-        self.assertEqual(mock_log.debug.call_count, 3, 'Debug log should have been called 3 times')
-
-    @patch.object(pjlink_test, 'send_command')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_get_av_mute_status(self, mock_log, mock_send_command):
+        log_debug_calls = [
+            call('(111.111.111.111) Changing status to {status} "{msg}"'.format(status=STATUS_CODE[S_ON],
+                                                                                msg=test_message)),
+            call('(111.111.111.111) status_connect: {code}: "{msg}"'.format(code=STATUS_CODE[S_OK],
+                                                                            msg=test_message)),
+            call('(111.111.111.111) projector_status: {code}: "{msg}"'.format(code=STATUS_CODE[S_ON],
+                                                                              msg=test_message)),
+            call('(111.111.111.111) error_status: {code}: "{msg}"'.format(code=STATUS_CODE[S_OK],
+                                                                          msg=test_message))]
+
+        # GIVEN: Test object and mocks
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'changeStatus') as mock_changeStatus:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.projector_status = 0
+            pjlink.status_connect = 0
+
+            # WHEN: change_status called with projector ON status
+            pjlink.change_status(status=S_ON, msg=test_message)
+
+            # THEN: Proper settings should change and signals sent
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            self.assertEqual(pjlink.projector_status, S_ON, 'Projector status should be ON')
+            self.assertEqual(pjlink.status_connect, S_OK, 'Status connect should not have changed')
+            mock_changeStatus.emit.assert_called_once_with(pjlink.ip, S_ON, test_message)
+
+    def test_projector_get_av_mute_status(self):
         """
         Test sending command to retrieve shutter/audio state
         """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        mock_log.reset_mock()
-        mock_send_command.reset_mock()
         test_data = 'AVMT'
-        test_log = '(127.0.0.1) Sending AVMT command'
-
-        # WHEN: get_av_mute_status is called
-        pjlink.get_av_mute_status()
-
-        # THEN: log data and send_command should have been called
-        mock_log.debug.assert_called_once_with(test_log)
-        mock_send_command.assert_called_once_with(cmd=test_data)
-
-    @patch.object(pjlink_test, 'send_command')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_get_available_inputs(self, mock_log, mock_send_command):
+        log_debug_calls = [call('(111.111.111.111) reset_information() connect status is '
+                                '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])),
+                           call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))]
+
+        # GIVEN: Test object and mocks
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: get_av_mute_status is called
+            pjlink.get_av_mute_status()
+
+            # THEN: log data and send_command should have been called
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            mock_send_command.assert_called_once_with(cmd=test_data)
+
+    def test_projector_get_available_inputs(self):
         """
         Test sending command to retrieve avaliable inputs
         """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        mock_log.reset_mock()
-        mock_send_command.reset_mock()
         test_data = 'INST'
-        test_log = '(127.0.0.1) Sending INST command'
-
-        # WHEN: get_available_inputs is called
-        pjlink.get_available_inputs()
-
-        # THEN: log data and send_command should have been called
-        mock_log.debug.assert_called_once_with(test_log)
-        mock_send_command.assert_called_once_with(cmd=test_data)
-
-    @patch.object(pjlink_test, 'send_command')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_get_error_status(self, mock_log, mock_send_command):
+        log_debug_calls = [call('(111.111.111.111) reset_information() connect status is '
+                                '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])),
+                           call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))]
+
+        # GIVEN: Test object and mocks
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: get_available_inputs is called
+            pjlink.get_available_inputs()
+
+            # THEN: log data and send_command should have been called
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            mock_send_command.assert_called_once_with(cmd=test_data)
+
+    def test_projector_get_error_status(self):
         """
         Test sending command to retrieve projector error status
         """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        mock_log.reset_mock()
-        mock_send_command.reset_mock()
         test_data = 'ERST'
-        test_log = '(127.0.0.1) Sending ERST command'
-
-        # WHEN: get_error_status is called
-        pjlink.get_error_status()
-
-        # THEN: log data and send_command should have been called
-        mock_log.debug.assert_called_once_with(test_log)
-        mock_send_command.assert_called_once_with(cmd=test_data)
-
-    @patch.object(pjlink_test, 'send_command')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_get_input_source(self, mock_log, mock_send_command):
+        log_debug_calls = [call('(111.111.111.111) reset_information() connect status is '
+                                '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])),
+                           call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))]
+        # GIVEN: Test object and mocks
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: get_error_status is called
+            pjlink.get_error_status()
+
+            # THEN: log data and send_command should have been called
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            mock_send_command.assert_called_once_with(cmd=test_data)
+
+    def test_projector_get_input_source(self):
         """
         Test sending command to retrieve current input
         """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        mock_log.reset_mock()
-        mock_send_command.reset_mock()
         test_data = 'INPT'
-        test_log = '(127.0.0.1) Sending INPT command'
-
-        # WHEN: get_input_source is called
-        pjlink.get_input_source()
-
-        # THEN: log data and send_command should have been called
-        mock_log.debug.assert_called_once_with(test_log)
-        mock_send_command.assert_called_once_with(cmd=test_data)
-
-    @patch.object(pjlink_test, 'send_command')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_get_lamp_status(self, mock_log, mock_send_command):
+        log_debug_calls = [call('(111.111.111.111) reset_information() connect status is '
+                                '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])),
+                           call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))]
+
+        # GIVEN: Test object and mocks
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: get_input_source is called
+            pjlink.get_input_source()
+
+            # THEN: log data and send_command should have been called
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            mock_send_command.assert_called_once_with(cmd=test_data)
+
+    def test_projector_get_lamp_status(self):
         """
         Test sending command to retrieve lamp(s) status
         """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        mock_log.reset_mock()
-        mock_send_command.reset_mock()
         test_data = 'LAMP'
-        test_log = '(127.0.0.1) Sending LAMP command'
-
-        # WHEN: get_lamp_status is called
-        pjlink.get_lamp_status()
-
-        # THEN: log data and send_command should have been called
-        mock_log.debug.assert_called_once_with(test_log)
-        mock_send_command.assert_called_once_with(cmd=test_data)
-
-    @patch.object(pjlink_test, 'send_command')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_get_manufacturer(self, mock_log, mock_send_command):
+        log_debug_calls = [call('(111.111.111.111) reset_information() connect status is '
+                                '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])),
+                           call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))]
+
+        # GIVEN: Test object and mocks
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: get_input_source is called
+            pjlink.get_lamp_status()
+
+            # THEN: log data and send_command should have been called
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            mock_send_command.assert_called_once_with(cmd=test_data)
+
+    def test_projector_get_manufacturer(self):
         """
         Test sending command to retrieve manufacturer name
         """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        mock_log.reset_mock()
-        mock_send_command.reset_mock()
         test_data = 'INF1'
-        test_log = '(127.0.0.1) Sending INF1 command'
-
-        # WHEN: get_manufacturer is called
-        pjlink.get_manufacturer()
-
-        # THEN: log data and send_command should have been called
-        mock_log.debug.assert_called_once_with(test_log)
-        mock_send_command.assert_called_once_with(cmd=test_data)
-
-    @patch.object(pjlink_test, 'send_command')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_get_model(self, mock_log, mock_send_command):
+        log_debug_calls = [call('(111.111.111.111) reset_information() connect status is '
+                                '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])),
+                           call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))]
+
+        # GIVEN: Test object and mocks
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: get_input_source is called
+            pjlink.get_manufacturer()
+
+            # THEN: log data and send_command should have been called
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            mock_send_command.assert_called_once_with(cmd=test_data)
+
+    def test_projector_get_model(self):
         """
         Test sending command to get model information
         """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        mock_log.reset_mock()
-        mock_send_command.reset_mock()
         test_data = 'INF2'
-        test_log = '(127.0.0.1) Sending INF2 command'
-
-        # WHEN: get_model is called
-        pjlink.get_model()
-
-        # THEN: log data and send_command should have been called
-        mock_log.debug.assert_called_once_with(test_log)
-        mock_send_command.assert_called_once_with(cmd=test_data)
-
-    @patch.object(pjlink_test, 'send_command')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_get_name(self, mock_log, mock_send_command):
+        log_debug_calls = [call('(111.111.111.111) reset_information() connect status is '
+                                '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])),
+                           call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))]
+
+        # GIVEN: Test object and mocks
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: get_input_source is called
+            pjlink.get_model()
+
+            # THEN: log data and send_command should have been called
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            mock_send_command.assert_called_once_with(cmd=test_data)
+
+    def test_projector_get_name(self):
         """
         Test sending command to get user-assigned name
         """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        mock_log.reset_mock()
-        mock_send_command.reset_mock()
         test_data = 'NAME'
-        test_log = '(127.0.0.1) Sending NAME command'
-
-        # WHEN: get_name is called
-        pjlink.get_name()
-
-        # THEN: log data and send_command should have been called
-        mock_log.debug.assert_called_once_with(test_log)
-        mock_send_command.assert_called_once_with(cmd=test_data)
-
-    @patch.object(pjlink_test, 'send_command')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_get_other_info(self, mock_log, mock_send_command):
+        log_debug_calls = [call('(111.111.111.111) reset_information() connect status is '
+                                '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])),
+                           call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))]
+
+        # GIVEN: Test object and mocks
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: get_input_source is called
+            pjlink.get_name()
+
+            # THEN: log data and send_command should have been called
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            mock_send_command.assert_called_once_with(cmd=test_data)
+
+    def test_projector_get_other_info(self):
         """
         Test sending command to retrieve other information
         """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        mock_log.reset_mock()
-        mock_send_command.reset_mock()
         test_data = 'INFO'
-        test_log = '(127.0.0.1) Sending INFO command'
-
-        # WHEN: get_other_info is called
-        pjlink.get_other_info()
-
-        # THEN: log data and send_command should have been called
-        mock_log.debug.assert_called_once_with(test_log)
-        mock_send_command.assert_called_once_with(cmd=test_data)
-
-    @patch.object(pjlink_test, 'send_command')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_get_power_status(self, mock_log, mock_send_command):
+        log_debug_calls = [call('(111.111.111.111) reset_information() connect status is '
+                                '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])),
+                           call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))]
+
+        # GIVEN: Test object and mocks
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: get_input_source is called
+            pjlink.get_other_info()
+
+            # THEN: log data and send_command should have been called
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            mock_send_command.assert_called_once_with(cmd=test_data)
+
+    def test_projector_get_power_status(self):
         """
         Test sending command to retrieve current power state
         """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        mock_log.reset_mock()
-        mock_send_command.reset_mock()
         test_data = 'POWR'
-        test_log = '(127.0.0.1) Sending POWR command'
-
-        # WHEN: get_power_status called
-        pjlink.get_power_status()
-
-        # THEN: log data and send_command should have been called
-        mock_log.debug.assert_called_once_with(test_log)
-        mock_send_command.assert_called_once_with(cmd=test_data)
-
-    def test_projector_get_status_error(self):
-        """
-        Test to check returned information for error code
-        """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        test_string = 'E_SOCKET_ADDRESS_NOT_AVAILABLE'
-        test_message = 'The address specified to socket.bind() does not belong to the host'
-
-        # WHEN: get_status called
-        string, message = pjlink._get_status(status=E_SOCKET_ADDRESS_NOT_AVAILABLE)
-
-        # THEN: Proper strings should have been returned
-        self.assertEqual(string, test_string, 'Code as string should have been returned')
-        self.assertEqual(message, test_message, 'Description of code should have been returned')
+        log_debug_calls = [call('(111.111.111.111) reset_information() connect status is '
+                                '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])),
+                           call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))]
+
+        # GIVEN: Test object and mocks
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: get_input_source is called
+            pjlink.get_power_status()
+
+            # THEN: log data and send_command should have been called
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            mock_send_command.assert_called_once_with(cmd=test_data)
 
     def test_projector_get_status_invalid(self):
         """
         Test to check returned information for error code
         """
         # GIVEN: Test object
-        pjlink = pjlink_test
-        test_string = 'Test string since get_status will only work with int'
-        test_message = 'Invalid status code'
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+        test_string = 'NaN test'
 
         # WHEN: get_status called
-        string, message = pjlink._get_status(status=test_string)
-
-        # THEN: Proper strings should have been returned
-        self.assertEqual(string, -1, 'Should have returned -1 as a bad status check')
-        self.assertEqual(message, test_message, 'Error message should have been returned')
-
-    def test_projector_get_status_status(self):
+        code, message = pjlink._get_status(status=test_string)
+
+        # THEN: Proper data should have been returned
+        self.assertEqual(code, -1, 'Should have returned -1 as a bad status check')
+        self.assertIsNone(message, 'Invalid code type should have returned None for message')
+
+    def test_projector_get_status_valid(self):
         """
         Test to check returned information for status codes
         """
         # GIVEN: Test object
-        pjlink = pjlink_test
-        test_string = 'S_NOT_CONNECTED'
-        test_message = 'Not connected'
+        test_message = 'Not Connected'
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
 
         # WHEN: get_status called
-        string, message = pjlink._get_status(status=S_NOT_CONNECTED)
+        code, message = pjlink._get_status(status=S_NOT_CONNECTED)
 
         # THEN: Proper strings should have been returned
-        self.assertEqual(string, test_string, 'Code as string should have been returned')
+        self.assertEqual(code, 'S_NOT_CONNECTED', 'Code returned should have been the same code that was sent')
         self.assertEqual(message, test_message, 'Description of code should have been returned')
 
     def test_projector_get_status_unknown(self):
@@ -396,25 +426,24 @@
         Test to check returned information for unknown code
         """
         # GIVEN: Test object
-        pjlink = pjlink_test
-        test_string = 999999
-        test_message = 'Unknown status'
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
 
         # WHEN: get_status called
-        string, message = pjlink._get_status(status=test_string)
+        code, message = pjlink._get_status(status=9999)
 
         # THEN: Proper strings should have been returned
-        self.assertEqual(string, test_string, 'Received code should have been returned')
-        self.assertEqual(message, test_message, 'Unknown status string should have been returned')
+        self.assertIsNone(code, 'Code returned should have been the same code that was sent')
+        self.assertIsNone(message, 'Should have returned None as message')
 
     def test_projector_process_inf1(self):
         """
         Test saving INF1 data (manufacturer)
         """
+        test_data = 'TEst INformation MultiCase'
+
         # GIVEN: Test object
-        pjlink = pjlink_test
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
         pjlink.manufacturer = None
-        test_data = 'TEst INformation MultiCase'
 
         # WHEN: process_inf called with test data
         pjlink.process_inf1(data=test_data)
@@ -426,10 +455,11 @@
         """
         Test saving INF2 data (model)
         """
+        test_data = 'TEst moDEl MultiCase'
+
         # GIVEN: Test object
-        pjlink = pjlink_test
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
         pjlink.model = None
-        test_data = 'TEst moDEl MultiCase'
 
         # WHEN: process_inf called with test data
         pjlink.process_inf2(data=test_data)
@@ -441,10 +471,11 @@
         """
         Test saving INFO data (other information)
         """
+        test_data = 'TEst ExtrANEous MultiCase INformatoin that MFGR might Set'
+
         # GIVEN: Test object
-        pjlink = pjlink_test
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
         pjlink.other_info = None
-        test_data = 'TEst ExtrANEous MultiCase INformatoin that MFGR might Set'
 
         # WHEN: process_inf called with test data
         pjlink.process_info(data=test_data)
@@ -452,509 +483,558 @@
         # THEN: Data should be saved
         self.assertEqual(pjlink.other_info, test_data, 'Test data should have been saved')
 
-    @patch.object(pjlink_test, 'projectorUpdateIcons')
-    def test_projector_process_avmt_bad_data(self, mock_UpdateIcons):
+    def test_projector_process_avmt_bad_data(self):
         """
         Test avmt bad data fail
         """
         # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.shutter = True
-        pjlink.mute = True
-
-        # WHEN: Called with an invalid setting
-        pjlink.process_avmt('36')
-
-        # THEN: Shutter should be closed and mute should be True
-        self.assertTrue(pjlink.shutter, 'Shutter should changed')
-        self.assertTrue(pjlink.mute, 'Audio should not have changed')
-        self.assertFalse(mock_UpdateIcons.emit.called, 'Update icons should NOT have been called')
-
-    @patch.object(pjlink_test, 'projectorUpdateIcons')
-    def test_projector_process_avmt_closed_muted(self, mock_UpdateIcons):
+        with patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.shutter = True
+            pjlink.mute = True
+
+            # WHEN: Called with an invalid setting
+            pjlink.process_avmt('36')
+
+            # THEN: Shutter should be closed and mute should be True
+            self.assertTrue(pjlink.shutter, 'Shutter should changed')
+            self.assertTrue(pjlink.mute, 'Audio should not have changed')
+            self.assertFalse(mock_UpdateIcons.emit.called, 'Update icons should NOT have been called')
+
+    def test_projector_process_avmt_closed_muted(self):
         """
         Test avmt status shutter closed and mute off
         """
         # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.shutter = False
-        pjlink.mute = False
-
-        # WHEN: Called with setting shutter to closed and mute on
-        pjlink.process_avmt('31')
-
-        # THEN: Shutter should be closed and mute should be True
-        self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed')
-        self.assertTrue(pjlink.mute, 'Audio should be muted')
-        self.assertTrue(mock_UpdateIcons.emit.called, 'Update icons should have been called')
-
-    @patch.object(pjlink_test, 'projectorUpdateIcons')
-    def test_projector_process_avmt_shutter_closed(self, mock_UpdateIcons):
+        with patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.shutter = False
+            pjlink.mute = False
+
+            # WHEN: Called with setting shutter to closed and mute on
+            pjlink.process_avmt('31')
+
+            # THEN: Shutter should be closed and mute should be True
+            self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed')
+            self.assertTrue(pjlink.mute, 'Audio should be muted')
+            self.assertTrue(mock_UpdateIcons.emit.called, 'Update icons should have been called')
+
+    def test_projector_process_avmt_shutter_closed(self):
         """
         Test avmt status shutter closed and audio unchanged
         """
         # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.shutter = False
-        pjlink.mute = True
-
-        # WHEN: Called with setting shutter closed and mute off
-        pjlink.process_avmt('11')
-
-        # THEN: Shutter should be True and mute should be False
-        self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed')
-        self.assertTrue(pjlink.mute, 'Audio should not have changed')
-        self.assertTrue(mock_UpdateIcons.emit.called, 'Update icons should have been called')
-
-    @patch.object(pjlink_test, 'projectorUpdateIcons')
-    def test_projector_process_avmt_audio_muted(self, mock_UpdateIcons):
+        with patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.shutter = False
+            pjlink.mute = True
+
+            # WHEN: Called with setting shutter closed and mute off
+            pjlink.process_avmt('11')
+
+            # THEN: Shutter should be True and mute should be False
+            self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed')
+            self.assertTrue(pjlink.mute, 'Audio should not have changed')
+            self.assertTrue(mock_UpdateIcons.emit.called, 'Update icons should have been called')
+
+    def test_projector_process_avmt_audio_muted(self):
         """
         Test avmt status shutter unchanged and mute on
         """
         # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.shutter = True
-        pjlink.mute = False
-
-        # WHEN: Called with setting shutter closed and mute on
-        pjlink.process_avmt('21')
-
-        # THEN: Shutter should be closed and mute should be True
-        self.assertTrue(pjlink.shutter, 'Shutter should not have changed')
-        self.assertTrue(pjlink.mute, 'Audio should be off')
-        self.assertTrue(mock_UpdateIcons.emit.called, 'Update icons should have been called')
-
-    @patch.object(pjlink_test, 'projectorUpdateIcons')
-    def test_projector_process_avmt_open_unmuted(self, mock_UpdateIcons):
+        with patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.shutter = True
+            pjlink.mute = False
+
+            # WHEN: Called with setting shutter closed and mute on
+            pjlink.process_avmt('21')
+
+            # THEN: Shutter should be closed and mute should be True
+            self.assertTrue(pjlink.shutter, 'Shutter should not have changed')
+            self.assertTrue(pjlink.mute, 'Audio should be off')
+            self.assertTrue(mock_UpdateIcons.emit.called, 'Update icons should have been called')
+
+    def test_projector_process_avmt_open_unmuted(self):
         """
         Test avmt status shutter open and mute off
         """
         # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.shutter = True
-        pjlink.mute = True
-
-        # WHEN: Called with setting shutter to closed and mute on
-        pjlink.process_avmt('30')
-
-        # THEN: Shutter should be closed and mute should be True
-        self.assertFalse(pjlink.shutter, 'Shutter should have been set to open')
-        self.assertFalse(pjlink.mute, 'Audio should be on')
-        self.assertTrue(mock_UpdateIcons.emit.called, 'Update icons should have been called')
+        with patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.shutter = True
+            pjlink.mute = True
+
+            # WHEN: Called with setting shutter to closed and mute on
+            pjlink.process_avmt('30')
+
+            # THEN: Shutter should be closed and mute should be True
+            self.assertFalse(pjlink.shutter, 'Shutter should have been set to open')
+            self.assertFalse(pjlink.mute, 'Audio should be on')
+            self.assertTrue(mock_UpdateIcons.emit.called, 'Update icons should have been called')
 
     def test_projector_process_clss_one(self):
         """
         Test class 1 sent from projector
         """
         # GIVEN: Test object
-        pjlink = pjlink_test
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
 
         # WHEN: Process class response
         pjlink.process_clss('1')
 
         # THEN: Projector class should be set to 1
-        self.assertEqual(pjlink.pjlink_class, '1',
-                         'Projector should have set class=1')
+        self.assertEqual(pjlink.pjlink_class, '1', 'Projector should have set class=1')
 
     def test_projector_process_clss_two(self):
         """
         Test class 2 sent from projector
         """
         # GIVEN: Test object
-        pjlink = pjlink_test
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
 
         # WHEN: Process class response
         pjlink.process_clss('2')
 
         # THEN: Projector class should be set to 1
-        self.assertEqual(pjlink.pjlink_class, '2',
-                         'Projector should have set class=2')
-
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_process_clss_invalid_nan(self, mock_log):
-        """
-        Test CLSS reply has no class number
-        """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-
-        # WHEN: Process invalid reply
-        pjlink.process_clss('Z')
-        log_text = '(127.0.0.1) NAN CLSS version reply "Z" - defaulting to class "1"'
-
-        # THEN: Projector class should be set with default value
-        self.assertEqual(pjlink.pjlink_class, '1',
-                         'Non-standard class reply should have set class=1')
-        mock_log.error.assert_called_once_with(log_text)
-
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_process_clss_invalid_no_version(self, mock_log):
-        """
-        Test CLSS reply has no class number
-        """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-
-        # WHEN: Process invalid reply
-        pjlink.process_clss('Invalid')
-        log_text = '(127.0.0.1) No numbers found in class version reply "Invalid" - defaulting to class "1"'
-
-        # THEN: Projector class should be set with default value
-        self.assertEqual(pjlink.pjlink_class, '1',
-                         'Non-standard class reply should have set class=1')
-        mock_log.error.assert_called_once_with(log_text)
+        self.assertEqual(pjlink.pjlink_class, '2', 'Projector should have set class=2')
+
+    def test_projector_process_clss_invalid_nan(self):
+        """
+        Test CLSS reply has no class number
+        """
+        log_debug_calls = [call('(111.111.111.111) reset_information() connect status is '
+                                '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])),
+                           call('(111.111.111.111) Setting pjlink_class for this projector to "1"')]
+        log_error_calls = [call('(111.111.111.111) NAN CLSS version reply "Z" - defaulting to class "1"')]
+
+        # GIVEN: Test object
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: Process invalid reply
+            pjlink.process_clss('Z')
+
+            # THEN: Projector class should be set with default value
+            self.assertEqual(pjlink.pjlink_class, '1', 'Invalid NaN class reply should have set class=1')
+            mock_log.error.assert_has_calls(log_error_calls)
+            mock_log.debug.assert_has_calls(log_debug_calls)
+
+    def test_projector_process_clss_invalid_no_version(self):
+        """
+        Test CLSS reply has no class number
+        """
+        log_debug_calls = [call('(111.111.111.111) reset_information() connect status is '
+                                '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])),
+                           call('(111.111.111.111) Setting pjlink_class for this projector to "1"')]
+        log_error_calls = [call('(111.111.111.111) No numbers found in class version reply "Invalid" '
+                                '- defaulting to class "1"')]
+
+        # GIVEN: Test object
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: Process invalid reply
+            pjlink.process_clss('Invalid')
+
+            # THEN: Projector class should be set with default value
+            self.assertEqual(pjlink.pjlink_class, '1', 'Invalid class reply should have set class=1')
+            mock_log.error.assert_has_calls(log_error_calls)
+            mock_log.debug.assert_has_calls(log_debug_calls)
 
     def test_projector_process_erst_all_ok(self):
         """
-        Test test_projector_process_erst_all_ok
+        Test to verify pjlink.projector_errors is set to None when no errors
         """
+        chk_data = '0' * PJLINK_ERST_DATA['DATA_LENGTH']
+
         # GIVEN: Test object
-        pjlink = pjlink_test
-        chk_test = PJLINK_ERST_STATUS['OK']
-        chk_param = chk_test * len(PJLINK_ERST_POSITIONS)
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
 
         # WHEN: process_erst with no errors
-        pjlink.process_erst(chk_param)
+        pjlink.process_erst(chk_data)
 
         # THEN: PJLink instance errors should be None
         self.assertIsNone(pjlink.projector_errors, 'projector_errors should have been set to None')
 
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_process_erst_data_invalid_length(self, mock_log):
+    def test_projector_process_erst_data_invalid_length(self):
         """
         Test test_projector_process_erst_data_invalid_length
         """
+        chk_data = '0' * (PJLINK_ERST_DATA['DATA_LENGTH'] + 1)
+        log_debug_calls = [call('(111.111.111.111) reset_information() connect status is '
+                                '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED]))]
+        log_warn_calls = [call('111.111.111.111) Invalid error status response "0000000": '
+                               'length != {chk}'.format(chk=PJLINK_ERST_DATA['DATA_LENGTH']))]
+
         # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.projector_errors = None
-        log_text = '127.0.0.1) Invalid error status response "11111111": length != 6'
-
-        # WHEN: process_erst called with invalid data (too many values
-        pjlink.process_erst('11111111')
-
-        # THEN: pjlink.projector_errors should be empty and warning logged
-        self.assertIsNone(pjlink.projector_errors, 'There should be no errors')
-        self.assertTrue(mock_log.warning.called, 'Warning should have been logged')
-        mock_log.warning.assert_called_once_with(log_text)
-
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_process_erst_data_invalid_nan(self, mock_log):
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.projector_errors = None
+
+            # WHEN: process_erst called with invalid data (too many values
+            pjlink.process_erst(chk_data)
+
+            # THEN: pjlink.projector_errors should be empty and warning logged
+            self.assertIsNone(pjlink.projector_errors, 'There should be no errors')
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            mock_log.warning.assert_has_calls(log_warn_calls)
+
+    def test_projector_process_erst_data_invalid_nan(self):
         """
         Test test_projector_process_erst_data_invalid_nan
         """
+        chk_data = 'Z' + ('0' * (PJLINK_ERST_DATA['DATA_LENGTH'] - 1))
+        log_debug_calls = [call('(111.111.111.111) reset_information() connect status is '
+                                '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED]))]
+        log_warn_calls = [call('(111.111.111.111) Invalid error status response "Z00000"')]
+
         # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.projector_errors = None
-        log_text = '(127.0.0.1) Invalid error status response "1111Z1"'
-
-        # WHEN: process_erst called with invalid data (too many values
-        pjlink.process_erst('1111Z1')
-
-        # THEN: pjlink.projector_errors should be empty and warning logged
-        self.assertIsNone(pjlink.projector_errors, 'There should be no errors')
-        self.assertTrue(mock_log.warning.called, 'Warning should have been logged')
-        mock_log.warning.assert_called_once_with(log_text)
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.projector_errors = None
+
+            # WHEN: process_erst called with invalid data (too many values
+            pjlink.process_erst(chk_data)
+
+            # THEN: pjlink.projector_errors should be empty and warning logged
+            self.assertIsNone(pjlink.projector_errors, 'There should be no errors')
+            mock_log.debug.assert_has_calls(log_debug_calls)
+            mock_log.warning.assert_has_calls(log_warn_calls)
 
     def test_projector_process_erst_all_warn(self):
         """
         Test test_projector_process_erst_all_warn
         """
+        chk_data = '{fan}{lamp}{temp}{cover}{filt}{other}'.format(fan=PJLINK_ERST_STATUS[E_WARN],
+                                                                  lamp=PJLINK_ERST_STATUS[E_WARN],
+                                                                  temp=PJLINK_ERST_STATUS[E_WARN],
+                                                                  cover=PJLINK_ERST_STATUS[E_WARN],
+                                                                  filt=PJLINK_ERST_STATUS[E_WARN],
+                                                                  other=PJLINK_ERST_STATUS[E_WARN])
+        chk_test = {'Fan': E_WARN,
+                    'Lamp': E_WARN,
+                    'Temperature': E_WARN,
+                    'Cover': E_WARN,
+                    'Filter': E_WARN,
+                    'Other': E_WARN}
+
         # GIVEN: Test object
-        pjlink = pjlink_test
-        chk_test = PJLINK_ERST_STATUS[E_WARN]
-        chk_string = ERROR_STRING[E_WARN]
-        chk_param = chk_test * len(PJLINK_ERST_POSITIONS)
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+        pjlink.projector_errors = None
 
         # WHEN: process_erst with status set to WARN
-        pjlink.process_erst(chk_param)
+        pjlink.process_erst(chk_data)
 
         # THEN: PJLink instance errors should match chk_value
-        for chk in pjlink.projector_errors:
-            self.assertEqual(pjlink.projector_errors[chk], chk_string,
-                             'projector_errors["{chk}"] should have been set to "{err}"'.format(chk=chk,
-                                                                                                err=chk_string))
+        self.assertEqual(pjlink.projector_errors, chk_test, 'Projector errors should be all E_WARN')
 
     def test_projector_process_erst_all_error(self):
         """
         Test test_projector_process_erst_all_error
         """
+        chk_data = '{fan}{lamp}{temp}{cover}{filt}{other}'.format(fan=PJLINK_ERST_STATUS[E_ERROR],
+                                                                  lamp=PJLINK_ERST_STATUS[E_ERROR],
+                                                                  temp=PJLINK_ERST_STATUS[E_ERROR],
+                                                                  cover=PJLINK_ERST_STATUS[E_ERROR],
+                                                                  filt=PJLINK_ERST_STATUS[E_ERROR],
+                                                                  other=PJLINK_ERST_STATUS[E_ERROR])
+        chk_test = {'Fan': E_ERROR,
+                    'Lamp': E_ERROR,
+                    'Temperature': E_ERROR,
+                    'Cover': E_ERROR,
+                    'Filter': E_ERROR,
+                    'Other': E_ERROR}
+
         # GIVEN: Test object
-        pjlink = pjlink_test
-        chk_test = PJLINK_ERST_STATUS[E_ERROR]
-        chk_string = ERROR_STRING[E_ERROR]
-        chk_param = chk_test * len(PJLINK_ERST_POSITIONS)
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+        pjlink.projector_errors = None
 
         # WHEN: process_erst with status set to WARN
-        pjlink.process_erst(chk_param)
+        pjlink.process_erst(chk_data)
 
         # THEN: PJLink instance errors should match chk_value
-        for chk in pjlink.projector_errors:
-            self.assertEqual(pjlink.projector_errors[chk], chk_string,
-                             'projector_errors["{chk}"] should have been set to "{err}"'.format(chk=chk,
-                                                                                                err=chk_string))
+        self.assertEqual(pjlink.projector_errors, chk_test, 'Projector errors should be all E_ERROR')
 
     def test_projector_process_erst_warn_cover_only(self):
         """
         Test test_projector_process_erst_warn_cover_only
         """
+        chk_data = '{fan}{lamp}{temp}{cover}{filt}{other}'.format(fan=PJLINK_ERST_STATUS[S_OK],
+                                                                  lamp=PJLINK_ERST_STATUS[S_OK],
+                                                                  temp=PJLINK_ERST_STATUS[S_OK],
+                                                                  cover=PJLINK_ERST_STATUS[E_WARN],
+                                                                  filt=PJLINK_ERST_STATUS[S_OK],
+                                                                  other=PJLINK_ERST_STATUS[S_OK])
+        chk_test = {'Cover': E_WARN}
+
         # GIVEN: Test object
-        pjlink = pjlink_test
-        chk_test = PJLINK_ERST_STATUS[E_WARN]
-        chk_string = ERROR_STRING[E_WARN]
-        pos = PJLINK_ERST_DATA['COVER']
-        build_chk = []
-        for check in range(0, len(PJLINK_ERST_POSITIONS)):
-            if check == pos:
-                build_chk.append(chk_test)
-            else:
-                build_chk.append(PJLINK_ERST_STATUS['OK'])
-        chk_param = ''.join(build_chk)
-
-        # WHEN: process_erst with cover only set to WARN and all others set to OK
-        pjlink.process_erst(chk_param)
-
-        # THEN: Only COVER should have an error
-        self.assertEqual(len(pjlink.projector_errors), 1, 'projector_errors should only have 1 error')
-        self.assertTrue(('Cover' in pjlink.projector_errors), 'projector_errors should have an error for "Cover"')
-        self.assertEqual(pjlink.projector_errors['Cover'],
-                         chk_string,
-                         'projector_errors["Cover"] should have error "{err}"'.format(err=chk_string))
-
-    def test_projector_process_inpt(self):
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+        pjlink.projector_errors = None
+
+        # WHEN: process_erst with status set to WARN
+        pjlink.process_erst(chk_data)
+
+        # THEN: PJLink instance errors should match only cover warning
+        assert 1 == len(pjlink.projector_errors), 'There should only be 1 error listed in projector_errors'
+        assert 'Cover' in pjlink.projector_errors, '"Cover" should be the only error listed'
+        assert pjlink.projector_errors['Cover'] == E_WARN, '"Cover" should have E_WARN listed as error'
+        assert chk_test == pjlink.projector_errors, 'projector_errors should match test errors'
+
+    def test_projector_process_inpt_valid(self):
         """
         Test input source status shows current input
         """
+        log_debug_calls = [call('(111.111.111.111) reset_information() connect status is S_NOT_CONNECTED')]
+        chk_source_available = ['11', '12', '21', '22', '31', '32']
+
         # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.source = '0'
-
-        # WHEN: Called with input source
-        pjlink.process_inpt('1')
-
-        # THEN: Input selected should reflect current input
-        self.assertEqual(pjlink.source, '1', 'Input source should be set to "1"')
-
-    @patch.object(pjlink_test, 'projectorUpdateIcons')
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_process_inst(self, mock_log, mock_UpdateIcons):
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.source_available = chk_source_available
+            pjlink.source = '11'
+
+            # WHEN: Called with input source
+            pjlink.process_inpt('21')
+
+            # THEN: Input selected should reflect current input
+            assert pjlink.source == '21', 'Input source should be set to "21"'
+            mock_log.debug.assert_has_calls(log_debug_calls)
+
+    def test_projector_process_input_not_in_list(self):
+        """
+        Test setting input outside of available inputs
+
+        TODO: Future test
+        """
+        pass
+
+    def test_projector_process_input_not_in_default(self):
+        """
+        Test setting input with no sources available
+        TODO: Future test
+        """
+        pass
+
+    def test_projector_process_input_invalid(self):
+        """
+        Test setting input with an invalid value
+
+        TODO: Future test
+        """
+
+    def test_projector_process_inst_class_1(self):
         """
         Test saving video source available information
         """
+        log_debug_calls = [call('(111.111.111.111) Setting projector sources_available to '
+                                '"[\'11\', \'12\', \'21\', \'22\', \'31\', \'32\']"')]
+        chk_data = '21 12 11 22 32 31'  # Although they should already be sorted, use unsorted to verify method
+        chk_test = ['11', '12', '21', '22', '31', '32']
+
         # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.source_available = []
-        test_data = '21 10 30 31 11 20'
-        test_saved = ["10", "11", "20", "21", "30", "31"]
-        log_data = "(127.0.0.1) Setting projector sources_available to " \
-                   "\"['10', '11', '20', '21', '30', '31']\""
-        mock_UpdateIcons.reset_mock()
-        mock_log.reset_mock()
-
-        # WHEN: process_inst called with test data
-        pjlink.process_inst(data=test_data)
-
-        # THEN: Data should have been sorted and saved properly
-        self.assertEqual(pjlink.source_available, test_saved, "Sources should have been sorted and saved")
-        mock_log.debug.assert_called_once_with(log_data)
-        self.assertTrue(mock_UpdateIcons.emit.called, 'Update Icons should have been called')
-
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_process_lamp_invalid(self, mock_log):
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.source_available = []
+
+            # WHEN: process_inst called with test data
+            pjlink.process_inst(data=chk_data)
+
+            # THEN: Data should have been sorted and saved properly
+            assert pjlink.source_available == chk_test, "Sources should have been sorted and saved"
+            mock_log.debug.assert_has_calls(log_debug_calls)
+
+    def test_projector_process_lamp_invalid(self):
         """
         Test status multiple lamp on/off and hours
         """
+        log_data = [call('(111.111.111.111) process_lamp(): Invalid data "11111 1 22222 0 333A3 1"')]
+
         # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.lamp = [{'Hours': 00000, 'On': True},
-                       {'Hours': 11111, 'On': False}]
-        log_data = '(127.0.0.1) process_lamp(): Invalid data "11111 1 22222 0 333A3 1"'
-
-        # WHEN: Call process_command with invalid lamp data
-        pjlink.process_lamp('11111 1 22222 0 333A3 1')
-
-        # THEN: lamps should not have changed
-        self.assertEqual(len(pjlink.lamp), 2,
-                         'Projector should have kept 2 lamps specified')
-        self.assertEqual(pjlink.lamp[0]['On'], True,
-                         'Lamp 1 power status should have been set to TRUE')
-        self.assertEqual(pjlink.lamp[0]['Hours'], 00000,
-                         'Lamp 1 hours should have been left at 00000')
-        self.assertEqual(pjlink.lamp[1]['On'], False,
-                         'Lamp 2 power status should have been set to FALSE')
-        self.assertEqual(pjlink.lamp[1]['Hours'], 11111,
-                         'Lamp 2 hours should have been left at 11111')
-        mock_log.warning.assert_called_once_with(log_data)
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.lamp = [{'Hours': 00000, 'On': True},
+                           {'Hours': 11111, 'On': False}]
+
+            # WHEN: Call process_command with invalid lamp data
+            pjlink.process_lamp('11111 1 22222 0 333A3 1')
+
+            # THEN: lamps should not have changed
+            assert 2 == len(pjlink.lamp), 'Projector should have kept 2 lamps specified'
+            assert pjlink.lamp[0]['On'] is True, 'Lamp 1 power status should have stayed TRUE'
+            assert 00000 == pjlink.lamp[0]['Hours'], 'Lamp 1 hours should have been left at 00000'
+            assert pjlink.lamp[1]['On'] is False, 'Lamp 2 power status should have stayed FALSE'
+            assert 11111 == pjlink.lamp[1]['Hours'], 'Lamp 2 hours should have been left at 11111'
+            mock_log.warning.assert_has_calls(log_data)
 
     def test_projector_process_lamp_multiple(self):
         """
         Test status multiple lamp on/off and hours
         """
         # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.lamps = []
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+        pjlink.lamp = []
 
-        # WHEN: Call process_command with lamp data
+        # WHEN: Call process_command with invalid lamp data
         pjlink.process_lamp('11111 1 22222 0 33333 1')
 
         # THEN: Lamp should have been set with proper lamp status
-        self.assertEqual(len(pjlink.lamp), 3,
-                         'Projector should have 3 lamps specified')
-        self.assertEqual(pjlink.lamp[0]['On'], True,
-                         'Lamp 1 power status should have been set to TRUE')
-        self.assertEqual(pjlink.lamp[0]['Hours'], 11111,
-                         'Lamp 1 hours should have been set to 11111')
-        self.assertEqual(pjlink.lamp[1]['On'], False,
-                         'Lamp 2 power status should have been set to FALSE')
-        self.assertEqual(pjlink.lamp[1]['Hours'], 22222,
-                         'Lamp 2 hours should have been set to 22222')
-        self.assertEqual(pjlink.lamp[2]['On'], True,
-                         'Lamp 3 power status should have been set to TRUE')
-        self.assertEqual(pjlink.lamp[2]['Hours'], 33333,
-                         'Lamp 3 hours should have been set to 33333')
+        assert 3 == len(pjlink.lamp), 'Projector should have 3 lamps specified'
+        assert pjlink.lamp[0]['On'] is True, 'Lamp 1 power status should have been set to TRUE'
+        assert 11111 == pjlink.lamp[0]['Hours'], 'Lamp 1 hours should have been set to 11111'
+        assert pjlink.lamp[1]['On'] is False, 'Lamp 2 power status should have been set to FALSE'
+        assert 22222 == pjlink.lamp[1]['Hours'], 'Lamp 2 hours should have been set to 22222'
+        assert pjlink.lamp[2]['On'] is True, 'Lamp 3 power status should have been set to TRUE'
+        assert 33333 == pjlink.lamp[2]['Hours'], 'Lamp 3 hours should have been set to 33333'
 
     def test_projector_process_lamp_single(self):
         """
         Test status lamp on/off and hours
         """
+
         # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.lamps = []
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+        pjlink.lamp = []
 
-        # WHEN: Call process_command with lamp data
+        # WHEN: Call process_command with invalid lamp data
         pjlink.process_lamp('22222 1')
 
         # THEN: Lamp should have been set with status=ON and hours=22222
-        self.assertEqual(pjlink.lamp[0]['On'], True,
-                         'Lamp power status should have been set to TRUE')
-        self.assertEqual(pjlink.lamp[0]['Hours'], 22222,
-                         'Lamp hours should have been set to 22222')
+        assert 1 == len(pjlink.lamp), 'Projector should have only 1 lamp'
+        assert pjlink.lamp[0]['On'] is True, 'Lamp power status should have been set to TRUE'
+        assert 22222 == pjlink.lamp[0]['Hours'], 'Lamp hours should have been set to 22222'
 
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_process_name(self, mock_log):
+    def test_projector_process_name(self):
         """
         Test saving NAME data from projector
         """
-        # GIVEN: Test data
-        pjlink = pjlink_test
-        test_data = "Some Name the End-User Set IN Projector"
-        test_log = '(127.0.0.1) Setting projector PJLink name to "Some Name the End-User Set IN Projector"'
-        mock_log.reset_mock()
-
-        # WHEN: process_name called with test data
-        pjlink.process_name(data=test_data)
-
-        # THEN: name should be set and logged
-        self.assertEqual(pjlink.pjlink_name, test_data, 'Name test data should have been saved')
-        mock_log.debug.assert_called_once_with(test_log)
-
-    @patch.object(pjlink_test, 'projectorUpdateIcons')
-    @patch.object(pjlink_test, 'send_command')
-    @patch.object(pjlink_test, 'change_status')
-    def test_projector_process_powr_on(self,
-                                       mock_change_status,
-                                       mock_send_command,
-                                       mock_UpdateIcons):
+        chk_data = "Some Name the End-User Set IN Projector"
+        log_debug_calls = [call('(111.111.111.111) Setting projector PJLink name to '
+                                '"Some Name the End-User Set IN Projector"')]
+
+        # GIVEN: Test object
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+
+            # WHEN: process_name called with test data
+            pjlink.process_name(data=chk_data)
+
+            # THEN: name should be set and logged
+            assert pjlink.pjlink_name == chk_data, 'Name test data should have been saved'
+            mock_log.debug.assert_has_calls(log_debug_calls)
+
+    def test_projector_process_powr_on(self):
         """
         Test status power to ON
         """
-        # GIVEN: Test object and preset
-        pjlink = pjlink_test
-        pjlink.power = S_STANDBY
-        test_data = PJLINK_POWR_STATUS[S_ON]
-
-        # WHEN: Call process_command with turn power on command
-        pjlink.process_command(cmd='POWR', data=test_data)
-
-        # THEN: Power should be set to ON
-        self.assertEqual(pjlink.power, S_ON, 'Power should have been set to ON')
-        mock_send_command.assert_called_once_with('INST')
-        mock_change_status.assert_called_once_with(PJLINK_POWR_STATUS[test_data])
-        self.assertEqual(mock_UpdateIcons.emit.called, True, 'projectorUpdateIcons should have been called')
-
-    @patch.object(pjlink_test, 'projectorUpdateIcons')
-    @patch.object(pjlink_test, 'send_command')
-    @patch.object(pjlink_test, 'change_status')
-    def test_projector_process_powr_invalid(self,
-                                            mock_change_status,
-                                            mock_send_command,
-                                            mock_UpdateIcons):
+        # GIVEN: Test object
+        with patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status') as mock_change_status, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.power = S_STANDBY
+
+            # WHEN: process_name called with test data
+            pjlink.process_powr(data=PJLINK_POWR_STATUS[S_ON])
+
+            # THEN: Power should be set to ON
+            assert pjlink.power == S_ON, 'Power should have been set to ON'
+            assert mock_UpdateIcons.emit.called is True, 'projectorUpdateIcons should have been called'
+            mock_send_command.assert_called_once_with('INST')
+            mock_change_status.assert_called_once_with(S_ON)
+
+    def test_projector_process_powr_invalid(self):
         """
         Test process_powr invalid call
         """
-        # GIVEN: Test object and preset
-        pjlink = pjlink_test
-        pjlink.power = S_STANDBY
-        test_data = '99'
-
-        # WHEN: Call process_command with turn power on command
-        pjlink.process_command(cmd='POWR', data=test_data)
-
-        # THEN: Power should be set to ON
-        self.assertEqual(pjlink.power, S_STANDBY, 'Power should not have changed')
-        self.assertFalse(mock_change_status.called, 'Change status should not have been called')
-        self.assertFalse(mock_send_command.called, 'send_command("INST") should not have been called')
-        self.assertFalse(mock_UpdateIcons.emit.called, 'projectorUpdateIcons should not have been called')
-
-    @patch.object(pjlink_test, 'projectorUpdateIcons')
-    @patch.object(pjlink_test, 'send_command')
-    @patch.object(pjlink_test, 'change_status')
-    def test_projector_process_powr_off(self,
-                                        mock_change_status,
-                                        mock_send_command,
-                                        mock_UpdateIcons):
+        log_warn_calls = [call('(111.111.111.111) Unknown power response: "99"')]
+
+        # GIVEN: Test object
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status') as mock_change_status, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.power = S_STANDBY
+
+            # WHEN: process_name called with test data
+            pjlink.process_powr(data='99')
+
+            # THEN: Power should be set to ON
+            assert pjlink.power == S_STANDBY, 'Power should not have changed'
+            assert mock_UpdateIcons.emit.called is False, 'projectorUpdateIcons() should not have been called'
+            mock_change_status.called is False, 'change_status() should not have been called'
+            mock_send_command.called is False, 'send_command() should not have been called'
+            mock_log.warning.assert_has_calls(log_warn_calls)
+
+    def test_projector_process_powr_off(self):
         """
         Test status power to STANDBY
         """
-        # GIVEN: Test object and preset
-        pjlink = pjlink_test
-        pjlink.power = S_ON
-        test_data = PJLINK_POWR_STATUS[S_STANDBY]
-
-        # WHEN: Call process_command with turn power on command
-        pjlink.process_command(cmd='POWR', data=test_data)
-
-        # THEN: Power should be set to STANDBY
-        self.assertEqual(pjlink.power, S_STANDBY, 'Power should have been set to STANDBY')
-        self.assertEqual(mock_UpdateIcons.emit.called, True, 'projectorUpdateIcons should have been called')
-        mock_change_status.assert_called_once_with(PJLINK_POWR_STATUS[test_data])
-        self.assertFalse(mock_send_command.called, "send_command['INST'] should not have been called")
+        # GIVEN: Test object
+        with patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status') as mock_change_status, \
+                patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.power = S_ON
+
+            # WHEN: process_name called with test data
+            pjlink.process_powr(data=PJLINK_POWR_STATUS[S_STANDBY])
+
+            # THEN: Power should be set to ON
+            assert pjlink.power == S_STANDBY, 'Power should have changed to S_STANDBY'
+            assert mock_UpdateIcons.emit.called is True, 'projectorUpdateIcons should have been called'
+            mock_change_status.called is True, 'change_status should have been called'
+            mock_send_command.called is False, 'send_command should not have been called'
 
     def test_projector_process_rfil_save(self):
         """
         Test saving filter type
         """
+        filter_model = 'Filter Type Test'
+
         # GIVEN: Test object
-        pjlink = pjlink_test
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
         pjlink.model_filter = None
-        filter_model = 'Filter Type Test'
 
         # WHEN: Filter model is received
         pjlink.process_rfil(data=filter_model)
 
         # THEN: Filter model number should be saved
-        self.assertEqual(pjlink.model_filter, filter_model, 'Filter type should have been saved')
+        assert pjlink.model_filter == filter_model, 'Filter type should have been saved'
 
     def test_projector_process_rfil_nosave(self):
         """
         Test saving filter type previously saved
         """
+        filter_model = 'Filter Type Test'
+        log_warn_calls = [call('(111.111.111.111) Filter model already set'),
+                          call('(111.111.111.111) Saved model: "Old filter type"'),
+                          call('(111.111.111.111) New model: "Filter Type Test"')]
+
         # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.model_filter = 'Old filter type'
-        filter_model = 'Filter Type Test'
-
-        # WHEN: Filter model is received
-        pjlink.process_rfil(data=filter_model)
-
-        # THEN: Filter model number should be saved
-        self.assertNotEquals(pjlink.model_filter, filter_model, 'Filter type should NOT have been saved')
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.model_filter = 'Old filter type'
+
+            # WHEN: Filter model is received
+            pjlink.process_rfil(data=filter_model)
+
+            # THEN: Filter model number should be saved
+            assert pjlink.model_filter != filter_model, 'Filter type should NOT have been saved'
+            mock_log.warning.assert_has_calls(log_warn_calls)
 
     def test_projector_process_rlmp_save(self):
         """
         Test saving lamp type
         """
         # GIVEN: Test object
-        pjlink = pjlink_test
+        # GIVEN: Test object
+        pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
         pjlink.model_lamp = None
         lamp_model = 'Lamp Type Test'
 
@@ -968,153 +1048,173 @@
         """
         Test saving lamp type previously saved
         """
+        lamp_model = 'Lamp Type Test'
+        log_warn_calls = [call('(111.111.111.111) Lamp model already set'),
+                          call('(111.111.111.111) Saved lamp: "Old lamp type"'),
+                          call('(111.111.111.111) New lamp: "Lamp Type Test"')]
+
         # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.model_lamp = 'Old lamp type'
-        lamp_model = 'Filter Type Test'
-
-        # WHEN: Filter model is received
-        pjlink.process_rlmp(data=lamp_model)
-
-        # THEN: Filter model number should be saved
-        self.assertNotEquals(pjlink.model_lamp, lamp_model, 'Lamp type should NOT have been saved')
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.model_lamp = 'Old lamp type'
+
+            # WHEN: Filter model is received
+            pjlink.process_rlmp(data=lamp_model)
+
+            # THEN: Filter model number should be saved
+            assert pjlink.model_lamp != lamp_model, 'Lamp type should NOT have been saved'
+            mock_log.warning.assert_has_calls(log_warn_calls)
 
     def test_projector_process_snum_set(self):
         """
         Test saving serial number from projector
         """
+        log_debug_calls = [call('(111.111.111.111) Setting projector serial number to "Test Serial Number"')]
+        test_number = 'Test Serial Number'
+
         # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.serial_no = None
-        test_number = 'Test Serial Number'
-
-        # WHEN: No serial number is set and we receive serial number command
-        pjlink.process_snum(data=test_number)
-
-        # THEN: Serial number should be set
-        self.assertEqual(pjlink.serial_no, test_number,
-                         'Projector serial number should have been set')
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.serial_no = None
+
+            # WHEN: No serial number is set and we receive serial number command
+            pjlink.process_snum(data=test_number)
+
+            # THEN: Serial number should be set
+            assert pjlink.serial_no == test_number, 'Projector serial number should have been set'
+            mock_log.debug.assert_has_calls(log_debug_calls)
 
     def test_projector_process_snum_different(self):
         """
         Test projector serial number different than saved serial number
         """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.serial_no = 'Previous serial number'
+        log_warn_calls = [call('(111.111.111.111) Projector serial number does not match saved serial number'),
+                          call('(111.111.111.111) Saved:    "Previous serial number"'),
+                          call('(111.111.111.111) Received: "Test Serial Number"'),
+                          call('(111.111.111.111) NOT saving serial number')]
         test_number = 'Test Serial Number'
 
-        # WHEN: No serial number is set and we receive serial number command
-        pjlink.process_snum(data=test_number)
-
-        # THEN: Serial number should be set
-        self.assertNotEquals(pjlink.serial_no, test_number,
-                             'Projector serial number should NOT have been set')
-
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_process_sver(self, mock_log):
+        # GIVEN: Test object
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.serial_no = 'Previous serial number'
+
+            # WHEN: No serial number is set and we receive serial number command
+            pjlink.process_snum(data=test_number)
+
+            # THEN: Serial number should be set
+            assert pjlink.serial_no != test_number, 'Projector serial number should NOT have been set'
+            mock_log.warning.assert_has_calls(log_warn_calls)
+
+    def test_projector_process_sver(self):
         """
         Test invalid software version information - too long
         """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.sw_version = None
-        pjlink.sw_version_received = None
         test_data = 'Test 1 Subtest 1'
-        test_log = '(127.0.0.1) Setting projector software version to "Test 1 Subtest 1"'
-        mock_log.reset_mock()
-
-        # WHEN: process_sver called with invalid data
-        pjlink.process_sver(data=test_data)
-
-        # THEN: Version information should not change
-        self.assertEqual(pjlink.sw_version, test_data, 'Software version should have been updated')
-        self.assertIsNone(pjlink.sw_version_received, 'Received software version should not have changed')
-        mock_log.debug.assert_called_once_with(test_log)
-
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_process_sver_changed(self, mock_log):
+        log_debug_calls = [call('(111.111.111.111) Setting projector software version to "Test 1 Subtest 1"')]
+
+        # GIVEN: Test object
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.sw_version = None
+            pjlink.sw_version_received = None
+
+            # WHEN: process_sver called with invalid data
+            pjlink.process_sver(data=test_data)
+
+            # THEN: Version information should not change
+            assert pjlink.sw_version == test_data, 'Software version should have been updated'
+            mock_log.debug.assert_has_calls(log_debug_calls)
+
+    def test_projector_process_sver_changed(self):
         """
         Test invalid software version information - Received different than saved
         """
-        # GIVEN: Test object
-        pjlink = pjlink_test
+        test_data_old = 'Test 1 Subtest 1'
         test_data_new = 'Test 1 Subtest 2'
-        test_data_old = 'Test 1 Subtest 1'
-        pjlink.sw_version = test_data_old
-        pjlink.sw_version_received = None
-        test_log = '(127.0.0.1) Saving new serial number as sw_version_received'
-        mock_log.reset_mock()
-
-        # WHEN: process_sver called with invalid data
-        pjlink.process_sver(data=test_data_new)
-
-        # THEN: Version information should not change
-        self.assertEqual(pjlink.sw_version, test_data_old, 'Software version should not have been updated')
-        self.assertEqual(pjlink.sw_version_received, test_data_new,
-                         'Received software version should have been changed')
-        self.assertEqual(mock_log.warning.call_count, 4, 'log.warn should have been called 4 times')
-        # There was 4 calls, but only the last one is checked with this method
-        mock_log.warning.assert_called_with(test_log)
-
-    @patch.object(openlp.core.projectors.pjlink, 'log')
-    def test_projector_process_sver_invalid(self, mock_log):
+        log_warn_calls = [call('(111.111.111.111) Projector software version does not match saved software version'),
+                          call('(111.111.111.111) Saved:    "Test 1 Subtest 1"'),
+                          call('(111.111.111.111) Received: "Test 1 Subtest 2"'),
+                          call('(111.111.111.111) Updating software version')]
+
+        # GIVEN: Test object
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.sw_version = test_data_old
+
+            # WHEN: process_sver called with invalid data
+            pjlink.process_sver(data=test_data_new)
+
+            # THEN: Version information should not change
+            assert pjlink.sw_version == test_data_new, 'Software version should have changed'
+            mock_log.warning.assert_has_calls(log_warn_calls)
+
+    def test_projector_process_sver_invalid(self):
         """
         Test invalid software version information - too long
         """
-        # GIVEN: Test object
-        pjlink = pjlink_test
-        pjlink.sw_version = None
-        pjlink.sw_version_received = None
         test_data = 'This is a test software version line that is too long based on PJLink version 2 specs'
-        test_log = "Invalid software version - too long"
-        mock_log.reset_mock()
-
-        # WHEN: process_sver called with invalid data
-        pjlink.process_sver(data=test_data)
-
-        # THEN: Version information should not change
-        self.assertIsNone(pjlink.sw_version, 'Software version should not have changed')
-        self.assertIsNone(pjlink.sw_version_received, 'Received software version should not have changed')
-        mock_log.warning.assert_called_once_with(test_log)
+        log_warn_calls = [call('Invalid software version - too long')]
+
+        # GIVEN: Test object
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            pjlink.sw_version = None
+
+            # WHEN: process_sver called with invalid data
+            pjlink.process_sver(data=test_data)
+
+            # THEN: Version information should not change
+            assert pjlink.sw_version is None, 'Software version should not have changed'
+            assert pjlink.sw_version_received is None, 'Received software version should not have changed'
+            mock_log.warning.assert_has_calls(log_warn_calls)
 
     def test_projector_reset_information(self):
         """
         Test reset_information() resets all information and stops timers
         """
-        # GIVEN: Test object and test data
-        pjlink = pjlink_test
-        pjlink.power = S_ON
-        pjlink.pjlink_name = 'OPENLPTEST'
-        pjlink.manufacturer = 'PJLINK'
-        pjlink.model = '1'
-        pjlink.shutter = True
-        pjlink.mute = True
-        pjlink.lamp = True
-        pjlink.fan = True
-        pjlink.source_available = True
-        pjlink.other_info = 'ANOTHER TEST'
-        pjlink.send_queue = True
-        pjlink.send_busy = True
-
-        # WHEN: reset_information() is called
-        with patch.object(pjlink, 'timer') as mock_timer:
-            with patch.object(pjlink, 'socket_timer') as mock_socket_timer:
+        log_debug_calls = [call('(111.111.111.111): Calling timer.stop()'),
+                           call('(111.111.111.111): Calling socket_timer.stop()')]
+
+        # GIVEN: Test object
+        with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
+            pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
+            # timer and socket_timer not available until instantiation, so mock here
+            with patch.object(pjlink, 'socket_timer') as mock_socket_timer, \
+                    patch.object(pjlink, 'timer') as mock_timer:
+
+                pjlink.power = S_ON
+                pjlink.pjlink_name = 'OPENLPTEST'
+                pjlink.manufacturer = 'PJLINK'
+                pjlink.model = '1'
+                pjlink.shutter = True
+                pjlink.mute = True
+                pjlink.lamp = True
+                pjlink.fan = True
+                pjlink.source_available = True
+                pjlink.other_info = 'ANOTHER TEST'
+                pjlink.send_queue = True
+                pjlink.send_busy = True
+
+                # WHEN: reset_information() is called
                 pjlink.reset_information()
 
-        # THEN: All information should be reset and timers stopped
-        self.assertEqual(pjlink.power, S_OFF, 'Projector power should be OFF')
-        self.assertIsNone(pjlink.pjlink_name, 'Projector pjlink_name should be None')
-        self.assertIsNone(pjlink.manufacturer, 'Projector manufacturer should be None')
-        self.assertIsNone(pjlink.model, 'Projector model should be None')
-        self.assertIsNone(pjlink.shutter, 'Projector shutter should be None')
-        self.assertIsNone(pjlink.mute, 'Projector shuttter should be None')
-        self.assertIsNone(pjlink.lamp, 'Projector lamp should be None')
-        self.assertIsNone(pjlink.fan, 'Projector fan should be None')
-        self.assertIsNone(pjlink.source_available, 'Projector source_available should be None')
-        self.assertIsNone(pjlink.source, 'Projector source should be None')
-        self.assertIsNone(pjlink.other_info, 'Projector other_info should be None')
-        self.assertEqual(pjlink.send_queue, [], 'Projector send_queue should be an empty list')
-        self.assertFalse(pjlink.send_busy, 'Projector send_busy should be False')
-        self.assertTrue(mock_timer.stop.called, 'Projector timer.stop()  should have been called')
-        self.assertTrue(mock_socket_timer.stop.called, 'Projector socket_timer.stop() should have been called')
+                # THEN: All information should be reset and timers stopped
+                assert pjlink.power == S_OFF, 'Projector power should be OFF'
+                assert pjlink.pjlink_name is None, 'Projector pjlink_name should be None'
+                assert pjlink.manufacturer is None, 'Projector manufacturer should be None'
+                assert pjlink.model is None, 'Projector model should be None'
+                assert pjlink.shutter is None, 'Projector shutter should be None'
+                assert pjlink.mute is None, 'Projector shuttter should be None'
+                assert pjlink.lamp is None, 'Projector lamp should be None'
+                assert pjlink.fan is None, 'Projector fan should be None'
+                assert pjlink.source_available is None, 'Projector source_available should be None'
+                assert pjlink.source is None, 'Projector source should be None'
+                assert pjlink.other_info is None, 'Projector other_info should be None'
+                assert pjlink.send_queue == [], 'Projector send_queue should be an empty list'
+                assert pjlink.send_busy is False, 'Projector send_busy should be False'
+                assert mock_timer.stop.called is True, 'Projector timer.stop()  should have been called'
+                assert mock_socket_timer.stop.called is True, 'Projector socket_timer.stop() should have been called'
+                mock_log.debug.assert_has_calls(log_debug_calls)


Follow ups