Pioneer Ethernet Plugin Help
Posted: Mon May 26, 2014 1:57 am
Alright i have been a long time lurker on these forums and found all of my answers without posting for years but I am stumped on this one.......I do not know alot about python, just what I have picked up from decyphoring code from other peoples plugins. What I am trying to do is use VoxCommando and this Pioneer Ethernet Plugin to control my VSX-1021k. I have gotten everything to work correctly except for setting my volume to a certain level. I have made a command that I can say to VoxCommando "Set receiver volume {1}" and it passes that payload to Eventghost, where I get lost is no matter what I do the Pioneer plugin dosent like the payload. I can set the action like "Pioneer AV Ethernet: Set Master Volume: 150" and it works great but using "Pioneer AV Ethernet: Set Master Volume: {eg.event.payload[0]}" nets me a "unknown" from the receiver. I have also set up a flash OSD to see if the payload is being corectly sent from VoxCommando and it is. Also tried setting and using a variable in place of {eg.event.payload[0]} and also hit a brick wall. Will someone with more python experience please be so kind as to tell me why this won't work. Thanks for your time!
Here is the code for the Pioneer Plugin:
Here is the code for the Pioneer Plugin:
Code: Select all
###
### Pioneer Ethernet
### ================
### Public Domain
###
###
### Revision history:
### -----------------
### 0.1 - initial
### 0.2 - Added: variable commands (Volume)
### parsing of received hex additional data
### standby and resume states working
### comments into code
### socket is now managed into the listener
### debug support
### 0.3 - Modified: responses from power commands
### Added: control of excepctions if receiver is disconnected
### messages indicating connection status
###
help = """\
Plugin to control Pioneer Receivers via Ethernet.
Note that not all commands work with every model."""
eg.RegisterPlugin(
name = "Pioneer AV Ethernet",
author = "sergiensergien (based on Onkyo development by prostetnic, Bartman, Fiasco, FoLLgoTT)",
version = "0.3." + "$LastChangedRevision: 1246 $".split()[1],
kind = "external",
guid = "{50e42894-579e-4ae2-9d9e-3b67c2c338cf}",
description= "Control Pioneer A/V Receivers via Ethernet",
help = help,
canMultiLoad = True,
createMacrosOnAdd = True,
icon = (
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAA"
"AAd0SU1FB9YDBAsPCqtpoiUAAAAWdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCAyLjZsqHS1"
"AAADe0lEQVQ4T02TXVCUZRSAPyZsxpnqoovum+miq+6qFUJYQP7cogS2VpGFAAlKpyAC"
"EVh+4k80WGADQWlxZRfZACH5ECVY5TdHiEbAIQVkHRzUCZK/ZWGXp10SxjPzXrzvvM9z"
"zpw5xwUQXg7ndX1jw3VkYmrzycK/gusrgrB3z17hvXfffv2tN99YcXHEi///g07Bznkw"
"+9i1yvAbqvJKolRJhJ2KQp4ZQWTWd5wsV9Ni6sWybn3tZWYXvjt531V1tgJF8lcEZbij"
"MLgRbTpAVHcgyk5/QnSefHrmc7QdV1hbX9+VbAvuTk67ZpfVIDsWgu/ZfURfD+TElVBy"
"m4+TooshvDaAL697EPOHLzK1L9prbayuWbYlwsLzFZfCql+Qx8XhqfoAZUsIhsEGLGsW"
"Nm12FlZtTM0/w2DSEmn0dEgclRSFcFG8wcamTRAqtO0kpRficdQLpV5Gr7mf3qFHzC1u"
"YF61MvrIztD0OsP3F6m/ZUDZ7klosxfxJZn0j04hpJ+uIvZ4EpJvJKhvFWOzW1lc2qT/"
"zznuzdnoGnlG+8BDWnvMDE7M8q1eQcygN6GFkWj0nQgp+WUcilTyUaIHzRPtWKx2rLYt"
"5hfXHBnMNIjDtHbfY3RmmcdLdkrEfKIHDyDLPYS6vhnhh9wSgiPCcYvbT+3gNQbGluib"
"WOTG8EOaTGOY/nrKwPgynXdm+H3gCZkNeQ6BP0GnZGgMRoTUnDMcSfgaiUJKWuPP6MRx"
"LhjvUN3Qh671NnVNQ1y9Oe2YgUmaeqY4WnMEpSgl5ORhzhmaEFRFpSTnOpool+GXFoa6"
"sQNNXReVupucv9zHeWM3Gl0HNY0mci7VEtF2EJ+8fSTkpSP23EaYmpl1ySgowk8ehiTc"
"i6iiExRcNKC+0IlG20m5XqRCK5J9qYxoMZSgail+scEUn6vn+bJFImxtbQkl5dWoCkvx"
"Cg3k/Uh3grKCSahJJln3E8n1BSQ2JhArHuSTUgneET6kFWno6h3GyW5P4sqqRUhMLSCn"
"uJQA+cdIFYF8GC3F/Xs3vDM8kaZ4sP+YD/4RX5B+Wk1l3SXnkG2zu7uwZrHuqa1rILuw"
"hNSsfOSxsQQoPkN2WI4iPp7EzBx+dPRLb7zK0orl1Z2FEmwvTM4HZ0kzZvM7hqZfMbaJ"
"jEw8YHh8kvrGFmr1euaf/iPZAW12uzD2t1n4DwtSpLoLWTYZAAAAAElFTkSuQmCC"
),
)
import socket
import asynchat
import asyncore
import re
import binascii
import itertools
import operator
import time
import eg
cmdList = (
('Power / Sleep', None, None, None, None),
('Power.Off', 'Power Off', 'PF', None, 'PWR1' ),
('Power.On', 'Power On', 'PO', None, 'PWR0' ),
('Master Volume', None, None, None, None),
('Volume.Up', 'Master Volume Up', 'VU', None, None ),
('Volume.Down', 'Master Volume Down', 'VD', None, None ),
('Volume.Set', 'Set Master Volume (000-200)', 'VL', '000-200', None ),
('Mute.On', 'Mute On', 'MF', None, 'MUT0' ),
('Mute.Off', 'Mute Off', 'MO', None, 'MUT1' ),
('Input Select Command', None, None, None, None),
('Input.DVD', 'DVD', '04FN', None, 'FN04' ),
('Input.BD', 'BD', '25FN', None, 'FN25' ),
('Input.TV/SAT', 'TV / SAT', '05FN', None, 'FN05' ),
('Input.DVR/BRD', 'DVR / BRD', '15FN', None, 'FN15' ),
('Input.Video', 'Video', '10FN', None, 'FN10' ),
('Input.HDMI1', 'HDMI 1', '19FN', None, 'FN19' ),
('Input.HDMI2', 'HDMI 2', '20FN', None, 'FN20' ),
('Input.HDMI3', 'HDMI 3', '21FN', None, 'FN21' ),
('Input.HDMI4', 'HDMI 4', '22FN', None, 'FN22' ),
('Input.HDMIFront', 'HDMI Front', '23FN', None, 'FN23' ),
('Input.iRadio', 'Internet Radio', '26FN', None, 'FN26' ),
('Input.IPOD/USB', 'iPOD / USB', '17FN', None, 'FN17' ),
('Input.XMRadio', 'XM Radio', '18FN', None, 'FN18' ),
('Input.CD', 'CD', '01FN', None, 'FN01' ),
('Input.CDR/Tape', 'CD-R / Tape', '03FN', None, 'FN03' ),
('Input.Tuner', 'Tuner', '02FN', None, 'FN02' ),
('Input.Adapter', 'Adapter', '33FN', None, 'FN33' ),
('Input.Sirius', 'Sirius', '27FN', None, 'FN27' ),
('Input.HDMI.Cycle', 'HDMI Input (Cycle)', '31FN', None, None ),
('Input.Up', 'Next Input', 'FU', None, None ),
('Input.Down', 'Previous Input', 'FD', None, None ),
('Stereo Front Stage Mode', None, None, None, None),
('Stereo.Cycle', 'Stereo Mode(Cycle)', '0001SR', None, None ),
('Stereo.Stereo', 'Stereo', '0009SR', None, 'LM0001'),
('Stereo.FrontStageWide', 'Front Stage Wide', '0004SR', None, 'LM0003'),
('Stereo.FrontStageNarrow', 'Front Stage Narrow', '0003SR', None, 'LM0002'),
('Stream Direct Mode', None, None, None, None),
('Stream.Cycle', 'Stream Direct Mode (Cycle)', '0005SR', None, None ),
('Stream.Direct', '2 Channel', '0007SR', None, 'LM0601'),
('Stream.PureDirect', '2 Channel', '0008SR', None, 'LM0701'),
('Stream.ALC', '2 Channel', '0151SR', None, 'LM0501'),
('Stream.Auto Surround', '2 Channel', '0006SR', None, 'LM0401'),
('Surround Mode', None, None, None, None),
('Surround.Cycle', 'Dolby Surround Mode (Cycle)', '0010SR', None, None),
('Surround.ProLogic', 'Dolby ProLogic', '0012SR', None, 'LM0107'),
('Surround.PLIIxMovie', 'Dolby ProLogic IIx Movie', '0013SR', None, 'LM0102'),
('Surround.PLIIxMusic', 'Dolby ProLogic IIx Music', '0014SR', None, 'LM0104'),
('Surround.PLIIxGame', 'Dolby ProLogic IIx Game', '0015SR', None, 'LM0106'),
('Surround.Neo6Cinema', 'Neo:6 Cinema', '0016SR', None, 'LM0108'),
('Surround.Neo6Music', 'Neo:6 Music', '0017SR', None, 'LM0109'),
('Surround.PLIIz', 'Dolby ProLogic IIz Height (*)', None , None, None ),
('Surround.WideSrndMovie', 'Wide Surround Movie (*)', None , None, None ),
('Surround.WideSrndMusic', 'Wide Surround Music (*)', None , None, None ),
('Surround.NeuralSrnd', 'Neural Surround (*)', None , None, None ),
('Surround.DolbyDigitalEX', 'Dolby Digital EX (*)', None , None, None ),
('Surround.DTS.ES', 'DTS ES (*)', None , None, None ),
('Surround.DTS.Neo6', 'DTS Neo:6 (*)', None , None, None ),
('Surround.NeuralSrnd', 'Neural Surround (*)', None , None, None ),
('Advanced Surround', None, None, None, None),
('Advanced.Cycle', 'Wide Surround Movie', '0100SR', None, None ),
('Advanced.Action', 'Adv. Surround: Action', '0101SR', None, 'LM0201'),
('Advanced.Sci-Fi', 'Adv. Surround: Sci-Fi', '0102SR', None, 'LM0203'),
('Advanced.Drama', 'Adv. Surround: Drama', '0103SR', None, 'LM0202'),
('Advanced.Show', 'Adv. Surround: Show', '0104SR', None, 'LM0202'),
('Advanced.MonoFilm', 'Adv. Surround: Mono Film', '0105SR', None, 'LM0205'),
('Advanced.Expanded', 'Adv. Surround: Expanded', '0106SR', None, 'LM0204'),
('Advanced.Classical', 'Adv. Surround: Classical', '0107SR', None, 'LM0206'),
('Advanced.Unplugged', 'Adv. Surround: Unplugged', '0109SR', None, 'LM020a'),
('Advanced.Pop/Rock', 'Adv. Surround: Pop/Rock', '0110SR', None, 'LM020c'),
('Advanced.ExpStereo', 'Adv. Surround: Exp. Stereo', '0111SR', None, 'LM020b'),
('Advanced.TVSurround', 'Adv. Surround: TV Surround', '0112SR', None, 'LM020d'),
('Advanced.AdvancedGame', 'Adv. Surround: Advanced Game', '0116SR', None, 'LM0207'),
('Advanced.Sports', 'Adv. Surround: Sports', '0118SR', None, 'LM0208'),
('Advanced.PhonesSrnd', 'Adv. Surround: Phones Srnd. (*)', None, None, None ),
('State Queries', None, None, None, None),
('Power.State', 'Returns Power State', '?P', None, 'PWR' ),
('Volume.State', 'Returns Master Volume level', '?V', None, 'VOL' ),
('Mute.State', 'Returns Mute State', '?M', None, 'MUT' ),
('Input.State', 'Returns Current Input', '?F', None, None ),
('Mode.State', 'Returns Listening Mode', '?S', None, None ),
('Listening.Info', 'Returns Listening Info', '?L', None, None ),
(None, None, None, None, None),
)
class PioneerEventer(asynchat.async_chat):
"""Pioneer engine class. Implements command line user interface."""
### Initializing all the variables and open the tcp connection..
###
def __init__(self, host, port, plugin):
asynchat.async_chat.__init__(self)
self.set_terminator("\x0d\x0a")
self.reading_headers = False
self.data = ""
self.host = host
self.port = port
self.plugin = plugin
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.socket.connect((self.host, self.port))
connected = True
except socket.error, exc:
print 'PIONEER: Connect fail:', (self.host, self.port)
connected = False
if connected == True:
asynchat.async_chat.__init__(self, sock=self.socket)
self.plugin.TriggerEvent("connected")
eg.RestartAsyncore()
### This will be run if the connections is succesful
###
def handle_connect(self):
# connection succeeded
connected = True
print "PIONEER: Listening to device started"
### This will be run if the tcp connection is disconnected
### or if we want to close it ourselves.
###
def handle_close(self):
print "PIONEER: Listener Close"
# self.handler.reader = None
connected = False
self.plugin.TriggerEvent("disconnected")
self.close()
### This will be run if there is a problem opening the connection
###
def handle_expt(self):
# connection failed
# if eg.debugLevel:
eg.PrintTraceback()
connected = False
print "PIONEER: Listener Failed"
self.plugin.TriggerEvent("disconnected")
self.close()
### This gets run whenever asynchat detects there is data waiting
### for us to be read at the socket, to put it in our buffer
###
def collect_incoming_data(self, data):
# received a chunk of incoming data
self.data = self.data + data
if eg.debugLevel:
print "PIONEER (Debug): Eventer Collecting"
### This gets run whenever asynchat detects there is data waiting
### for us to be examined, so this is where it's all at..
###
def found_terminator(self):
# got a response line
data = self.data
self.data = ""
if eg.debugLevel:
print "PIONEER (Debug): Received " + str(data)
### The PIONEER device sends a keep-alive consisting only in the terminator
### So if "data" is not empty, we want to process it.
if len(data):
### First we search through our known fixed responses for a match
response = operator.itemgetter(4)
try:
datum = map(response, cmdList).index(str(data))
self.plugin.TriggerEvent("Received." + cmdList[datum][0], None)
except ValueError:
### If not fixed match, it can be a long info message ...
if len(data) > 6:
print "PIONEER: Says " + data[:4] + " : " + binascii.unhexlify(data[4:])
### Or, perhaps, it's a big hole in our knowledge of Pioneer devices ...
else:
self.plugin.TriggerEvent("Received.Unknown", str(data))
else:
if eg.debugLevel:
print "PIONEER (Debug): Keep alive"
### Sends data to our device when called (with no additional argument)
###
class CmdAction(eg.ActionClass):
"""Base class for all argumentless actions"""
def __call__(self):
if self.plugin.eventer == 0:
self.plugin.InitConnection()
if self.plugin.eventer.connected == True:
datum = self.plugin.eventer.socket.send(str(self.cmd) + '\x0d')
if eg.debugLevel:
print "PIONEER (Debug): Send (" + repr(datum) + ") : " + str(self.cmd)
else:
self.plugin.TriggerEvent("disconnected")
### Sends data to our device when called (with additional argument)
###
class ValueAction(eg.ActionWithStringParameter):
"""Base class for all actions with adjustable argument"""
def __call__(self, data):
if self.plugin.eventer == 0:
self.plugin.InitConnection()
if self.plugin.eventer.connected == True:
datum = self.plugin.eventer.socket.send(str(data) + str(self.cmd) + '\x0d')
if eg.debugLevel:
print "PIONEER (Debug): Send (" + repr(datum) + ") : " + str(self.cmd) + " = " + data
else:
self.plugin.TriggerEvent("disconnected")
### Sends RAW data to our device when called (with no additional argument)
###
class Raw(eg.ActionWithStringParameter):
name = 'Send Raw command'
def __call__(self, data):
if self.plugin.eventer == 0:
self.plugin.InitConnection()
if self.plugin.eventer.connected == True:
datum = self.plugin.eventer.socket.send(str(data) + '\x0d')
if eg.debugLevel:
print "PIONEER (Debug): RAW Send (" + repr(datum) + ") : " + str(data)
else:
self.plugin.TriggerEvent("disconnected")
### The EventGhost classes and functions are over here..
###
###
class PioneerEthernet(eg.PluginClass):
### Initialization of the plugin.
### We load all the commands from the command table.
###
def __init__(self):
self.serial = None
group = self
for cmd_name, cmd_text, cmd_cmd, cmd_rangespec, cmd_answer 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)
elif cmd_rangespec is not None:
# Command with argument
actionName, paramDescr = cmd_text.split("(")
actionName = actionName.strip()
paramDescr = paramDescr[:-1]
minValue, maxValue = cmd_rangespec.split("-")
class Action(ValueAction):
name = actionName
cmd = cmd_cmd
parameterDescription = "Value: (%s)" % paramDescr
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)
def __start__(self, host, port):
self.host = host
self.port = port
self.InitConnection()
def __stop__(self):
if self.eventer:
self.eventer.close()
self.eventer = None
return
### Listener Initialization. It is called when the plugin starts and
### when the system cames back from a suspended state.
###
def InitConnection(self):
try:
self.eventer = PioneerEventer(self.host, self.port, self)
except socket.error, exc:
self.eventer = 0
raise self.Exception(exc[1])
#Send useful Queries to start macros if needed
if self.eventer.connected != 0:
datum = self.eventer.socket.send('?M\x0d')
datum = self.eventer.socket.send('?F\x0d')
datum = self.eventer.socket.send('?L\x0d')
datum = self.eventer.socket.send('?S\x0d')
### When we suspend the systems we close down the connection
### to the device.
###
def OnComputerSuspend(self, suspendType):
if self.eventer:
self.eventer.handle_close()
print "PIONEER: Disconnected from the PIONEER device!"
self.eventer = None
### When we resume from a suspended state we have to wait for the
### interfaces to came back in line and restablish the connections ...
### the waiting time can be up to 20 secs in wireless enviroment.
###
def OnComputerResume(self, suspendType):
print "PIONEER: Resuming connection to PIONEER device"
time.sleep(20)
self.InitConnection()
### Plugin configuration.
### Change "host" with the IP of the PIONEER device.
###
def Configure(self, host="192.168.2.100", port=8102):
panel = eg.ConfigPanel()
hostCtrl = panel.TextCtrl(host)
portCtrl = panel.SpinIntCtrl(port, max=65535)
panel.AddLine("Host:", hostCtrl)
panel.AddLine("Port:", portCtrl)
while panel.Affirmed():
panel.SetResult(
hostCtrl.GetValue(),
portCtrl.GetValue()
)