PJlink class 2 updates C

This commit is contained in:
Ken Roberts 2017-05-19 22:51:58 -07:00
parent 5c161eb541
commit cb5c0f82a3
8 changed files with 122 additions and 63 deletions

View File

@ -621,5 +621,5 @@ from .imagemanager import ImageManager
from .renderer import Renderer from .renderer import Renderer
from .mediamanageritem import MediaManagerItem from .mediamanageritem import MediaManagerItem
from .projector.db import ProjectorDB, Projector from .projector.db import ProjectorDB, Projector
from .projector.pjlink1 import PJLink1 from .projector.pjlink1 import PJLink
from .projector.constants import PJLINK_PORT, ERROR_MSG, ERROR_STRING from .projector.constants import PJLINK_PORT, ERROR_MSG, ERROR_STRING

View File

@ -150,11 +150,15 @@ class Projector(CommonBase, Base):
name: Column(String(20)) name: Column(String(20))
location: Column(String(30)) location: Column(String(30))
notes: Column(String(200)) notes: Column(String(200))
pjlink_name: Column(String(128)) # From projector (future) pjlink_name: Column(String(128)) # From projector
manufacturer: Column(String(128)) # From projector (future) manufacturer: Column(String(128)) # From projector
model: Column(String(128)) # From projector (future) model: Column(String(128)) # From projector
other: Column(String(128)) # From projector (future) other: Column(String(128)) # From projector
sources: Column(String(128)) # From projector (future) sources: Column(String(128)) # From projector
serial_no: Column(String(30)) # From projector (Class 2)
sw_version: Column(String(30)) # From projector (Class 2)
model_filter: Column(String(30)) # From projector (Class 2)
model_lamp: Column(String(30)) # From projector (Class 2)
ProjectorSource relates ProjectorSource relates
""" """
@ -164,20 +168,25 @@ class Projector(CommonBase, Base):
""" """
return '< Projector(id="{data}", ip="{ip}", port="{port}", pin="{pin}", name="{name}", ' \ return '< Projector(id="{data}", ip="{ip}", port="{port}", pin="{pin}", name="{name}", ' \
'location="{location}", notes="{notes}", pjlink_name="{pjlink_name}", ' \ 'location="{location}", notes="{notes}", pjlink_name="{pjlink_name}", ' \
'manufacturer="{manufacturer}", model="{model}", other="{other}", ' \ 'manufacturer="{manufacturer}", model="{model}", serial_no="{serial}", other="{other}", ' \
'sources="{sources}", source_list="{source_list}") >'.format(data=self.id, 'sources="{sources}", source_list="{source_list}", model_filter="{mfilter}", ' \
ip=self.ip, 'model_lamp="{mlamp}", sw_version="{sw_ver}") >'.format(data=self.id,
port=self.port, ip=self.ip,
pin=self.pin, port=self.port,
name=self.name, pin=self.pin,
location=self.location, name=self.name,
notes=self.notes, location=self.location,
pjlink_name=self.pjlink_name, notes=self.notes,
manufacturer=self.manufacturer, pjlink_name=self.pjlink_name,
model=self.model, manufacturer=self.manufacturer,
other=self.other, model=self.model,
sources=self.sources, other=self.other,
source_list=self.source_list) sources=self.sources,
source_list=self.source_list,
serial=self.serial_no,
mfilter=self.model_filter,
mlamp=self.model_lamp,
sw_ver=self.sw_version)
ip = Column(String(100)) ip = Column(String(100))
port = Column(String(8)) port = Column(String(8))
pin = Column(String(20)) pin = Column(String(20))
@ -189,6 +198,10 @@ class Projector(CommonBase, Base):
model = Column(String(128)) model = Column(String(128))
other = Column(String(128)) other = Column(String(128))
sources = Column(String(128)) sources = Column(String(128))
serial_no = Column(String(30))
sw_version = Column(String(30))
model_filter = Column(String(30))
model_lamp = Column(String(30))
source_list = relationship('ProjectorSource', source_list = relationship('ProjectorSource',
order_by='ProjectorSource.code', order_by='ProjectorSource.code',
backref='projector', backref='projector',
@ -359,6 +372,10 @@ class ProjectorDB(Manager):
old_projector.model = projector.model old_projector.model = projector.model
old_projector.other = projector.other old_projector.other = projector.other
old_projector.sources = projector.sources old_projector.sources = projector.sources
old_projector.serial_no = projector.serial_no
old_projector.sw_version = projector.sw_version
old_projector.model_filter = projector.model_filter
old_projector.model_lamp = projector.model_lamp
return self.save_object(old_projector) return self.save_object(old_projector)
def delete_projector(self, projector): def delete_projector(self, projector):

View File

@ -42,7 +42,7 @@ log = logging.getLogger(__name__)
log.debug('pjlink1 loaded') log.debug('pjlink1 loaded')
__all__ = ['PJLink1'] __all__ = ['PJLink']
from codecs import decode from codecs import decode
@ -68,7 +68,7 @@ PJLINK_HEADER = '{prefix}{{linkclass}}'.format(prefix=PJLINK_PREFIX)
PJLINK_SUFFIX = CR PJLINK_SUFFIX = CR
class PJLink1(QtNetwork.QTcpSocket): class PJLink(QtNetwork.QTcpSocket):
""" """
Socket service for connecting to a PJLink-capable projector. Socket service for connecting to a PJLink-capable projector.
""" """
@ -129,7 +129,7 @@ class PJLink1(QtNetwork.QTcpSocket):
self.ip = ip self.ip = ip
self.port = port self.port = port
self.pin = pin self.pin = pin
super(PJLink1, self).__init__() super(PJLink, self).__init__()
self.dbid = None self.dbid = None
self.location = None self.location = None
self.notes = None self.notes = None
@ -162,7 +162,7 @@ class PJLink1(QtNetwork.QTcpSocket):
# Socket timer for some possible brain-dead projectors or network cable pulled # Socket timer for some possible brain-dead projectors or network cable pulled
self.socket_timer = None self.socket_timer = None
# Map command to function # Map command to function
self.pjlink1_functions = { self.pjlink_functions = {
'AVMT': self.process_avmt, 'AVMT': self.process_avmt,
'CLSS': self.process_clss, 'CLSS': self.process_clss,
'ERST': self.process_erst, 'ERST': self.process_erst,
@ -286,7 +286,7 @@ class PJLink1(QtNetwork.QTcpSocket):
elif status in STATUS_STRING: elif status in STATUS_STRING:
return STATUS_STRING[status], ERROR_MSG[status] return STATUS_STRING[status], ERROR_MSG[status]
else: else:
return status, translate('OpenLP.PJLink1', 'Unknown status') return status, translate('OpenLP.PJLink', 'Unknown status')
def change_status(self, status, msg=None): def change_status(self, status, msg=None):
""" """
@ -296,7 +296,7 @@ class PJLink1(QtNetwork.QTcpSocket):
:param status: Status code :param status: Status code
:param msg: Optional message :param msg: Optional message
""" """
message = translate('OpenLP.PJLink1', 'No message') if msg is None else msg message = translate('OpenLP.PJLink', 'No message') if msg is None else msg
(code, message) = self._get_status(status) (code, message) = self._get_status(status)
if msg is not None: if msg is not None:
message = msg message = msg
@ -576,7 +576,7 @@ class PJLink1(QtNetwork.QTcpSocket):
if sent == -1: if sent == -1:
# Network error? # Network error?
self.change_status(E_NETWORK, self.change_status(E_NETWORK,
translate('OpenLP.PJLink1', 'Error while sending data to projector')) translate('OpenLP.PJLink', 'Error while sending data to projector'))
def process_command(self, cmd, data): def process_command(self, cmd, data):
""" """
@ -625,8 +625,9 @@ class PJLink1(QtNetwork.QTcpSocket):
self.projectorReceivedData.emit() self.projectorReceivedData.emit()
return return
if cmd in self.pjlink1_functions: if cmd in self.pjlink_functions:
self.pjlink1_functions[cmd](data) log.debug('({ip}) Calling function for {cmd}'.format(ip=self.ip, cmd=cmd))
self.pjlink_functions[cmd](data)
else: else:
log.warning('({ip}) Invalid command {data}'.format(ip=self.ip, data=cmd)) log.warning('({ip}) Invalid command {data}'.format(ip=self.ip, data=cmd))
self.send_busy = False self.send_busy = False
@ -662,6 +663,7 @@ class PJLink1(QtNetwork.QTcpSocket):
:param data: Power status :param data: Power status
""" """
log.debug('({ip}: Processing POWR command'.format(ip=self.ip))
if data in PJLINK_POWR_STATUS: if data in PJLINK_POWR_STATUS:
power = PJLINK_POWR_STATUS[data] power = PJLINK_POWR_STATUS[data]
update_icons = self.power != power update_icons = self.power != power

View File

@ -38,7 +38,7 @@ from openlp.core.lib.projector.constants import ERROR_MSG, ERROR_STRING, E_AUTHE
E_NETWORK, E_NOT_CONNECTED, E_UNKNOWN_SOCKET_ERROR, STATUS_STRING, S_CONNECTED, S_CONNECTING, S_COOLDOWN, \ 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 S_INITIALIZE, S_NOT_CONNECTED, S_OFF, S_ON, S_STANDBY, S_WARMUP
from openlp.core.lib.projector.db import ProjectorDB from openlp.core.lib.projector.db import ProjectorDB
from openlp.core.lib.projector.pjlink1 import PJLink1 from openlp.core.lib.projector.pjlink1 import PJLink
from openlp.core.ui.projector.editform import ProjectorEditForm from openlp.core.ui.projector.editform import ProjectorEditForm
from openlp.core.ui.projector.sourceselectform import SourceSelectTabs, SourceSelectSingle from openlp.core.ui.projector.sourceselectform import SourceSelectTabs, SourceSelectSingle
@ -690,19 +690,19 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, UiProjecto
Helper app to build a projector instance Helper app to build a projector instance
:param projector: Dict of projector database information :param projector: Dict of projector database information
:returns: PJLink1() instance :returns: PJLink() instance
""" """
log.debug('_add_projector()') log.debug('_add_projector()')
return PJLink1(dbid=projector.id, return PJLink(dbid=projector.id,
ip=projector.ip, ip=projector.ip,
port=int(projector.port), port=int(projector.port),
name=projector.name, name=projector.name,
location=projector.location, location=projector.location,
notes=projector.notes, notes=projector.notes,
pin=None if projector.pin == '' else projector.pin, pin=None if projector.pin == '' else projector.pin,
poll_time=self.poll_time, poll_time=self.poll_time,
socket_timeout=self.socket_timeout socket_timeout=self.socket_timeout
) )
def add_projector(self, projector, start=False): def add_projector(self, projector, start=False):
""" """
@ -961,7 +961,7 @@ class ProjectorItem(QtCore.QObject):
""" """
Initialization for ProjectorItem instance Initialization for ProjectorItem instance
:param link: PJLink1 instance for QListWidgetItem :param link: PJLink instance for QListWidgetItem
""" """
self.link = link self.link = link
self.thread = None self.thread = None

View File

@ -29,7 +29,6 @@ class TestProjectorConstants(TestCase):
""" """
Test specific functions in the projector constants module. Test specific functions in the projector constants module.
""" """
@skip('Waiting for merge of ~alisonken1/openlp/pjlink2-resource-data')
def build_pjlink_video_label_test(self): def build_pjlink_video_label_test(self):
""" """
Test building PJLINK_DEFAULT_CODES dictionary Test building PJLINK_DEFAULT_CODES dictionary

View File

@ -25,13 +25,13 @@ Package to test the openlp.core.lib.projector.pjlink1 package.
from unittest import TestCase from unittest import TestCase
from unittest.mock import call, patch, MagicMock from unittest.mock import call, patch, MagicMock
from openlp.core.lib.projector.pjlink1 import PJLink1 from openlp.core.lib.projector.pjlink1 import PJLink
from openlp.core.lib.projector.constants import E_PARAMETER, ERROR_STRING, S_OFF, S_STANDBY, S_ON, \ from openlp.core.lib.projector.constants import E_PARAMETER, ERROR_STRING, S_OFF, S_STANDBY, S_ON, \
PJLINK_POWR_STATUS, S_CONNECTED PJLINK_POWR_STATUS, S_CONNECTED
from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE, TEST_HASH from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE, TEST_HASH
pjlink_test = PJLink1(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True) pjlink_test = PJLink(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True)
class TestPJLink(TestCase): class TestPJLink(TestCase):
@ -164,23 +164,36 @@ class TestPJLink(TestCase):
'Lamp 3 hours should have been set to 33333') 'Lamp 3 hours should have been set to 33333')
@patch.object(pjlink_test, 'projectorReceivedData') @patch.object(pjlink_test, 'projectorReceivedData')
def test_projector_process_power_on(self, mock_projectorReceivedData): @patch.object(pjlink_test, 'projectorUpdateIcons')
@patch.object(pjlink_test, 'send_command')
@patch.object(pjlink_test, 'change_status')
def test_projector_process_power_on(self, mock_change_status,
mock_send_command,
mock_UpdateIcons,
mock_ReceivedData):
""" """
Test status power to ON Test status power to ON
""" """
# GIVEN: Test object and preset # GIVEN: Test object and preset
pjlink = pjlink_test pjlink = pjlink_test
pjlink.power = S_STANDBY pjlink.power = S_STANDBY
pjlink.socket_timer = MagicMock()
# WHEN: Call process_command with turn power on command # WHEN: Call process_command with turn power on command
pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_ON]) pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_ON])
# THEN: Power should be set to ON # THEN: Power should be set to ON
self.assertEquals(pjlink.power, S_ON, 'Power should have been set to ON') self.assertEquals(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')
@patch.object(pjlink_test, 'projectorReceivedData') @patch.object(pjlink_test, 'projectorReceivedData')
def test_projector_process_power_off(self, mock_projectorReceivedData): @patch.object(pjlink_test, 'projectorUpdateIcons')
@patch.object(pjlink_test, 'send_command')
@patch.object(pjlink_test, 'change_status')
def test_projector_process_power_off(self, mock_change_status,
mock_send_command,
mock_UpdateIcons,
mock_ReceivedData):
""" """
Test status power to STANDBY Test status power to STANDBY
""" """
@ -193,6 +206,8 @@ class TestPJLink(TestCase):
# THEN: Power should be set to STANDBY # THEN: Power should be set to STANDBY
self.assertEquals(pjlink.power, S_STANDBY, 'Power should have been 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')
@patch.object(pjlink_test, 'projectorUpdateIcons') @patch.object(pjlink_test, 'projectorUpdateIcons')
def test_projector_process_avmt_closed_unmuted(self, mock_projectorReceivedData): def test_projector_process_avmt_closed_unmuted(self, mock_projectorReceivedData):
@ -372,7 +387,7 @@ class TestPJLink(TestCase):
@patch.object(pjlink_test, '_not_implemented') @patch.object(pjlink_test, '_not_implemented')
def not_implemented_test(self, mock_not_implemented): def not_implemented_test(self, mock_not_implemented):
""" """
Test pjlink1._not_implemented method being called Test PJLink._not_implemented method being called
""" """
# GIVEN: test object # GIVEN: test object
pjlink = pjlink_test pjlink = pjlink_test
@ -381,13 +396,13 @@ class TestPJLink(TestCase):
# WHEN: A future command is called that is not implemented yet # WHEN: A future command is called that is not implemented yet
pjlink.process_command(test_cmd, "Garbage data for test only") pjlink.process_command(test_cmd, "Garbage data for test only")
# THEN: pjlink1.__not_implemented should have been called with test_cmd # THEN: PJLink.__not_implemented should have been called with test_cmd
mock_not_implemented.assert_called_with(test_cmd) mock_not_implemented.assert_called_with(test_cmd)
@patch.object(pjlink_test, 'disconnect_from_host') @patch.object(pjlink_test, 'disconnect_from_host')
def socket_abort_test(self, mock_disconnect): def socket_abort_test(self, mock_disconnect):
""" """
Test PJLink1.socket_abort calls disconnect_from_host Test PJLink.socket_abort calls disconnect_from_host
""" """
# GIVEN: Test object # GIVEN: Test object
pjlink = pjlink_test pjlink = pjlink_test
@ -400,7 +415,7 @@ class TestPJLink(TestCase):
def poll_loop_not_connected_test(self): def poll_loop_not_connected_test(self):
""" """
Test PJLink1.poll_loop not connected return Test PJLink.poll_loop not connected return
""" """
# GIVEN: Test object and mocks # GIVEN: Test object and mocks
pjlink = pjlink_test pjlink = pjlink_test
@ -409,7 +424,7 @@ class TestPJLink(TestCase):
pjlink.state.return_value = False pjlink.state.return_value = False
pjlink.ConnectedState = True pjlink.ConnectedState = True
# WHEN: PJLink1.poll_loop called # WHEN: PJLink.poll_loop called
pjlink.poll_loop() pjlink.poll_loop()
# THEN: poll_loop should exit without calling any other method # THEN: poll_loop should exit without calling any other method
@ -418,7 +433,7 @@ class TestPJLink(TestCase):
@patch.object(pjlink_test, 'send_command') @patch.object(pjlink_test, 'send_command')
def poll_loop_start_test(self, mock_send_command): def poll_loop_start_test(self, mock_send_command):
""" """
Test PJLink1.poll_loop makes correct calls Test PJLink.poll_loop makes correct calls
""" """
# GIVEN: test object and test data # GIVEN: test object and test data
pjlink = pjlink_test pjlink = pjlink_test
@ -450,7 +465,7 @@ class TestPJLink(TestCase):
call('NAME', queue=True), call('NAME', queue=True),
] ]
# WHEN: PJLink1.poll_loop is called # WHEN: PJLink.poll_loop is called
pjlink.poll_loop() pjlink.poll_loop()
# THEN: proper calls were made to retrieve projector data # THEN: proper calls were made to retrieve projector data

View File

@ -26,13 +26,15 @@ record functions.
PREREQUISITE: add_record() and get_all() functions validated. PREREQUISITE: add_record() and get_all() functions validated.
""" """
import os import os
import shutil
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from openlp.core.lib.projector.db import Manufacturer, Model, Projector, ProjectorDB, ProjectorSource, Source from openlp.core.lib.projector.db import Manufacturer, Model, Projector, ProjectorDB, ProjectorSource, Source
from openlp.core.lib.projector.constants import PJLINK_PORT from openlp.core.lib.projector.constants import PJLINK_PORT
from tests.resources.projector.data import TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA from tests.resources.projector.data import TEST_DB_PJLINK1, TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA
from tests.utils.constants import TEST_RESOURCES_PATH
def compare_data(one, two): def compare_data(one, two):
@ -45,7 +47,11 @@ def compare_data(one, two):
one.port == two.port and \ one.port == two.port and \
one.name == two.name and \ one.name == two.name and \
one.location == two.location and \ one.location == two.location and \
one.notes == two.notes one.notes == two.notes and \
one.sw_version == two.sw_version and \
one.serial_no == two.serial_no and \
one.model_filter == two.model_filter and \
one.model_lamp == two.model_lamp
def compare_source(one, two): def compare_source(one, two):
@ -168,6 +174,10 @@ class TestProjectorDB(TestCase):
record.name = TEST3_DATA['name'] record.name = TEST3_DATA['name']
record.location = TEST3_DATA['location'] record.location = TEST3_DATA['location']
record.notes = TEST3_DATA['notes'] record.notes = TEST3_DATA['notes']
record.sw_version = TEST3_DATA['sw_version']
record.serial_no = TEST3_DATA['serial_no']
record.model_filter = TEST3_DATA['model_filter']
record.model_lamp = TEST3_DATA['model_lamp']
updated = self.projector.update_projector(record) updated = self.projector.update_projector(record)
self.assertTrue(updated, 'Save updated record should have returned True') self.assertTrue(updated, 'Save updated record should have returned True')
record = self.projector.get_projector_by_ip(TEST3_DATA['ip']) record = self.projector.get_projector_by_ip(TEST3_DATA['ip'])
@ -246,7 +256,8 @@ class TestProjectorDB(TestCase):
projector = Projector() projector = Projector()
# WHEN: projector() is populated # WHEN: projector() is populated
# NOTE: projector.pin, projector.other, projector.sources should all return None # NOTE: projector.[pin, other, sources, sw_version, serial_no, sw_version, model_lamp, model_filter]
# should all return None.
# projector.source_list should return an empty list # projector.source_list should return an empty list
projector.id = 0 projector.id = 0
projector.ip = '127.0.0.1' projector.ip = '127.0.0.1'
@ -262,8 +273,9 @@ class TestProjectorDB(TestCase):
self.assertEqual(str(projector), self.assertEqual(str(projector),
'< Projector(id="0", ip="127.0.0.1", port="4352", pin="None", name="Test One", ' '< Projector(id="0", ip="127.0.0.1", port="4352", pin="None", name="Test One", '
'location="Somewhere over the rainbow", notes="Not again", pjlink_name="TEST", ' 'location="Somewhere over the rainbow", notes="Not again", pjlink_name="TEST", '
'manufacturer="IN YOUR DREAMS", model="OpenLP", other="None", sources="None", ' 'manufacturer="IN YOUR DREAMS", model="OpenLP", serial_no="None", other="None", '
'source_list="[]") >', 'sources="None", source_list="[]", model_filter="None", model_lamp="None", '
'sw_version="None") >',
'Projector.__repr__() should have returned a proper representation string') 'Projector.__repr__() should have returned a proper representation string')
def test_projectorsource_repr(self): def test_projectorsource_repr(self):

View File

@ -27,6 +27,8 @@ import os
from tempfile import gettempdir from tempfile import gettempdir
# Test data # Test data
TEST_DB_PJLINK1 = 'projector_pjlink1.sqlite'
TEST_DB = os.path.join(gettempdir(), 'openlp-test-projectordb.sql') TEST_DB = os.path.join(gettempdir(), 'openlp-test-projectordb.sql')
TEST_SALT = '498e4a67' TEST_SALT = '498e4a67'
@ -44,21 +46,33 @@ TEST1_DATA = dict(ip='111.111.111.111',
pin='1111', pin='1111',
name='___TEST_ONE___', name='___TEST_ONE___',
location='location one', location='location one',
notes='notes one') notes='notes one',
serial_no='Serial Number 1',
sw_version='Version 1',
model_filter='Filter type 1',
model_lamp='Lamp type 1')
TEST2_DATA = dict(ip='222.222.222.222', TEST2_DATA = dict(ip='222.222.222.222',
port='2222', port='2222',
pin='2222', pin='2222',
name='___TEST_TWO___', name='___TEST_TWO___',
location='location two', location='location two',
notes='notes two') notes='notes one',
serial_no='Serial Number 2',
sw_version='Version 2',
model_filter='Filter type 2',
model_lamp='Lamp type 2')
TEST3_DATA = dict(ip='333.333.333.333', TEST3_DATA = dict(ip='333.333.333.333',
port='3333', port='3333',
pin='3333', pin='3333',
name='___TEST_THREE___', name='___TEST_THREE___',
location='location three', location='location three',
notes='notes three') notes='notes one',
serial_no='Serial Number 3',
sw_version='Version 3',
model_filter='Filter type 3',
model_lamp='Lamp type 3')
TEST_VIDEO_CODES = { TEST_VIDEO_CODES = {
'11': 'RGB 1', '11': 'RGB 1',