← Back to team overview

openlp-core team mailing list archive

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

 

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

Commit message:
PJLink class 2 update F

Requested reviews:
  Tim Bentley (trb143)
  Tomas Groth (tomasgroth)
  Raoul Snyman (raoul-snyman)

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

-- Added '# noqa' for flake8 to ignore specific line errors
-- Renamed test_projector_clssto clss_one
-- Added test_projector_clss_two
-- Updated  PJLink non-standard class check
-- Added process_snum
-- Added tests for process_snum
-- Renamed tests/functional/openlp_core_lib/test_projectordb.py to test_projector_db.py
-- Added filter model command-- Added lamp model command
-- Added lamp model command
-- Added tests for filter/model commands
-- Fix typo in projector status window
-- Cleanups from commit notes
-- Added invalid data buffer cleanups

--------------------------------
lp:~alisonken1/openlp/pjlink2-f (revision 2754)
[SUCCESS] https://ci.openlp.io/job/Branch-01-Pull/2102/
[SUCCESS] https://ci.openlp.io/job/Branch-02-Functional-Tests/2012/
[SUCCESS] https://ci.openlp.io/job/Branch-03-Interface-Tests/1924/
[SUCCESS] https://ci.openlp.io/job/Branch-04a-Code_Analysis/1301/
[SUCCESS] https://ci.openlp.io/job/Branch-04b-Test_Coverage/1146/
[SUCCESS] https://ci.openlp.io/job/Branch-04c-Code_Analysis2/275/
[SUCCESS] https://ci.openlp.io/job/Branch-05-AppVeyor-Tests/120/

-- 
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/lib/projector/db.py'
--- openlp/core/lib/projector/db.py	2017-06-01 22:35:57 +0000
+++ openlp/core/lib/projector/db.py	2017-07-07 23:49:26 +0000
@@ -303,7 +303,7 @@
         :param ip: Host IP/Name
         :returns: Projector() instance
         """
-        log.debug('get_projector_by_ip(ip="%s")' % ip)
+        log.debug('get_projector_by_ip(ip="{ip}")'.format(ip=ip))
         projector = self.get_object_filtered(Projector, Projector.ip == ip)
         if projector is None:
             # Not found

=== modified file 'openlp/core/lib/projector/pjlink1.py'
--- openlp/core/lib/projector/pjlink1.py	2017-06-17 00:25:06 +0000
+++ openlp/core/lib/projector/pjlink1.py	2017-07-07 23:49:26 +0000
@@ -44,6 +44,7 @@
 
 __all__ = ['PJLink']
 
+import re
 from codecs import decode
 
 from PyQt5 import QtCore, QtNetwork
@@ -53,7 +54,7 @@
     E_AUTHENTICATION, E_CONNECTION_REFUSED, E_GENERAL, E_INVALID_DATA, E_NETWORK, E_NOT_CONNECTED, \
     E_PARAMETER, E_PROJECTOR, E_SOCKET_TIMEOUT, E_UNAVAILABLE, E_UNDEFINED, PJLINK_ERRORS, \
     PJLINK_ERST_STATUS, PJLINK_MAX_PACKET, PJLINK_PORT, PJLINK_POWR_STATUS, PJLINK_VALID_CMD, \
-    PJLINK_DEFAULT_CODES, STATUS_STRING, S_CONNECTED, S_CONNECTING, S_NETWORK_RECEIVED, S_NETWORK_SENDING, \
+    STATUS_STRING, S_CONNECTED, S_CONNECTING, S_NETWORK_RECEIVED, S_NETWORK_SENDING, \
     S_NOT_CONNECTED, S_OFF, S_OK, S_ON, S_STATUS
 
 # Shortcuts
@@ -113,8 +114,13 @@
         self.port = port
         self.pin = pin
         super().__init__()
+        self.model_lamp = None
+        self.model_filter = None
         self.mac_adx = kwargs.get('mac_adx')
+        self.serial_no = None
+        self.serial_no_received = None  # Used only if saved serial number is different than received serial number
         self.dbid = None
+        self.db_update = False  # Use to check if db needs to be updated prior to exiting
         self.location = None
         self.notes = None
         self.dbid = kwargs.get('dbid')
@@ -158,7 +164,9 @@
             'LAMP': self.process_lamp,
             'NAME': self.process_name,
             'PJLINK': self.check_login,
-            'POWR': self.process_powr
+            'POWR': self.process_powr,
+            'SNUM': self.process_snum,
+            'SVER': self.process_sver
         }
 
     def reset_information(self):
@@ -166,12 +174,16 @@
         Reset projector-specific information to default
         """
         log.debug('({ip}) reset_information() connect status is {state}'.format(ip=self.ip, state=self.state()))
+        self.send_queue = []
         self.power = S_OFF
         self.pjlink_name = None
         self.manufacturer = None
         self.model = None
         self.serial_no = None
+        self.serial_no_received = None
         self.sw_version = None
+        self.sw_version_received = None
+        self.mac_adx = None
         self.shutter = None
         self.mute = None
         self.lamp = None
@@ -188,7 +200,6 @@
         if hasattr(self, 'socket_timer'):
             log.debug('({ip}): Calling socket_timer.stop()'.format(ip=self.ip))
             self.socket_timer.stop()
-        self.send_queue = []
         self.send_busy = False
 
     def thread_started(self):
@@ -240,6 +251,7 @@
         Normally called by timer().
         """
         if self.state() != self.ConnectedState:
+            log.warn("({ip}) poll_loop(): Not connected - returning".format(ip=self.ip))
             return
         log.debug('({ip}) Updating projector status'.format(ip=self.ip))
         # Reset timer in case we were called from a set command
@@ -248,8 +260,11 @@
             self.timer.setInterval(self.poll_time)
         # Restart timer
         self.timer.start()
-        # These commands may change during connetion
-        for command in ['POWR', 'ERST', 'LAMP', 'AVMT', 'INPT']:
+        # 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, queue=True)
         # The following commands do not change, so only check them once
         if self.power == S_ON and self.source_available is None:
@@ -262,6 +277,38 @@
             self.send_command('INF2', queue=True)
         if self.pjlink_name is None:
             self.send_command('NAME', queue=True)
+        if self.pjlink_class == '2':
+            # Class 2 specific checks
+            if self.serial_no is None:
+                self.send_command('SNUM', queue=True)
+            if self.sw_version is None:
+                self.send_command('SVER', queue=True)
+            if self.model_filter is None:
+                self.send_command('RFIL', queue=True)
+            if self.model_lamp is None:
+                self.send_command('RLMP', queue=True)
+
+    def process_rfil(self, data):
+        """
+        Process replacement filter type
+        """
+        if self.model_filter is None:
+            self.model_filter = data
+        else:
+            log.warn("({ip}) Filter model already set".format(ip=self.ip))
+            log.warn("({ip}) Saved model: '{old}'".format(ip=self.ip, old=self.model_filter))
+            log.warn("({ip}) New model: '{new}'".format(ip=self.ip, new=data))
+
+    def process_rlmp(self, data):
+        """
+        Process replacement lamp type
+        """
+        if self.model_lamp is None:
+            self.model_lamp = data
+        else:
+            log.warn("({ip}) Lamp model already set".format(ip=self.ip))
+            log.warn("({ip}) Saved lamp: '{old}'".format(ip=self.ip, old=self.model_lamp))
+            log.warn("({ip}) New lamp: '{new}'".format(ip=self.ip, new=data))
 
     def _get_status(self, status):
         """
@@ -331,7 +378,8 @@
                 self.change_status(E_SOCKET_TIMEOUT)
                 return
             read = self.readLine(self.max_size)
-            dontcare = self.readLine(self.max_size)  # Clean out the trailing \r\n
+            # _ = self.readLine(self.max_size)  # Clean out the trailing \r\n
+            self.readLine(self.max_size)  # Clean out the trailing \r\n
             if read is None:
                 log.warning('({ip}) read is None - socket error?'.format(ip=self.ip))
                 return
@@ -341,7 +389,8 @@
             data = decode(read, 'utf-8')
             # Possibility of extraneous data on input when reading.
             # Clean out extraneous characters in buffer.
-            dontcare = self.readLine(self.max_size)
+            # _ = self.readLine(self.max_size)
+            self.readLine(self.max_size)
             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
@@ -363,24 +412,24 @@
             # Authentication error
             self.disconnect_from_host()
             self.change_status(E_AUTHENTICATION)
-            log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.name))
+            log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.ip))
             return
         elif data_check[1] == '0' and self.pin is not None:
             # Pin set and no authentication needed
             log.warning('({ip}) Regular connection but PIN set'.format(ip=self.name))
             self.disconnect_from_host()
             self.change_status(E_AUTHENTICATION)
-            log.debug('({ip}) Emitting projectorNoAuthentication() signal'.format(ip=self.name))
+            log.debug('({ip}) Emitting projectorNoAuthentication() signal'.format(ip=self.ip))
             self.projectorNoAuthentication.emit(self.name)
             return
         elif data_check[1] == '1':
             # Authenticated login with salt
             if self.pin is None:
-                log.warning('({ip}) Authenticated connection but no pin set'.format(ip=self.name))
+                log.warning('({ip}) Authenticated connection but no pin set'.format(ip=self.ip))
                 self.disconnect_from_host()
                 self.change_status(E_AUTHENTICATION)
-                log.debug('({ip}) Emitting projectorAuthentication() signal'.format(ip=self.name))
-                self.projectorAuthentication.emit(self.name)
+                log.debug('({ip}) Emitting projectorAuthentication() signal'.format(ip=self.ip))
+                self.projectorAuthentication.emit(self.ip)
                 return
             else:
                 log.debug('({ip}) Setting hash with salt="{data}"'.format(ip=self.ip, data=data_check[2]))
@@ -400,6 +449,20 @@
             self.timer.setInterval(2000)  # Set 2 seconds for initial information
             self.timer.start()
 
+    def _trash_buffer(self, msg=None):
+        """
+        Clean out extraneous stuff in the buffer.
+        """
+        log.warning("({ip}) {message}".format(ip=self.ip, message='Invalid packet' if msg is None else msg))
+        self.send_busy = False
+        trash_count = 0
+        while self.bytesAvailable() > 0:
+            trash = self.read(self.max_size)
+            trash_count += len(trash)
+        log.debug("({ip}) Finished cleaning buffer - {count} bytes dropped".format(ip=self.ip,
+                                                                                   count=trash_count))
+        return
+
     @QtCore.pyqtSlot()
     def get_data(self):
         """
@@ -414,43 +477,27 @@
         if read == -1:
             # No data available
             log.debug('({ip}) get_data(): No data available (-1)'.format(ip=self.ip))
-            self.send_busy = False
-            self.projectorReceivedData.emit()
-            return
+            return self.receive_data_signal()
         self.socket_timer.stop()
         self.projectorNetwork.emit(S_NETWORK_RECEIVED)
         # NOTE: Class2 has changed to some values being UTF-8
         data_in = decode(read, 'utf-8')
         data = data_in.strip()
-        if len(data) < 7:
-            # Not enough data for a packet
-            log.debug('({ip}) get_data(): Packet length < 7: "{data}"'.format(ip=self.ip, data=data))
-            self.receive_data_signal()
-            return
+        if (len(data) < 7) or (not data.startswith(PJLINK_PREFIX)):
+            return self._trash_buffer(msg='get_data(): Invalid packet - length or prefix')
         elif '=' not in data:
-            log.warning('({ip}) get_data(): Invalid packet received'.format(ip=self.ip))
-            self.receive_data_signal()
-            return
+            return self._trash_buffer(msg='get_data(): Invalid packet does not have equal')
         log.debug('({ip}) get_data(): Checking new data "{data}"'.format(ip=self.ip, data=data))
-        # At this point, we should have something to work with
-        if data.upper().startswith('PJLINK'):
-            # Reconnected from remote host disconnect ?
-            self.check_login(data)
-            self.receive_data_signal()
-            return
-        data_split = data.split('=')
+        header, data = data.split('=')
         try:
-            (prefix, version, cmd, data) = (data_split[0][0], data_split[0][1], data_split[0][2:], data_split[1])
+            version, cmd = header[1], header[2:]
         except ValueError as e:
-            log.warning('({ip}) get_data(): Invalid packet - expected header + command + data'.format(ip=self.ip))
+            self.change_status(E_INVALID_DATA)
             log.warning('({ip}) get_data(): Received data: "{data}"'.format(ip=self.ip, data=data_in.strip()))
-            self.change_status(E_INVALID_DATA)
-            self.receive_data_signal()
-            return
+            return self._trash_buffer('get_data(): Expected header + command + data')
         if cmd not in PJLINK_VALID_CMD:
             log.warning('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd))
-            self.receive_data_signal()
-            return
+            return self._trash_buffer(msg='get_data(): Unknown command "{data}"'.format(data=cmd))
         if int(self.pjlink_class) < int(version):
             log.warn('({ip}) get_data(): Projector returned class reply higher '
                      'than projector stated class'.format(ip=self.ip))
@@ -506,7 +553,7 @@
                                                                                                salt='' if salt is None
                                                                                                else ' with hash'))
         cmd_ver = PJLINK_VALID_CMD[cmd]['version']
-        if self.pjlink_class in cmd_ver:
+        if self.pjlink_class in PJLINK_VALID_CMD[cmd]['version']:
             header = PJLINK_HEADER.format(linkclass=self.pjlink_class)
         elif len(cmd_ver) == 1 and (int(cmd_ver[0]) < int(self.pjlink_class)):
             # Typically a class 1 only command
@@ -575,6 +622,7 @@
         self.waitForBytesWritten(2000)  # 2 seconds should be enough
         if sent == -1:
             # Network error?
+            log.warning("({ip}) _send_command(): -1 received".format(ip=self.ip))
             self.change_status(E_NETWORK,
                                translate('OpenLP.PJLink', 'Error while sending data to projector'))
 
@@ -617,20 +665,17 @@
             elif data.upper() == 'ERR4':
                 # Projector/display error
                 self.change_status(E_PROJECTOR)
-            self.send_busy = False
-            self.projectorReceivedData.emit()
+            self.receive_data_signal()
             return
         # Command succeeded - no extra information
         elif data.upper() == 'OK':
             log.debug('({ip}) Command returned OK'.format(ip=self.ip))
-            # A command returned successfully, recheck data
-            self.send_busy = False
-            self.projectorReceivedData.emit()
+            # A command returned successfully
+            self.receive_data_signal()
             return
         # Command checks already passed
         log.debug('({ip}) Calling function for {cmd}'.format(ip=self.ip, cmd=cmd))
-        self.send_busy = False
-        self.projectorReceivedData.emit()
+        self.receive_data_signal()
         self.pjlink_functions[cmd](data)
 
     def process_lamp(self, data):
@@ -729,11 +774,22 @@
         :param data: Class that projector supports.
         """
         # bug 1550891: Projector returns non-standard class response:
-        #            : Expected: %1CLSS=1
-        #            : Received: %1CLSS=Class 1
+        #            : Expected: '%1CLSS=1'
+        #            : Received: '%1CLSS=Class 1'  (Optoma)
+        #            : Received: '%1CLSS=Version1'  (BenQ)
         if len(data) > 1:
-            # Split non-standard information from response
-            clss = data.split()[-1]
+            log.warn("({ip}) Non-standard CLSS reply: '{data}'".format(ip=self.ip, data=data))
+            # Due to stupid projectors not following standards (Optoma, BenQ comes to mind),
+            # AND the different responses that can be received, the semi-permanent way to
+            # fix the class reply is to just remove all non-digit characters.
+            try:
+                clss = re.findall('\d', data)[0]  # Should only be the first match
+            except IndexError:
+                log.error("({ip}) No numbers found in class version reply - defaulting to class '1'".format(ip=self.ip))
+                clss = '1'
+        elif not data.isdigit():
+            log.error("({ip}) NAN class version reply - defaulting to class '1'".format(ip=self.ip))
+            clss = '1'
         else:
             clss = data
         self.pjlink_class = clss
@@ -845,6 +901,42 @@
                     PJLINK_ERST_STATUS[data[5]]
         return
 
+    def process_snum(self, data):
+        """
+        Serial number of projector.
+
+        :param data: Serial number from projector.
+        """
+        if self.serial_no is None:
+            log.debug("({ip}) Setting projector serial number to '{data}'".format(ip=self.ip, data=data))
+            self.serial_no = data
+            self.db_update = False
+        else:
+            # Compare serial numbers and see if we got the same projector
+            if self.serial_no != data:
+                log.warn("({ip}) Projector serial number does not match saved serial number".format(ip=self.ip))
+                log.warn("({ip}) Saved:    '{old}'".format(ip=self.ip, old=self.serial_no))
+                log.warn("({ip}) Received: '{new}'".format(ip=self.ip, new=data))
+                log.warn("({ip}) NOT saving serial number".format(ip=self.ip))
+                self.serial_no_received = data
+
+    def process_sver(self, data):
+        """
+        Software version of projector
+        """
+        if 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:
+                log.warn("({ip}) Projector software version does not match saved software version".format(ip=self.ip))
+                log.warn("({ip}) Saved:    '{old}'".format(ip=self.ip, old=self.sw_version))
+                log.warn("({ip}) Received: '{new}'".format(ip=self.ip, new=data))
+                log.warn("({ip}) NOT saving serial number".format(ip=self.ip))
+                self.sw_version_received = data
+
     def connect_to_host(self):
         """
         Initiate connection to projector.

=== modified file 'openlp/core/lib/projector/pjlink2.py'
--- openlp/core/lib/projector/pjlink2.py	2017-06-09 12:12:39 +0000
+++ openlp/core/lib/projector/pjlink2.py	2017-07-07 23:49:26 +0000
@@ -71,10 +71,10 @@
 
 log.debug('pjlink2 loaded')
 
-from PyQt5 import QtCore, QtNetwork
-
-
-class PJLinkUDP(QtNetwork.QTcpSocket):
+from PyQt5 import QtNetwork
+
+
+class PJLinkUDP(QtNetwork.QUdpSocket):
     """
     Socket service for handling datagram (UDP) sockets.
     """

=== modified file 'openlp/core/lib/projector/upgrade.py'
--- openlp/core/lib/projector/upgrade.py	2017-06-09 12:12:39 +0000
+++ openlp/core/lib/projector/upgrade.py	2017-07-07 23:49:26 +0000
@@ -25,12 +25,9 @@
 """
 import logging
 
-# Not all imports used at this time, but keep for future upgrades
-from sqlalchemy import Table, Column, types, inspect
-from sqlalchemy.exc import NoSuchTableError
+from sqlalchemy import Table, Column, types
 from sqlalchemy.sql.expression import null
 
-from openlp.core.common.db import drop_columns
 from openlp.core.lib.db import get_upgrade_op
 
 log = logging.getLogger(__name__)

=== modified file 'openlp/core/ui/projector/manager.py'
--- openlp/core/ui/projector/manager.py	2017-06-10 05:57:00 +0000
+++ openlp/core/ui/projector/manager.py	2017-07-07 23:49:26 +0000
@@ -420,9 +420,7 @@
         :param opt: Needed by PyQt5
         """
         try:
-            ip = opt.link.ip
-            projector = opt
-            projector.link.set_shutter_closed()
+            opt.link.set_shutter_closed()
         except AttributeError:
             for list_item in self.projector_list_widget.selectedItems():
                 if list_item is None:
@@ -455,9 +453,7 @@
         :param opt: Needed by PyQt5
         """
         try:
-            ip = opt.link.ip
-            projector = opt
-            projector.link.connect_to_host()
+            opt.link.connect_to_host()
         except AttributeError:
             for list_item in self.projector_list_widget.selectedItems():
                 if list_item is None:
@@ -527,7 +523,8 @@
         self.projector_list = new_list
         list_item = self.projector_list_widget.takeItem(self.projector_list_widget.currentRow())
         list_item = None
-        deleted = self.projectordb.delete_projector(projector.db_item)
+        if not self.projectordb.delete_projector(projector.db_item):
+            log.warning('Delete projector {item} failed'.format(item=projector.db_item))
         for item in self.projector_list:
             log.debug('New projector list - item: {ip} {name}'.format(ip=item.link.ip, name=item.link.name))
 
@@ -538,9 +535,7 @@
         :param opt: Needed by PyQt5
         """
         try:
-            ip = opt.link.ip
-            projector = opt
-            projector.link.disconnect_from_host()
+            opt.link.disconnect_from_host()
         except AttributeError:
             for list_item in self.projector_list_widget.selectedItems():
                 if list_item is None:
@@ -573,9 +568,7 @@
         :param opt: Needed by PyQt5
         """
         try:
-            ip = opt.link.ip
-            projector = opt
-            projector.link.set_power_off()
+            opt.link.set_power_off()
         except AttributeError:
             for list_item in self.projector_list_widget.selectedItems():
                 if list_item is None:
@@ -593,9 +586,7 @@
         :param opt: Needed by PyQt5
         """
         try:
-            ip = opt.link.ip
-            projector = opt
-            projector.link.set_power_on()
+            opt.link.set_power_on()
         except AttributeError:
             for list_item in self.projector_list_widget.selectedItems():
                 if list_item is None:
@@ -613,9 +604,7 @@
         :param opt: Needed by PyQt5
         """
         try:
-            ip = opt.link.ip
-            projector = opt
-            projector.link.set_shutter_open()
+            opt.link.set_shutter_open()
         except AttributeError:
             for list_item in self.projector_list_widget.selectedItems():
                 if list_item is None:
@@ -662,9 +651,10 @@
                                                              data=translate('OpenLP.ProjectorManager', 'Closed')
                                                              if projector.link.shutter
                                                              else translate('OpenLP', 'Open'))
-            message = '%s<b>%s</b>: %s<br />' % (message,
-                                                 translate('OpenLP.ProjectorManager', 'Current source input is'),
-                                                 projector.link.source)
+            message = '{msg}<b>{source}</b>: {selected}<br />'.format(msg=message,
+                                                                      source=translate('OpenLP.ProjectorManager',
+                                                                                       'Current source input is'),
+                                                                      selected=projector.link.source)
             if projector.link.pjlink_class == '2':
                 # Information only available for PJLink Class 2 projectors
                 message += '<b>{title}</b>: {data}<br /><br />'.format(title=translate('OpenLP.ProjectorManager',
@@ -685,10 +675,10 @@
                                                                                      'Lamp'),
                                                                      count=count,
                                                                      status=translate('OpenLP.ProjectorManager',
-                                                                                      ' is on')
+                                                                                      'ON')
                                                                      if item['On']
                                                                      else translate('OpenLP.ProjectorManager',
-                                                                                    'is off'))
+                                                                                    'OFF'))
 
                 message += '<b>{title}</b>: {hours}<br />'.format(title=translate('OpenLP.ProjectorManager', 'Hours'),
                                                                   hours=item['Hours'])

=== modified file 'openlp/core/ui/projector/sourceselectform.py'
--- openlp/core/ui/projector/sourceselectform.py	2017-06-04 12:14:23 +0000
+++ openlp/core/ui/projector/sourceselectform.py	2017-07-07 23:49:26 +0000
@@ -393,9 +393,9 @@
                                                  QtCore.Qt.WindowCloseButtonHint)
         self.edit = edit
         if self.edit:
-            title = translate('OpenLP.SourceSelectForm', 'Edit Projector Source Text')
+            self.title = translate('OpenLP.SourceSelectForm', 'Edit Projector Source Text')
         else:
-            title = translate('OpenLP.SourceSelectForm', 'Select Projector Source')
+            self.title = translate('OpenLP.SourceSelectForm', 'Select Projector Source')
         self.setObjectName('source_select_single')
         self.setWindowIcon(build_icon(':/icon/openlp-log.svg'))
         self.setModal(True)

=== modified file 'setup.cfg'
--- setup.cfg	2017-02-26 21:14:49 +0000
+++ setup.cfg	2017-07-07 23:49:26 +0000
@@ -1,4 +1,14 @@
+# E402 module level import not at top of file
+# E722 do not use bare except, specify exception instead
+# F841 local variable '<variable>' is assigned to but never used
+
 [pep8]
 exclude=resources.py,vlc.py
 max-line-length = 120
-ignore = E402,E722
+ignore = E402
+
+[flake8]
+exclude=resources.py,vlc.py
+max-line-length = 120
+ignore = E402
+

=== renamed file 'tests/functional/openlp_core_lib/test_projectordb.py' => 'tests/functional/openlp_core_lib/test_projector_db.py'
=== modified file 'tests/functional/openlp_core_lib/test_projector_pjlink1.py'
--- tests/functional/openlp_core_lib/test_projector_pjlink1.py	2017-05-27 18:21:24 +0000
+++ tests/functional/openlp_core_lib/test_projector_pjlink1.py	2017-07-07 23:49:26 +0000
@@ -59,9 +59,101 @@
         self.assertTrue(mock_qmd5_hash.called_with(TEST_PIN,
                                                    "Connection request should have been called with TEST_PIN"))
 
-    def test_projector_class(self):
-        """
-        Test class version from projector
+    def test_projector_process_rfil_save(self):
+        """
+        Test saving filter type
+        """
+        # GIVEN: Test object
+        pjlink = pjlink_test
+        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')
+
+    def test_projector_process_rfil_nosave(self):
+        """
+        Test saving filter type previously saved
+        """
+        # 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')
+
+    def test_projector_process_rlmp_save(self):
+        """
+        Test saving lamp type
+        """
+        # GIVEN: Test object
+        pjlink = pjlink_test
+        pjlink.model_lamp = None
+        lamp_model = 'Lamp Type Test'
+
+        # WHEN: Filter model is received
+        pjlink.process_rlmp(data=lamp_model)
+
+        # THEN: Filter model number should be saved
+        self.assertEqual(pjlink.model_lamp, lamp_model, 'Lamp type should have been saved')
+
+    def test_projector_process_rlmp_nosave(self):
+        """
+        Test saving lamp type previously saved
+        """
+        # 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')
+
+    def test_projector_process_snum_set(self):
+        """
+        Test saving serial number from projector
+        """
+        # 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')
+
+    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'
+        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')
+
+    def test_projector_clss_one(self):
+        """
+        Test class 1 sent from projector
         """
         # GIVEN: Test object
         pjlink = pjlink_test
@@ -70,12 +162,26 @@
         pjlink.process_clss('1')
 
         # THEN: Projector class should be set to 1
-        self.assertEquals(pjlink.pjlink_class, '1',
-                          'Projector should have returned class=1')
-
-    def test_non_standard_class_reply(self):
-        """
-        Bugfix 1550891: CLSS request returns non-standard 'Class N' reply
+        self.assertEqual(pjlink.pjlink_class, '1',
+                         'Projector should have returned class=1')
+
+    def test_projector_clss_two(self):
+        """
+        Test class 2 sent from projector
+        """
+        # GIVEN: Test object
+        pjlink = pjlink_test
+
+        # 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 returned class=2')
+
+    def test_bug_1550891_non_standard_class_reply(self):
+        """
+        Bugfix 1550891: CLSS request returns non-standard reply
         """
         # GIVEN: Test object
         pjlink = pjlink_test
@@ -84,8 +190,8 @@
         pjlink.process_clss('Class 1')
 
         # THEN: Projector class should be set with proper value
-        self.assertEquals(pjlink.pjlink_class, '1',
-                          'Non-standard class reply should have set proper class')
+        self.assertEqual(pjlink.pjlink_class, '1',
+                         'Non-standard class reply should have set class=1')
 
     @patch.object(pjlink_test, 'change_status')
     def test_status_change(self, mock_change_status):
@@ -131,10 +237,10 @@
         pjlink.process_command('LAMP', '22222 1')
 
         # THEN: Lamp should have been set with status=ON and hours=22222
-        self.assertEquals(pjlink.lamp[0]['On'], True,
-                          'Lamp power status should have been set to TRUE')
-        self.assertEquals(pjlink.lamp[0]['Hours'], 22222,
-                          'Lamp hours should have been set to 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')
 
     @patch.object(pjlink_test, 'projectorReceivedData')
     def test_projector_process_multiple_lamp(self, mock_projectorReceivedData):
@@ -148,20 +254,20 @@
         pjlink.process_command('LAMP', '11111 1 22222 0 33333 1')
 
         # THEN: Lamp should have been set with proper lamp status
-        self.assertEquals(len(pjlink.lamp), 3,
-                          'Projector should have 3 lamps specified')
-        self.assertEquals(pjlink.lamp[0]['On'], True,
-                          'Lamp 1 power status should have been set to TRUE')
-        self.assertEquals(pjlink.lamp[0]['Hours'], 11111,
-                          'Lamp 1 hours should have been set to 11111')
-        self.assertEquals(pjlink.lamp[1]['On'], False,
-                          'Lamp 2 power status should have been set to FALSE')
-        self.assertEquals(pjlink.lamp[1]['Hours'], 22222,
-                          'Lamp 2 hours should have been set to 22222')
-        self.assertEquals(pjlink.lamp[2]['On'], True,
-                          'Lamp 3 power status should have been set to TRUE')
-        self.assertEquals(pjlink.lamp[2]['Hours'], 33333,
-                          'Lamp 3 hours should have been set to 33333')
+        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')
 
     @patch.object(pjlink_test, 'projectorReceivedData')
     @patch.object(pjlink_test, 'projectorUpdateIcons')
@@ -182,9 +288,9 @@
         pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_ON])
 
         # THEN: Power should be set to ON
-        self.assertEquals(pjlink.power, S_ON, 'Power should have been set to ON')
+        self.assertEqual(pjlink.power, S_ON, 'Power should have been set to ON')
         mock_send_command.assert_called_once_with('INST')
-        self.assertEquals(mock_UpdateIcons.emit.called, True, 'projectorUpdateIcons should have been called')
+        self.assertEqual(mock_UpdateIcons.emit.called, True, 'projectorUpdateIcons should have been called')
 
     @patch.object(pjlink_test, 'projectorReceivedData')
     @patch.object(pjlink_test, 'projectorUpdateIcons')
@@ -205,9 +311,9 @@
         pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_STANDBY])
 
         # THEN: Power should be set to STANDBY
-        self.assertEquals(pjlink.power, S_STANDBY, 'Power should have been set to STANDBY')
-        self.assertEquals(mock_send_command.called, False, 'send_command should not have been called')
-        self.assertEquals(mock_UpdateIcons.emit.called, True, 'projectorUpdateIcons should have been called')
+        self.assertEqual(pjlink.power, S_STANDBY, 'Power should have been set to STANDBY')
+        self.assertEqual(mock_send_command.called, False, 'send_command should not have been called')
+        self.assertEqual(mock_UpdateIcons.emit.called, True, 'projectorUpdateIcons should have been called')
 
     @patch.object(pjlink_test, 'projectorUpdateIcons')
     def test_projector_process_avmt_closed_unmuted(self, mock_projectorReceivedData):
@@ -289,7 +395,7 @@
         pjlink.process_inpt('1')
 
         # THEN: Input selected should reflect current input
-        self.assertEquals(pjlink.source, '1', 'Input source should be set to "1"')
+        self.assertEqual(pjlink.source, '1', 'Input source should be set to "1"')
 
     def test_projector_reset_information(self):
         """
@@ -318,7 +424,7 @@
                 pjlink.reset_information()
 
         # THEN: All information should be reset and timers stopped
-        self.assertEquals(pjlink.power, S_OFF, 'Projector power should be OFF')
+        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')
@@ -329,7 +435,7 @@
         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.assertEquals(pjlink.send_queue, [], 'Projector send_queue should be an empty list')
+        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.called, 'Projector timer.stop()  should have been called')
         self.assertTrue(mock_socket_timer.called, 'Projector socket_timer.stop() should have been called')
@@ -355,8 +461,8 @@
         # WHEN: call with authentication request and pin not set
         pjlink.check_login(data=TEST_CONNECT_AUTHENTICATE)
 
-        # THEN: No Authentication signal should have been sent
-        mock_authentication.emit.assert_called_with(pjlink.name)
+        # THEN: 'No Authentication' signal should have been sent
+        mock_authentication.emit.assert_called_with(pjlink.ip)
 
     @patch.object(pjlink_test, 'waitForReadyRead')
     @patch.object(pjlink_test, 'state')
@@ -381,8 +487,8 @@
         pjlink.check_login(data=TEST_CONNECT_AUTHENTICATE)
 
         # THEN: send_command should have the proper authentication
-        self.assertEquals("{test}".format(test=mock_send_command.call_args),
-                          "call(data='{hash}%1CLSS ?\\r')".format(hash=TEST_HASH))
+        self.assertEqual("{test}".format(test=mock_send_command.call_args),
+                         "call(data='{hash}%1CLSS ?\\r')".format(hash=TEST_HASH))
 
     @patch.object(pjlink_test, 'disconnect_from_host')
     def socket_abort_test(self, mock_disconnect):


Follow ups