Code: Select all
#
# CambridgeAudioAzur540RSerial V0.1
# ================
# Based on work by Pavel Ivanov aka Johnson, <[email protected]>
# Public Domain
#
# Revision history:
# -----------------
# 0.1 - (23.12.11) initial
import eg
help = '''\
Plugin based on Pavel Ivanov's plugin for the 840A adapted for the Azur 340R/540R.
'''
eg.RegisterPlugin(
name = 'CambridgeAudio Azur 340R/540R Serial',
author = 'cafe.racer',
version = '0.1',
kind = 'external',
description = 'Control Cambridge Audio 340R/540R via RS232',
help = help,
canMultiLoad = True,
createMacrosOnAdd = True,
)
import threading
import time
import re
class conf:
FirstVolumeRepeatPause = 0.3
NextVolumeRepeatPause = 0.110
class current:
Volume = 45
Mute = False
cmdList = (
('Power', None, None, None),
('PowerOn', 'Power On', '1,01,1', None),
('PowerOff', 'Power Off', '1,01,0', None),
('Volume', None, None, None),
('MuteOff', 'Mute Off', '1,12,0', None),
('MuteOn', 'Mute On', '1,12,1', None),
('VolumeGoto', 'Goto Volume x (00-50, 50=0dB)', '1,13,x', '00-50'),
('VolumeUp', 'Volume Up', '1,02', None),
('VolumeDown', 'Volume Down', '1,03', None),
('Parameters', None, None, None),
('BassUp', 'Bass Up', '1,04', None),
('BassDown', 'Bass Down', '1,05', None),
('TrebleUp', 'Treble Up', '1,06', None),
('TrebleDown', 'Treble Down', '1,07', None),
('SubOn', 'Sub On', '1,08', None),
('SubOff', 'Sub Off', '1,09', None),
('SetLFEtrim', 'Set LFE trim (0 = 0db, 1 = -1db to -10 = -10dB)', '1,10,x', '00-10'),
('MuteOn', 'Mute On', '1,11,01', None),
('MuteOff', 'Mute Off', '1,11,00', None),
('DynamicRangeSet', 'Dynamic Range Set (0 - 0/4, 1 - 1/4, 2 - 2/4, 3 - 3/4, 4 - 4/4)', '1,12,x', '00-04'),
('Sources', None, None, None),
('InputSelect', 'Input Select (1 - DVD, 2 - Video 1, 3-Tuner, 4 - Video2, 5 - Video3, 6 - Tape/MD/CDR, 7 - CD/Aux, 8 - 6.1 Direct In)', '2,01,x', '01-08'),
('InputDVD', 'DVD', '2,01,01', None),
('InputVideo1', 'Video 1', '2,01,02/r', None),
('InputVideo2', 'Video 2', '2,01,04/r', None),
('InputVideo3', 'Video 3', '2,01,05', None),
('InputTuner', 'Tuner', '2,01,03', None),
('InputTape', 'Tape/MD/CDR', '2,01,06', None),
('InputCD', 'CD/Aux', '2,01,07', None),
('InputDirect', '6.1 Direct In', '2,01,08', None),
('InputUp', 'Next Source Up', '2,02', None),
('InputDown', 'Next Source Down', '2,03', None),
('InputMode', 'Set Input Mode (00=Analogue 01=Digital)', '2,04,x', '00-01'),
('Audio Processing', None, None, None),
('SetAudiotoStereo', 'Set Audio to Stereo', '4,01,00', None),
('SetAudiotoStereoSub', 'Set Audio to Stereo + Sub', '4,01,01', None),
('SetPLIINeo', 'Set PLII/Neo', '4,02', None),
('SetDDDTS', 'Set DD/DTS', '4,03', None),
('GetCurrentDSPmode', 'Get Current PLII/Neo/DSP mode', '4,04', None),
('GetcurrentDDDTSmode', 'Get current DD/DTS mode', '4,05', None),
('SetDSPCycles', 'Set DSP Cycles', '4,06', None),
('Version', None, None, None),
('SoftwareVersionGet', 'Get Software Version', '5,01,', None),
('ProtocolVersionGet', 'Get Protocol Version', '5,02,', None),
('Tuner', None, None, None),
('TunerStereo', 'Tuner Stereo', '3,01,00', None),
('TunerMono', 'Tuner Mono', '3,01,01', None),
('FrequencyUp', 'Frequency Up', '3,02', None),
('FrequencyDown', 'Frequency Down', '3,03', None),
('Up', 'Up', '3,04', None),
('Down', 'Down', '3,05', None),
('TunerMode', 'Tuner Mode', '3,06', None),
('TunerSearchStop', 'Tuner Search Stop', '3,07', None),
('TunerAutoSearch', 'Tuner Auto Search', '3,08', None),
('TunerBand', 'Tuner Band', '3,09', None),
('TunerPTYSelect', 'Tuner PTY Select', '3,10', None),
('StoreStation', 'Store Station', '3,11', None),
('GetPreset', 'Get Preset', '3,12', None),
('TunerDisplyMode', 'Tuner Disply Mode', '3,13', None),
('GetFrequency', 'Get Frequency', '3,14', None),
('GetStationName', 'Get Station Name', '3,15', None),
('GetPTY', 'Get PTY', '3,16', None),
('StopPTYSelect', 'Stop PTY Select', '3,17', None),
(None,None,None,None),
)
EventList = {
'11':{
'content':'Error',
'01':'Command Group Unknown',
'02':'Command Number in Group Unnown',
'03':'Command Data Error'
},
'6':{
'content':'Amplifier',
'01':'Power State',
'02':'Volume Up',
'03':'Volume Down',
'04':'Bass Up',
'05':'Bass Down',
'06':'Treble Up',
'07':'Treble Down',
'08':'Sub On',
'09':'Sub Off',
'10':'Set LFE trim',
'11':'Mute On State',
'12':'Dynamic Range Set',
'13':'OSD On',
'14':'OSD Off',
'15':'OSD Up',
'16':'OSD Down',
'17':'OSD Left',
'18':'OSD Right',
'19':'OSD Enter'
},
'7':{
'content':'Source',
'01':'Input Select',
'02':'Next Input Up',
'03':'Next Input Down',
'04':'Input Mode'
},
'8':{
'content':'Tuner',
'01':'Tuner Stereo/Mono',
'02':'Frequency Up',
'03':'Frequency Down',
'04':'Up',
'05':'Down',
'06':'Tuner Mode',
'07':'Tuner Search Stop',
'08':'Tuner Auto Search',
'09':'Tuner Band',
'10':'Tuner PTY Select',
'11':'Store Station',
'12':'Get Preset',
'13':'Tuner Diaply Mode',
'14':'Get Frequency',
'15':'Get Station Name',
'16':'Get PTY',
'17':'Stop PTY Select'
},
'9':{
'content':'Audio Processing',
'01':'Set Audio to Stereo',
'02':'Set PLII/Neo/DSP',
'03':'DD/DTS',
'04':'Get current PLII/Neo/DSP mode',
'05':'Get current DD/DTS Mode'
},
'10':{
'content':'Version',
'01':'Software Version',
'02':'Protocol Version'
}
}
class CmdAction(eg.ActionClass):
'''Base class for all argumentless actions'''
def __call__(self):
self.plugin.serialThread.SuspendReadEvents()
self.plugin.serialThread.Write('#' + self.cmd + chr(13))
self.plugin.serialThread.ResumeReadEvents()
class ValueAction(eg.ActionWithStringParameter):
'''Base class for all actions with adjustable argument'''
def __call__(self, data):
self.plugin.serialThread.SuspendReadEvents()
self.plugin.serialThread.Write('#' + self.cmdLeft + str(data) + self.cmdRight + chr(13))
self.plugin.serialThread.ResumeReadEvents()
class Raw(eg.ActionWithStringParameter):
name = 'Send Raw command'
def __call__(self, data):
self.plugin.serialThread.SuspendReadEvents()
self.plugin.serialThread.Write(str(data) + chr(13))
self.plugin.serialThread.ResumeReadEvents()
class VolumeAction(eg.ActionClass):
def __call__(self):
self.VolumeThread = threading.Thread(
target=self.VolumeThreadLoop,
name="VolumeThread",
args=(eg.event, current.Volume, self.increment,)
)
self.VolumeThread.start()
def VolumeThreadLoop(self, event, vol, increment):
firstLoop = True
while not event.isEnded:
vol += increment
if (vol < 1) or (vol > 96): break
self.plugin.serialThread.SuspendReadEvents()
self.plugin.serialThread.Write('#1,02,' + format(vol, '02d') + chr(13))
self.plugin.serialThread.ResumeReadEvents()
if firstLoop:
time.sleep(conf.FirstVolumeRepeatPause)
firstLoop = False
else:
time.sleep(conf.NextVolumeRepeatPause)
class ToggleMuteAction(eg.ActionClass):
def __call__(self):
self.plugin.serialThread.SuspendReadEvents()
self.plugin.serialThread.Write('#1,11,' + str(int(not current.Mute)) + chr(13))
self.plugin.serialThread.ResumeReadEvents()
class CambridgeAudioSerial(eg.PluginClass):
def __init__(self):
self.serialThread = eg.SerialThread()
group = self
for cmd_name, cmd_text, cmd_cmd, cmd_rangespec in cmdList:
if cmd_text is None:
# New subgroup, or back up
if cmd_name is None:
group = self
else:
group = self.AddGroup(cmd_name)
if cmd_name == 'Volume': groupVolume = group
elif cmd_rangespec is not None:
# Command with argument
actionName, paramDescr = cmd_text.split('(')
actionName = actionName.strip()
paramDescr = paramDescr[:-1]
_cmdLeft, _cmdRight = cmd_cmd.split('x')
class Action(ValueAction):
name = actionName
cmd = cmd_cmd
parameterDescription = 'Value: (%s)' % paramDescr
cmdLeft = _cmdLeft
cmdRight = _cmdRight
Action.__name__ = cmd_name
group.AddAction(Action)
else:
# Argumentless command
class Action(CmdAction):
name = cmd_text
cmd = cmd_cmd
Action.__name__ = cmd_name
group.AddAction(Action)
group.AddAction(Raw)
class Action(VolumeAction):
name = 'Volume Up until event end'
increment = 1
Action.__name__ = 'VolumeUpUntilEnd'
groupVolume.AddAction(Action)
class Action(VolumeAction):
name = 'Volume Down until event end'
increment = -1
Action.__name__ = 'VolumeDownUntilEnd'
groupVolume.AddAction(Action)
class Action(ToggleMuteAction):
name = 'Toggle Mute'
Action.__name__ = 'ToggleMute'
groupVolume.AddAction(Action)
def __start__(self, port):
self.port = port
self.serialThread.parmre = re.compile('#(\d),(\d\d),(([^,]+)?(,(.*))?)??$')
self.serialThread.SetReadEventCallback(self.OnReceive)
self.serialThread.Open(port, 9600, '8N1')
self.serialThread.SetRts(0)
self.serialThread.Start()
current.Volume = 45
def __stop__(self):
self.serialThread.Close()
def Configure(self, port=0):
panel = eg.ConfigPanel(self)
portCtrl = panel.SerialPortChoice(port)
panel.AddLine('Port:', portCtrl)
while panel.Affirmed():
panel.SetResult(portCtrl.GetValue())
def OnReceive(self, serial):
buffer = ''
while True:
ch = serial.Read(1, 0.1)
if (ch == ''): return # nothing received inside timeout, possibly indicates erroneous data
if (ch != '\r'):
buffer += ch
continue
m = self.serialThread.parmre.match(buffer)
if (m is not None) and (m.group(1) in EventList) and (m.group(2) in EventList[m.group(1)]):
if (EventList[m.group(1)][m.group(2)] == 'VolumeChanged'):
current.Volume = int(m.group(3))
elif (EventList[m.group(1)][m.group(2)] == 'MuteStateChanged'):
current.Mute = bool(int(m.group(3)))
self.TriggerEvent(EventList[m.group(1)]['content'] + '.' + EventList[m.group(1)][m.group(2)], payload = m.group(3))
else:
self.TriggerEvent('Unknown(' + buffer + ')')
return