openlp/openlp/core/lib/projector/pjlink1.py

812 lines
30 KiB
Python
Raw Normal View History

2014-10-06 19:10:03 +00:00
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Ken Roberts, Simon Scudder, #
# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Dave Warnock, Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`projector.pjlink1` module provides the necessary functions
for connecting to a PJLink-capable projector.
See PJLink Specifications for Class 1 for details.
NOTE:
Function names follow the following syntax:
def process_CCCC(...):
WHERE:
CCCC = PJLink command being processed.
See PJLINK_FUNC(...) for command returned from projector.
"""
import logging
log = logging.getLogger(__name__)
2014-10-09 01:43:34 +00:00
log.debug('rpjlink1 loaded')
2014-10-06 19:10:03 +00:00
__all__ = ['PJLink1']
from time import sleep
from codecs import decode, encode
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import QObject, pyqtSignal, pyqtSlot
from PyQt4.QtNetwork import QAbstractSocket, QTcpSocket
from openlp.core.common import translate, qmd5_hash
from openlp.core.lib.projector.constants import *
# Shortcuts
SocketError = QAbstractSocket.SocketError
SocketSTate = QAbstractSocket.SocketState
PJLINK_PREFIX = '%'
PJLINK_CLASS = '1'
PJLINK_HEADER = '%s%s' % (PJLINK_PREFIX, PJLINK_CLASS)
PJLINK_SUFFIX = CR
class PJLink1(QTcpSocket):
"""
Socket service for connecting to a PJLink-capable projector.
"""
changeStatus = pyqtSignal(str, int, str)
projectorNetwork = pyqtSignal(int) # Projector network activity
2014-10-15 17:22:12 +00:00
projectorStatus = pyqtSignal(int) # Status update
2014-10-13 20:16:25 +00:00
projectorAuthentication = pyqtSignal(str) # Authentication error
projectorNoAuthentication = pyqtSignal(str) # PIN set and no authentication needed
2014-10-15 17:22:12 +00:00
projectorReceivedData = pyqtSignal() # Notify when received data finished processing
projectorUpdateIcons = pyqtSignal() # Update the status icons on toolbar
2014-10-06 19:10:03 +00:00
def __init__(self, name=None, ip=None, port=PJLINK_PORT, pin=None, *args, **kwargs):
"""
Setup for instance.
:param name: Display name
:param ip: IP address to connect to
:param port: Port to use. Default to PJLINK_PORT
:param pin: Access pin (if needed)
Optional parameters
:param dbid: Database ID number
:param location: Location where projector is physically located
:param notes: Extra notes about the projector
"""
log.debug('PJlink(args="%s" kwargs="%s")' % (args, kwargs))
self.name = name
self.ip = ip
self.port = port
self.pin = pin
super(PJLink1, self).__init__()
self.dbid = None
self.location = None
self.notes = None
# Allowances for Projector Wizard option
if 'dbid' in kwargs:
self.dbid = kwargs['dbid']
else:
self.dbid = None
if 'location' in kwargs:
self.location = kwargs['location']
else:
self.location = None
if 'notes' in kwargs:
self.notes = kwargs['notes']
else:
self.notes = None
if 'wizard' in kwargs:
self.new_wizard = True
else:
self.new_wizard = False
if 'poll_time' in kwargs:
# Convert seconds to milliseconds
self.poll_time = kwargs['poll_time'] * 1000
else:
2014-10-15 01:40:21 +00:00
# Default 20 seconds
self.poll_time = 20000
if 'socket_timeout' in kwargs:
# Convert seconds to milliseconds
self.socket_timeout = kwargs['socket_timeout'] * 1000
2014-10-15 01:40:21 +00:00
else:
# Default is 5 seconds
self.socket_timeout = 5000
2014-10-06 19:10:03 +00:00
self.i_am_running = False
self.status_connect = S_NOT_CONNECTED
self.last_command = ''
self.projector_status = S_NOT_CONNECTED
self.error_status = S_OK
# Socket information
2014-10-09 20:30:07 +00:00
# Account for self.readLine appending \0 and/or extraneous \r
2014-10-06 19:10:03 +00:00
self.maxSize = PJLINK_MAX_PACKET + 2
self.setReadBufferSize(self.maxSize)
# PJLink projector information
self.pjlink_class = '1' # Default class
self.reset_information()
2014-10-06 19:10:03 +00:00
# Set from ProjectorManager.add_projector()
self.widget = None # QListBox entry
self.timer = None # Timer that calls the poll_loop
2014-10-15 17:22:12 +00:00
self.send_queue = []
self.send_busy = False
2014-10-06 19:10:03 +00:00
# Map command returned to function
self.PJLINK1_FUNC = {'AVMT': self.process_avmt,
'CLSS': self.process_clss,
'ERST': self.process_erst,
'INFO': self.process_info,
'INF1': self.process_inf1,
'INF2': self.process_inf2,
'INPT': self.process_inpt,
'INST': self.process_inst,
'LAMP': self.process_lamp,
'NAME': self.process_name,
2014-10-13 16:40:58 +00:00
'PJLINK': self.check_login,
2014-10-06 19:10:03 +00:00
'POWR': self.process_powr
}
def reset_information(self):
self.power = S_OFF
self.pjlink_name = None
self.manufacturer = None
self.model = None
self.shutter = None
self.mute = None
self.lamp = None
self.fan = None
self.source_available = None
self.source = None
self.other_info = None
2014-10-13 16:40:58 +00:00
if hasattr(self, 'timer'):
self.timer.stop()
2014-10-06 19:10:03 +00:00
def thread_started(self):
"""
Connects signals to methods when thread is started.
"""
log.debug('(%s) Thread starting' % self.ip)
self.i_am_running = True
self.connected.connect(self.check_login)
self.disconnected.connect(self.disconnect_from_host)
self.error.connect(self.get_error)
def thread_stopped(self):
"""
Cleanups when thread is stopped.
"""
log.debug('(%s) Thread stopped' % self.ip)
self.connected.disconnect(self.check_login)
self.disconnected.disconnect(self.disconnect_from_host)
self.error.disconnect(self.get_error)
2014-10-15 17:22:12 +00:00
self.projectorReceivedData.disconnect(self._send_command)
2014-10-06 19:10:03 +00:00
self.disconnect_from_host()
self.deleteLater()
self.i_am_running = False
def poll_loop(self):
"""
Called by QTimer in ProjectorManager.ProjectorItem.
Retrieves status information.
"""
if self.state() != self.ConnectedState:
return
log.debug('(%s) Updating projector status' % self.ip)
# Reset timer in case we were called from a set command
if self.timer.interval() < self.poll_time:
# Reset timer to 5 seconds
self.timer.setInterval(self.poll_time)
2014-10-06 19:10:03 +00:00
self.timer.start()
2014-10-09 01:43:34 +00:00
for command in ['POWR', 'ERST', 'LAMP', 'AVMT', 'INPT']:
2014-10-13 16:40:58 +00:00
# Changeable information
2014-10-09 01:43:34 +00:00
self.send_command(command)
if self.power == S_ON and self.source_available is None:
self.send_command('INST')
if self.other_info is None:
self.send_command('INFO')
2014-10-13 16:40:58 +00:00
if self.manufacturer is None:
2014-10-13 20:16:25 +00:00
self.send_command('INF1')
if self.model is None:
self.send_command('INF2')
if self.pjlink_name is None:
self.send_command('NAME')
if self.power == S_ON and self.source_available is None:
self.send_command('INST')
2014-10-06 19:10:03 +00:00
def _get_status(self, status):
"""
Helper to retrieve status/error codes and convert to strings.
"""
# Return the status code as a string
if status in ERROR_STRING:
return (ERROR_STRING[status], ERROR_MSG[status])
elif status in STATUS_STRING:
return (STATUS_STRING[status], ERROR_MSG[status])
else:
2014-10-09 01:43:34 +00:00
return (status, translate('OpenLP.PJLink1', 'Unknown status'))
2014-10-06 19:10:03 +00:00
def change_status(self, status, msg=None):
"""
Check connection/error status, set status for projector, then emit status change signal
for gui to allow changing the icons.
"""
2014-10-09 01:43:34 +00:00
message = translate('OpenLP.PJLink1', 'No message') if msg is None else msg
2014-10-06 19:10:03 +00:00
(code, message) = self._get_status(status)
if msg is not None:
message = msg
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:
self.status_connect = status
self.projector_status = S_NOT_CONNECTED
elif status < S_NETWORK_SENDING:
self.status_connect = S_CONNECTED
self.projector_status = status
(status_code, status_message) = self._get_status(self.status_connect)
log.debug('(%s) status_connect: %s: %s' % (self.ip, status_code, status_message if msg is None else msg))
(status_code, status_message) = self._get_status(self.projector_status)
log.debug('(%s) projector_status: %s: %s' % (self.ip, status_code, status_message if msg is None else msg))
(status_code, status_message) = self._get_status(self.error_status)
log.debug('(%s) error_status: %s: %s' % (self.ip, status_code, status_message if msg is None else msg))
self.changeStatus.emit(self.ip, status, message)
def check_command(self, cmd):
"""
Verifies command is valid based on PJLink class.
"""
return self.pjlink_class in PJLINK_VALID_CMD and \
cmd in PJLINK_VALID_CMD[self.pjlink_class]
2014-10-09 20:30:07 +00:00
@pyqtSlot()
def check_login(self, data=None):
2014-10-06 19:10:03 +00:00
"""
Processes the initial connection and authentication (if needed).
"""
2014-10-13 20:16:25 +00:00
log.debug('(%s) check_login(data="%s")' % (self.ip, data))
2014-10-09 20:30:07 +00:00
if data is None:
# Reconnected setup?
if not self.waitForReadyRead(2000):
# Possible timeout issue
log.error('(%s) Socket timeout waiting for login' % self.ip)
self.change_status(E_SOCKET_TIMEOUT)
return
2014-10-09 20:30:07 +00:00
read = self.readLine(self.maxSize)
dontcare = self.readLine(self.maxSize) # Clean out the trailing \r\n
2014-10-13 20:16:25 +00:00
if read is None:
log.warn('(%s) read is None - socket error?' % self.ip)
return
elif len(read) < 8:
2014-10-09 20:30:07 +00:00
log.warn('(%s) Not enough data read)' % self.ip)
return
data = decode(read, 'ascii')
# Possibility of extraneous data on input when reading.
# Clean out extraneous characters in buffer.
dontcare = self.readLine(self.maxSize)
2014-10-13 20:16:25 +00:00
log.debug('(%s) check_login() read "%s"' % (self.ip, data.strip()))
2014-10-06 19:10:03 +00:00
# At this point, we should only have the initial login prompt with
# possible authentication
if not data.upper().startswith('PJLINK'):
# Invalid response
return self.disconnect_from_host()
2014-10-13 20:16:25 +00:00
# Test for authentication error
2014-10-13 16:40:58 +00:00
if '=' in data:
data_check = data.strip().split('=')
else:
data_check = data.strip().split(' ')
2014-10-06 19:10:03 +00:00
log.debug('(%s) data_check="%s"' % (self.ip, data_check))
# PJLink initial login will be:
# 'PJLink 0' - Unauthenticated login - no extra steps required.
# 'PJLink 1 XXXXXX' Authenticated login - extra processing required.
2014-10-13 20:16:25 +00:00
# Oops - projector error
if data_check[1].upper() == 'ERRA':
# Authentication error
self.disconnect_from_host()
self.change_status(E_AUTHENTICATION)
log.debug('(%s) emitting projectorAuthentication() signal' % self.name)
return
elif data_check[1] == '0' and self.pin is not None:
# Pin set and no authentication needed
self.disconnect_from_host()
self.change_status(E_AUTHENTICATION)
log.debug('(%s) emitting projectorNoAuthentication() signal' % self.name)
self.projectorNoAuthentication.emit(self.name)
return
elif data_check[1] == '1':
2014-10-06 19:10:03 +00:00
# Authenticated login with salt
2014-10-13 20:16:25 +00:00
log.debug('(%s) Setting hash with salt="%s"' % (self.ip, data_check[2]))
log.debug('(%s) pin="%s"' % (self.ip, self.pin))
2014-10-06 19:10:03 +00:00
salt = qmd5_hash(salt=data_check[2], data=self.pin)
2014-10-13 16:40:58 +00:00
else:
salt = None
2014-10-06 19:10:03 +00:00
# We're connected at this point, so go ahead and do regular I/O
self.readyRead.connect(self.get_data)
# Initial data we should know about
self.send_command(cmd='CLSS', salt=salt)
self.waitForReadyRead()
2014-10-15 17:22:12 +00:00
self.projectorReceivedData.connect(self._send_command)
2014-10-13 16:40:58 +00:00
if not self.new_wizard and self.state() == self.ConnectedState:
self.timer.setInterval(2000) # Set 2 seconds for initial information
2014-10-06 19:10:03 +00:00
self.timer.start()
2014-10-15 18:18:00 +00:00
@pyqtSlot()
2014-10-06 19:10:03 +00:00
def get_data(self):
2014-10-15 18:18:00 +00:00
log.debug('(%s) get_data() Received readyRead signal' % self.ip)
return self._get_data()
def _get_data(self):
2014-10-06 19:10:03 +00:00
"""
Socket interface to retrieve data.
"""
2014-10-15 18:18:00 +00:00
log.debug('(%s) get_data(): Reading data' % self.ip)
2014-10-06 19:10:03 +00:00
if self.state() != self.ConnectedState:
log.debug('(%s) get_data(): Not connected - returning' % self.ip)
return
read = self.readLine(self.maxSize)
if read == -1:
# No data available
log.debug('(%s) get_data(): No data available (-1)' % self.ip)
2014-10-15 17:22:12 +00:00
self.projectorReceivedData.emit()
2014-10-06 19:10:03 +00:00
return
self.projectorNetwork.emit(S_NETWORK_RECEIVED)
data_in = decode(read, 'ascii')
data = data_in.strip()
if len(data) < 7:
2014-10-06 19:10:03 +00:00
# Not enough data for a packet
log.debug('(%s) get_data(): Packet length < 7: "%s"' % (self.ip, data))
2014-10-15 17:22:12 +00:00
self.projectorReceivedData.emit()
2014-10-06 19:10:03 +00:00
return
2014-10-15 18:18:00 +00:00
log.debug('(%s) get_data(): Checking new data "%s"' % (self.ip, data))
2014-10-09 20:30:07 +00:00
if data.upper().startswith('PJLINK'):
# Reconnected from remote host disconnect ?
2014-10-15 17:22:12 +00:00
self.check_login(data)
self.projectorReceivedData.emit()
return
elif '=' not in data:
2014-10-15 18:18:00 +00:00
log.warn('(%s) get_data(): Invalid packet received' % self.ip)
2014-10-15 17:22:12 +00:00
self.projectorReceivedData.emit()
2014-10-06 19:10:03 +00:00
return
data_split = data.split('=')
try:
(prefix, class_, cmd, data) = (data_split[0][0], data_split[0][1], data_split[0][2:], data_split[1])
except ValueError as e:
2014-10-15 18:18:00 +00:00
log.warn('(%s) get_data(): Invalid packet - expected header + command + data' % self.ip)
log.warn('(%s) get_data(): Received data: "%s"' % (self.ip, read))
2014-10-06 19:10:03 +00:00
self.change_status(E_INVALID_DATA)
2014-10-15 17:22:12 +00:00
self.projectorReceivedData.emit()
2014-10-06 19:10:03 +00:00
return
if not self.check_command(cmd):
2014-10-15 18:18:00 +00:00
log.warn('(%s) get_data(): Invalid packet - unknown command "%s"' % (self.ip, cmd))
2014-10-15 17:22:12 +00:00
self.projectorReceivedData.emit()
2014-10-06 19:10:03 +00:00
return
return self.process_command(cmd, data)
@pyqtSlot(int)
def get_error(self, err):
"""
Process error from SocketError signal
"""
log.debug('(%s) get_error(err=%s): %s' % (self.ip, err, self.errorString()))
if err <= 18:
2014-10-13 16:40:58 +00:00
# QSocket errors. Redefined in projector.constants so we don't mistake
2014-10-06 19:10:03 +00:00
# them for system errors
check = err + E_CONNECTION_REFUSED
self.timer.stop()
else:
check = err
if check < E_GENERAL:
# Some system error?
self.change_status(err, self.errorString())
else:
self.change_status(E_NETWORK, self.errorString())
2014-10-15 18:18:00 +00:00
self.projectorUpdateIcons.emit()
2014-10-06 19:10:03 +00:00
return
def send_command(self, cmd, opts='?', salt=None):
"""
2014-10-15 17:22:12 +00:00
Add command to output queue if not already in queue
2014-10-06 19:10:03 +00:00
"""
if self.state() != self.ConnectedState:
log.warn('(%s) send_command(): Not connected - returning' % self.ip)
2014-10-15 17:22:12 +00:00
self.send_queue = []
2014-10-06 19:10:03 +00:00
return
self.projectorNetwork.emit(S_NETWORK_SENDING)
2014-10-15 18:18:00 +00:00
log.debug('(%s) send_command(): Sending cmd="%s" opts="%s" %s' % (self.ip,
cmd,
opts,
'' if salt is None else 'with hash'))
2014-10-06 19:10:03 +00:00
if salt is None:
out = '%s%s %s%s' % (PJLINK_HEADER, cmd, opts, CR)
else:
out = '%s%s %s%s' % (salt, cmd, opts, CR)
2014-10-15 17:22:12 +00:00
if out in self.send_queue:
# Already there, so don't add
2014-10-15 18:18:00 +00:00
log.debug('(%s) send_command(out=%s) Already in queue - skipping' % (self.ip, out.strip()))
elif len(self.send_queue) == 0:
return self._send_string(out)
2014-10-15 17:22:12 +00:00
else:
self.send_queue.append(out)
if not self.send_busy:
self._send_string()
@pyqtSlot()
def _send_command(self):
log.debug('Received projectorReceivedData signal')
return self._send_string()
def _send_string(self, data=None):
"""
Socket interface to send data. If data=None, then check queue.
:param data: Immediate data to send
:returns: None
"""
log.debug('(%s) _send_string()' % self.ip)
2014-10-15 18:18:00 +00:00
if self.state() != self.ConnectedState:
log.debug('(%s) _send_string() Not connected - abort' % self.ip)
self.send_queue = []
self.send_busy = False
return
2014-10-15 17:22:12 +00:00
if data is not None:
out = data
log.debug('(%s) _send_string(data=%s)' % (self.ip, out))
elif len(self.send_queue) != 0:
out = self.send_queue.pop(0)
log.debug('(%s) _send_string(queued data=%s)' % (self.ip, out))
else:
# No data to send
log.debug('(%s) _send_string(): No data to send' % self.ip)
self.send_busy = False
return
self.send_busy = True
2014-10-15 18:18:00 +00:00
log.debug('(%s) _send_string(): Sending "%s"' % (self.ip, out))
2014-10-15 17:38:39 +00:00
log.debug('(%s) _send_string(): Queue = %s' % (self.ip, self.send_queue))
2014-10-13 16:40:58 +00:00
try:
2014-10-14 15:14:16 +00:00
self.projectorNetwork.emit(S_NETWORK_SENDING)
2014-10-13 16:40:58 +00:00
sent = self.write(out)
self.waitForBytesWritten(2000) # 2 seconds should be enough
if sent == -1:
# Network error?
self.change_status(E_NETWORK,
2014-10-13 17:09:23 +00:00
translate('OpenLP.PJLink1', 'Error while sending data to projector'))
2014-10-13 16:40:58 +00:00
except SocketError as e:
self.disconnect_from_host()
self.changeStatus(E_NETWORK, '%s : %s' % (e.error(), e.errorString()))
2014-10-06 19:10:03 +00:00
def process_command(self, cmd, data):
"""
Verifies any return error code. Calls the appropriate command handler.
"""
log.debug('(%s) Processing command "%s"' % (self.ip, cmd))
if data in PJLINK_ERRORS:
# Oops - projector error
if data.upper() == 'ERRA':
# Authentication error
2014-10-13 16:40:58 +00:00
self.disconnect_from_host()
2014-10-06 19:10:03 +00:00
self.change_status(E_AUTHENTICATION)
2014-10-13 20:16:25 +00:00
log.debug('(%s) emitting projectorAuthentication() signal' % self.ip)
2014-10-13 17:09:23 +00:00
self.projectorAuthentication.emit(self.name)
2014-10-06 19:10:03 +00:00
elif data.upper() == 'ERR1':
# Undefined command
2014-10-09 01:43:34 +00:00
self.change_status(E_UNDEFINED, '%s "%s"' %
(translate('OpenLP.PJLink1', 'Undefined command:'), cmd))
2014-10-06 19:10:03 +00:00
elif data.upper() == 'ERR2':
# Invalid parameter
self.change_status(E_PARAMETER)
elif data.upper() == 'ERR3':
# Projector busy
self.change_status(E_UNAVAILABLE)
elif data.upper() == 'ERR4':
# Projector/display error
self.change_status(E_PROJECTOR)
2014-10-15 17:22:12 +00:00
self.projectorReceivedData.emit()
2014-10-06 19:10:03 +00:00
# Command succeeded - no extra information
if data.upper() == 'OK':
log.debug('(%s) Command returned OK' % self.ip)
2014-10-15 02:57:13 +00:00
# A command returned successfully, recheck data
2014-10-15 17:22:12 +00:00
self.projectorReceivedData.emit()
2014-10-06 19:10:03 +00:00
if cmd in self.PJLINK1_FUNC:
2014-10-15 17:22:12 +00:00
self.PJLINK1_FUNC[cmd](data)
2014-10-06 19:10:03 +00:00
else:
log.warn('(%s) Invalid command %s' % (self.ip, cmd))
2014-10-15 17:22:12 +00:00
self.projectorReceivedData.emit()
2014-10-06 19:10:03 +00:00
def process_lamp(self, data):
"""
Lamp(s) status. See PJLink Specifications for format.
"""
lamps = []
data_dict = data.split()
while data_dict:
fill = {'Hours': int(data_dict[0]), 'On': False if data_dict[1] == '0' else True}
lamps.append(fill)
data_dict.pop(0) # Remove lamp hours
data_dict.pop(0) # Remove lamp on/off
self.lamp = lamps
return
def process_powr(self, data):
"""
Power status. See PJLink specification for format.
"""
if data in PJLINK_POWR_STATUS:
2014-10-15 17:22:12 +00:00
power = PJLINK_POWR_STATUS[data]
update_icons = self.power != power
self.power = power
2014-10-06 19:10:03 +00:00
self.change_status(PJLINK_POWR_STATUS[data])
2014-10-15 17:22:12 +00:00
if update_icons:
self.projectorUpdateIcons.emit()
2014-10-06 19:10:03 +00:00
else:
# Log unknown status response
log.warn('Unknown power response: %s' % data)
return
def process_avmt(self, data):
"""
Shutter open/closed. See PJLink specification for format.
"""
2014-10-15 17:22:12 +00:00
shutter = self.shutter
mute = self.mute
2014-10-06 19:10:03 +00:00
if data == '11':
2014-10-15 17:22:12 +00:00
shutter = True
mute = False
2014-10-06 19:10:03 +00:00
elif data == '21':
2014-10-15 17:22:12 +00:00
shutter = False
mute = True
2014-10-06 19:10:03 +00:00
elif data == '30':
2014-10-15 17:22:12 +00:00
shutter = False
mute = False
2014-10-06 19:10:03 +00:00
elif data == '31':
2014-10-15 17:22:12 +00:00
shutter = True
mute = True
2014-10-06 19:10:03 +00:00
else:
log.warn('Unknown shutter response: %s' % data)
2014-10-15 17:22:12 +00:00
update_icons = shutter != self.shutter
update_icons = update_icons or mute != self.mute
self.shutter = shutter
self.mute = mute
if update_icons:
self.projectorUpdateIcons.emit()
2014-10-06 19:10:03 +00:00
return
def process_inpt(self, data):
"""
Current source input selected. See PJLink specification for format.
"""
self.source = data
return
def process_clss(self, data):
"""
PJLink class that this projector supports. See PJLink specification for format.
"""
self.pjlink_class = data
log.debug('(%s) Setting pjlink_class for this projector to "%s"' % (self.ip, self.pjlink_class))
return
def process_name(self, data):
"""
Projector name set by customer.
"""
self.pjlink_name = data
return
def process_inf1(self, data):
"""
Manufacturer name set by manufacturer.
"""
self.manufacturer = data
return
def process_inf2(self, data):
"""
Projector Model set by manufacturer.
"""
self.model = data
return
def process_info(self, data):
"""
Any extra info set by manufacturer.
"""
self.other_info = data
return
def process_inst(self, data):
"""
Available source inputs. See PJLink specification for format.
"""
sources = []
check = data.split()
for source in check:
sources.append(source)
self.source_available = sources
return
def process_erst(self, data):
"""
Error status. See PJLink Specifications for format.
"""
2014-10-15 17:34:27 +00:00
try:
2014-10-15 17:38:39 +00:00
datacheck = int(data)
2014-10-15 17:34:27 +00:00
except ValueError:
# Bad data - ignore
return
2014-10-15 17:38:39 +00:00
if datacheck == 0:
2014-10-06 19:10:03 +00:00
self.projector_errors = None
else:
self.projector_errors = {}
# Fan
if data[0] != '0':
self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Fan')] = \
PJLINK_ERST_STATUS[data[0]]
# Lamp
if data[1] != '0':
self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Lamp')] = \
PJLINK_ERST_STATUS[data[1]]
# Temp
if data[2] != '0':
self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Temperature')] = \
PJLINK_ERST_STATUS[data[2]]
# Cover
if data[3] != '0':
self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Cover')] = \
PJLINK_ERST_STATUS[data[3]]
# Filter
if data[4] != '0':
self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Filter')] = \
PJLINK_ERST_STATUS[data[4]]
# Other
if data[5] != '0':
self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Other')] = \
PJLINK_ERST_STATUS[data[5]]
return
def connect_to_host(self):
"""
Initiate connection.
"""
if self.state() == self.ConnectedState:
log.warn('(%s) connect_to_host(): Already connected - returning' % self.ip)
return
self.change_status(S_CONNECTING)
self.connectToHost(self.ip, self.port if type(self.port) is int else int(self.port))
@pyqtSlot()
def disconnect_from_host(self):
"""
Close socket and cleanup.
"""
if self.state() != self.ConnectedState:
log.warn('(%s) disconnect_from_host(): Not connected - returning' % self.ip)
return
self.disconnectFromHost()
try:
self.readyRead.disconnect(self.get_data)
except TypeError:
pass
self.change_status(S_NOT_CONNECTED)
2014-10-13 16:40:58 +00:00
self.reset_information()
2014-10-06 19:10:03 +00:00
def get_available_inputs(self):
"""
Send command to retrieve available source inputs.
"""
return self.send_command(cmd='INST')
def get_error_status(self):
"""
Send command to retrieve currently known errors.
"""
return self.send_command(cmd='ERST')
def get_input_source(self):
"""
Send command to retrieve currently selected source input.
"""
return self.send_command(cmd='INPT')
def get_lamp_status(self):
"""
Send command to return the lap status.
"""
return self.send_command(cmd='LAMP')
def get_manufacturer(self):
"""
Send command to retrieve manufacturer name.
"""
return self.send_command(cmd='INF1')
def get_model(self):
"""
Send command to retrieve the model name.
"""
return self.send_command(cmd='INF2')
def get_name(self):
"""
Send command to retrieve name as set by end-user (if set).
"""
return self.send_command(cmd='NAME')
def get_other_info(self):
"""
Send command to retrieve extra info set by manufacturer.
"""
return self.send_command(cmd='INFO')
def get_power_status(self):
"""
Send command to retrieve power status.
"""
return self.send_command(cmd='POWR')
def get_shutter_status(self):
"""
2014-10-09 20:30:07 +00:00
Send command to retrieve shutter status.
2014-10-06 19:10:03 +00:00
"""
return self.send_command(cmd='AVMT')
def set_input_source(self, src=None):
"""
Verify input source available as listed in 'INST' command,
then send the command to select the input source.
"""
if self.source_available is None:
return
elif src not in self.source_available:
return
self.send_command(cmd='INPT', opts=src)
2014-10-15 17:29:15 +00:00
self.poll_loop()
2014-10-06 19:10:03 +00:00
def set_power_on(self):
"""
Send command to turn power to on.
"""
self.send_command(cmd='POWR', opts='1')
2014-10-15 17:29:15 +00:00
self.poll_loop()
2014-10-06 19:10:03 +00:00
def set_power_off(self):
"""
Send command to turn power to standby.
"""
self.send_command(cmd='POWR', opts='0')
2014-10-15 17:29:15 +00:00
self.poll_loop()
2014-10-06 19:10:03 +00:00
def set_shutter_closed(self):
"""
Send command to set shutter to closed position.
"""
self.send_command(cmd='AVMT', opts='11')
2014-10-15 17:29:15 +00:00
self.poll_loop()
2014-10-06 19:10:03 +00:00
def set_shutter_open(self):
"""
Send command to set shutter to open position.
"""
self.send_command(cmd='AVMT', opts='10')
2014-10-15 17:29:15 +00:00
self.poll_loop()