Difference between revisions of "ESS Expandable Speaker Selector Switch"

From LinuxMCE
Jump to: navigation, search
(Added category: GSD)
 
(12 intermediate revisions by 6 users not shown)
Line 1: Line 1:
 +
[[Category: Hardware]]
 +
[[Category: Audio]]
 +
[[Category: GSD]]
 +
{{versioninfo}}
 
{| align="right"
 
{| align="right"
 
   | __TOC__
 
   | __TOC__
 
   |}
 
   |}
  
[[Category: Hardware]]
 
[[Category: Audio]]
 
[[Category:Tutorials]]
 
 
In editing phase !!! - working copy !!!
 
  
 
[[Image:Ess2468.gif]]
 
[[Image:Ess2468.gif]]
Line 19: Line 18:
 
There is simple RS232 protocol for this device, described [http://hacs.com/ess_commands.php?PHPSESSID=5849f9d4952a3863fd68a4d2a507123c here] It's pretty simple and ASCII based..
 
There is simple RS232 protocol for this device, described [http://hacs.com/ess_commands.php?PHPSESSID=5849f9d4952a3863fd68a4d2a507123c here] It's pretty simple and ASCII based..
  
The whole idea of implementation is to have each zone represented by "Zone" child devices (template #1867), adding as much as you have them in your device. By default zone "0" will be added to parent device (remember that zone affects all zones at the same time) and 1 additional ordinary zone - I assume there will be at least one zone in system. Then you add more zones to suit your system.
+
The whole idea of implementation is to have each zone represented by "Zone" child devices (template #1867), adding as much as you have them in your device. By default zone "0" will be represented by device it self - so sending commands to parent device will have effect on all zones. Sending commands only to Zone child devices, will have effect only in target zone. At the start, 1 child zone is added to parent device - I assume there will be at least one zone in system. Then you add more zones to suit your system.
  
 
    
 
    
Line 65: Line 64:
 
==== 'Add Ruby snippets to commands'====  
 
==== 'Add Ruby snippets to commands'====  
  
I've created new IR/Codeset group called "ESS Speaker Selector RS232", although I'm not sure how to handle it. Have also edited coresponding Ruby snippets for each command. Basically those are just simple strings that get sent on rs232 (they differ mainly on target zone) to take proper action on device according to received command from LinuxMCE... There are also a lot of other functions that help :
+
I've created new IR/Codeset group called "ESS Speaker Selector RS232", although I'm not sure how to handle it. Have also edited corresponding Ruby snippets for each command. Basically those are just simple strings that get sent on rs232 (they differ mainly on target zone) to take proper action on device according to received command from LinuxMCE...  
- determining for which child zone command is targeted
+
- to "normalize" volume levels between Marantz's internal dB units and range of 0-100
+
 
+
 
+
Simpler commands from Ruby part of template :
+
<pre><nowiki>
+
 
+
  Power
+
#193 Off
+
#SendMultiZoneCommand(cmd, ["@PWR:1", "@MSP:1", "@MAM:2"]) SendMultiZonePowerOffCommand(cmd, ["@AMT:2", "@MSP:1", "@MAM:2"])
+
+
#192 On
+
#SendMultiZoneCommand(cmd, ["@PWR:2", "@MSP:2", "@MAM:1"]) SendMultiZonePowerOnCommand(cmd, ["@AMT:1", "@MSP:2", "@MAM:1"])
+
+
#194 Toggle Power
+
+
+
Inputs
+
#169 AM
+
SendMultiZoneCommand(cmd, ["@SRC:H", "@MSC:H", "@MSC:H"])
+
+
#164 Aux
+
SendMultiZoneCommand(cmd, ["@SRC:9", "@MSC:9", "@MSC:9"])
+
+
#172 Aux 2
+
SendMultiZoneCommand(cmd, ["@SRC:A", "@MSC:A", "@MSC:A"])
+
+
#162 CD
+
SendMultiZoneCommand(cmd, ["@SRC:C", "@MSC:C", "@MSC:C"])
+
+
#420 CDR
+
SendMultiZoneCommand(cmd, ["@SRC:D", "@MSC:D", "@MSC:D"])
+
+
#178 DSS
+
SendMultiZoneCommand(cmd, ["@SRC:4", "@MSC:4", "@MSC:4"])
+
+
#165 DVD
+
SendMultiZoneCommand(cmd, ["@SRC:2", "@MSC:2", "@MSC:2"])
+
+
#170 FM
+
SendMultiZoneCommand(cmd, ["@SRC:G", "@MSC:G", "@MSC:G"])
+
+
#160 Tape / Tape 1
+
SendMultiZoneCommand(cmd, ["@SRC:E", "@MSC:E", "@MSC:E"])
+
+
#166 Tuner
+
SendMultiZoneCommand(cmd, ["@SRC:F", "@MSC:F", "@MSC:F"])
+
+
#161 TV
+
SendMultiZoneCommand(cmd, ["@SRC:1", "@MSC:1", "@MSC:1"])
+
+
#282 VCR-1
+
SendMultiZoneCommand(cmd, ["@SRC:3", "@MSC:3", "@MSC:3"])
+
+
DSP Modes (Audio)
+
#891 All Ch Stereo
+
#<$"@SUR:0H\r"$> SendIrCommand("@SUR:0H")
+
+
+
#97 Mute
+
SendMultiZoneCommand(cmd, ["@AMT:0", "@MSM:0", "@MAM:0"])
+
+
+
#90 Vol Down
+
SendMultiZoneCommand(cmd, ["@VOL:2", "@MSV:2", "@MVL:2"])
+
+
#89 Vol Up
+
SendMultiZoneCommand(cmd, ["@VOL:1", "@MSV:1", "@MVL:1"])
+
+
Speed Ctrl
+
#125 Scan Back/Rewind
+
</nowiki></pre>
+
  
  
Line 143: Line 70:
  
 
<pre><nowiki>
 
<pre><nowiki>
###########################################################3 Internal Methods
+
Power
# To do list :
+
#193 Off
# - parsing of input messages (inputs,mute,volume,power)
+
SendZoneCommand(cmd)
# - handling of tuner (with live/TV child device)
+
# - to properly send events and set device current volume data
+
  
def DecodeReceivedInfo_candidate(response)
+
#192 On
# Examples: Mute/On/Off: @AMT:1,@MSP:2,@MAM:2,  Volume: @VOL:-25,@MSV:-56,@MVL:-56  Inputs:@MSC:2E,@SRC:22,@SRC:2E
+
SendZoneCommand(cmd)
  log("DecodeReceivedInfo1: " + response + "\n");
+
  avrcommand = response[1..3]
+
  avrvalue = response[5]
+
  log("DecodeReceivedInfo2: " + response + " -> Command:"+ avrcommand.to_s + " Value: " + avrvalue.to_s + "\n");
+
  
  case avrcommand
+
Inputs
# Input Source changed
+
#953 A
    when 'SRC'
+
SendZoneCommand(cmd)
    log("Main Zone Input Changed to "+ avrvalue.to_s+ "\n");
+
    input_param=0
+
   
+
    case avrvalue
+
    when '1'
+
    log('Main Zone Input Changed to #161: TV')
+
    input_param=161
+
    temp_state="Input: TV"
+
    when '2'
+
    log('Main Zone Input Changed to #165: DVD')
+
    input_param=165
+
    temp_state="Input: TV"
+
    when '3'
+
    log('Main Zone Input Changed to #282: VCR-1')
+
    input_param=282
+
    temp_state="Input: TV"
+
    when '4'
+
    log('Main Zone Input Changed to #178: DSS')
+
    input_param=178
+
    temp_state="Input: TV"
+
    when '9'
+
    log('Main Zone Input Changed to #164: AUX')
+
    input_param=164
+
    temp_state="Input: TV"
+
    when 'A'
+
    log('Main Zone Input Changed to #171: AUX2')
+
    input_param=171
+
    temp_state="Input: TV"
+
    when 'C'
+
    log('Main Zone Input Changed to #162: CD')
+
    input_param=162
+
    temp_state="Input: TV"
+
    when 'D'
+
    log('Main Zone Input Changed to #420: CDR')
+
    input_param=420
+
    temp_state="Input: TV"
+
    when 'E'
+
    log('Main Zone Input Changed to #160: TAPE')
+
    input_param=160
+
    temp_state="Input: TV"
+
    when 'F'
+
    log('Main Zone Input Changed to #166: TUNER')
+
    input_param=166
+
    temp_state="Input: TV"
+
    when 'G'
+
    log('Main Zone Input Changed to #170: FM')
+
    input_param=160
+
    temp_state="Input: TV"
+
    when 'H'
+
    log('Main Zone Input Changed to #169: AM')
+
    input_param=169
+
    temp_state="Input: TV"
+
    end
+
#    device_id = device_.mapPortChannel_Device_.[0.to_s]
+
log("======= Determiming device ID for Zone 0 : " + device_id.to_s + "\n")
+
device_.mapPortChannel_Device_.each do |key, value|
+
log(key.to_s + " => " + value.to_s+ "\n")
+
end
+
    cmd = Command.new(device_id, -1001, 1, 2, 49);
+
    cmd.params_[41] = input_param.to_s();
+
    SendCommand(cmd);
+
    SetDeviceDataInDB( device_id, 200, temp_state ) # 200 = DEVICEDATA_State_CONST
+
  
    when 'VOL'
+
#954 B
    volume = response[5..-1].to_i();
+
SendZoneCommand(cmd)
    volume_norm = NormalizeVolume_MainZone(volume)
+
    log("Main Zone Volume Changed: " + volume.to_s + " Normalized Value: " + volume_norm.to_s + "\n");
+
#     device_id = device_.mapPortChannel_Device_.[0.to_s]
+
log("======= Determiming device ID for Zone 0 : " + device_id.to_s + "\n")
+
device_.mapPortChannel_Device_.each do |key, value|
+
log(key.to_s + " => " + value.to_s+ "\n")
+
end
+
    SetDeviceDataInDB( device_id, 158, volume_norm ) # 158 = DEVICEDATA_Volume_Level_CONST
+
    cmd = Command.new(device_id, -1001, 1, 2, 71);
+
    cmd.params_[30] = volume_norm
+
    SendCommand(cmd);
+
    when 'PWR'
+
    case avrvalue
+
    when '1'
+
    log("Device Power Changed: OFF");
+
    cmd.params_[10] = "0";
+
    $DevicePower = 0
+
    when '2'
+
    log("Device Power Changed: ON");
+
    cmd.params_[10] = "1";
+
    $DevicePower = 1
+
    end
+
    cmd = Command.new(device_.devid_, -1001, 1, 2, 48);
+
    SendCommand(cmd);
+
    else
+
    log("Yet not implemented: Handler for command: " + avrcommand.to_s)
+
    end
+
   
+
end   
+
  
 +
Internal
 +
#373 Private Method Listing
 +
  ###########################################################3 Internal Methods
 +
  # To do list :
 +
  # - ???
 +
  #
 +
  #
 +
  # RS232 protocol, ZN=Zone Number (00 is all, 01-31 separate zones)
 +
  # ZN 01 ON
 +
  # ZN 02 OFF
 +
  # ZN 03 A AMP
 +
  # ZN 04 B AMP
 +
  # ZN 07 VOLUME UP
 +
  # ZN 08 VOLUME DOWN
 +
  # ZN 09 SET LEVEL 0
 +
  # ZN 10 SET LEVEL 1
 +
  # ZN 11 SET LEVEL 2
 +
  # ZN 12 SET LEVEL 3
 +
  # ZN 13 SET LEVEL 4
 +
  # ZN 14 SET LEVEL 5
 +
  # ZN 15 SET LEVEL 6
 +
  # ZN 16 SET LEVEL 7
 +
  # ZN 17 SET LEVEL 8
 +
  # ZN 18 SET LEVEL 9
 +
  # ZN 19 SET LEVEL 10
 +
  # ZN 20 SET LEVEL 11
 +
  # ZN 32 ? STATUS
 +
  # ZN 33 ? LEVEL
 +
  # ZN 34 ? ON/OFF
 +
  # ZN 35 ? A/B
 +
 
 +
 
 +
  def log(line)
 +
          $log = File.open("/var/log/pluto/" + device_.devid_.to_s + "_Generic_Serial_Device.log", "a")
 +
  #   $log = File.open("/var/log/pluto/ESS.log", "a")
 +
    logTime = Time.now
 +
    timeStr = logTime.strftime("%d-%m-%Y  %H:%M:%S  ")
 +
 
 +
    $log.puts timeStr + "(***):" + line.to_s
 +
            $log.close
 +
  end
 +
 
 +
  def Send_RS232_Command(command)
 +
          log("Send_RS232_Command: [" + command+ "]\n")
 +
  # conn_.Send("[" + command+ "]")
 +
  end
 +
 
 +
 
 +
  def SendZoneCommand(cmd)
 +
 
 +
    log("Got Command with ID: " + cmd.id_.to_s + " from: " + cmd.devidfrom_.to_s + " to: " + cmd.devidto_.to_s + "\n")
 +
    zone = GetZone(cmd.devidto_)
 +
    if( zone >= 0 )
 +
          zone_desc=device_.childdevices_[cmd.devidto_].devdata_[186]
 +
  log("SendZoneCommand: Child Zone " + zone_desc + "[" + zone.to_s + "]\n")
 +
    elsif (cmd.devidto_ == device_.devid_)
 +
  log("SendZoneCommand: All Zones " + zone.to_s + ". Sending command to all zones !!!")
 +
  zone=0
 +
  zone_desc="All Zones"
 +
    else
 +
  log("SendZoneCommand: Invalid zone " + zone.to_s + ". Send commands only to valid devices!!!")
 +
    end
 +
   
 +
    serial_command = ""
 +
   
 +
    case cmd.id_
 +
      when 192 #192 is ON
 +
      serial_command = sprintf("%02d",zone)+sprintf("%02d",1);
 +
      Send_RS232_Command(serial_command);
 +
      temp_cmd = Command.new(cmd.id_, -1001, 1, 2, 48);
 +
      temp_cmd.params_[10] = "1";
 +
      SendCommand(temp_cmd);
 +
      $ZoneStatus[zone.to_s] = "ON";
 +
      if (zone==0)
 +
      SetZones("ON")
 +
      end
 +
      when 193 #193 is OFF
 +
      serial_command = sprintf("%02d",zone)+sprintf("%02d",2);
 +
      Send_RS232_Command(serial_command);
 +
      temp_cmd = Command.new(cmd.id_, -1001, 1, 2, 48);
 +
      temp_cmd.params_[10] = "0";
 +
      SendCommand(temp_cmd);
 +
      $ZoneStatus[zone.to_s] = "OFF";
 +
      if (zone==0)
 +
      SetZones("OFF")
 +
      end
 +
 
 +
      when 90 #90 is Volume Down
 +
      serial_command = sprintf("%02d",zone)+sprintf("%02d",8);
 +
      Send_RS232_Command(serial_command);
 +
      $ZoneVolumes[zone.to_s] = $ZoneVolumes[zone.to_s] - 10
 +
      if ($ZoneVolumes[zone.to_s]<0)
 +
      $ZoneVolumes[zone.to_s]=0
 +
  end
 +
      SetDeviceDataInDB( cmd.devidto_, 158, ($ZoneVolumes[zone.to_s]).to_s ) # 158 = DEVICEDATA_Volume_Level_CONST
 +
      temp_cmd = Command.new(cmd.devidto_, -1001, 1, 2, 71);
 +
      temp_cmd.params_[30] = ($ZoneVolumes[zone.to_s]).to_s
 +
      SendCommand(temp_cmd);
 +
 
 +
      when 89 #90 is Volume Up
 +
      serial_command = sprintf("%02d",zone)+sprintf("%02d",7);
 +
      Send_RS232_Command(serial_command);
 +
      $ZoneVolumes[zone.to_s] = $ZoneVolumes[zone.to_s] + 10
 +
      if ($ZoneVolumes[zone.to_s]>=120)
 +
      $ZoneVolumes[zone.to_s]=110
 +
  end
 +
      SetDeviceDataInDB( cmd.devidto_, 158, ($ZoneVolumes[zone.to_s]).to_s ) # 158 = DEVICEDATA_Volume_Level_CONST
 +
      temp_cmd = Command.new(cmd.devidto_, -1001, 1, 2, 71);
 +
      temp_cmd.params_[30] = ($ZoneVolumes[zone.to_s]).to_s
 +
      SendCommand(temp_cmd);
 +
      when 313 #313 is Set Volume with Level as Param #76
 +
  log("313:Set Volume Command: Got Cmd with ID: " + cmd.id_.to_s + " from: " + cmd.devidfrom_.to_s + " to: " + cmd.devidto_.to_s + " | Zone : " + zone.to_s + "\n")
 +
      level=(cmd.params_[76]).to_i
 +
      if level >110
 +
      level=110
 +
  log("313:Set Volume Command: Volume too high - truncated to max level of 110\n")
 +
      elsif level <0
 +
      level=0
 +
  log("313:Set Volume Command: Volume too low - truncated to min level of 0\n")
 +
      end
 +
  level = (level/10.0).round
 +
      serial_command = sprintf("%02d",zone)+sprintf("%02d",level+9);
 +
      Send_RS232_Command(serial_command);
 +
      $ZoneVolumes[zone.to_s] = (cmd.params_[76]).to_i
 +
      SetDeviceDataInDB( cmd.devidto_, 158, ($ZoneVolumes[zone.to_s]).to_s ) # 158 = DEVICEDATA_Volume_Level_CONST
 +
      temp_cmd = Command.new(cmd.devidto_, -1001, 1, 2, 71);
 +
      temp_cmd.params_[30] = ($ZoneVolumes[zone.to_s]).to_s
 +
      SendCommand(temp_cmd);
 +
      if (zone==0)
 +
      SetVolumes($ZoneVolumes[zone.to_s])
 +
      end
 +
 
 +
      when 616 #616 is Select A
 +
      serial_command = sprintf("%02d",zone)+sprintf("%02d",3);
 +
      Send_RS232_Command(serial_command);
 +
      when 617 #617 is Select B
 +
      serial_command = sprintf("%02d",zone)+sprintf("%02d",4);
 +
      Send_RS232_Command(serial_command);
 +
      else
 +
      log("Yet not implemented: Handler for command: ID " + cmd.id.to_s)
 +
      end
 +
 
 +
 
 +
  end
 +
 
 +
 
 +
  def GetZone(deviceDestination)
 +
  if( device_.mapDevice_PortChannel_.has_key?(deviceDestination) and
 +
  device_.mapDevice_PortChannel_[deviceDestination] != nil and
 +
  !device_.mapDevice_PortChannel_[deviceDestination].empty? )
 +
  return device_.mapDevice_PortChannel_[deviceDestination].to_i
 +
  end
 +
 
 +
  return -1
 +
  end
 +
 
 +
 
 +
  def ReceiveReport()
 +
  @buff = conn_.RecvDelimited("]", 1000)
 +
  log("Response from ESS: " + @buff + "\n")
 +
  # log("Calling DecodeReceivedInfo: " + @buff + "\n")
 +
  # temp_resp=@buff;
 +
  # DecodeReceivedInfo(temp_resp)
 +
  # log("End of DecodeReceivedInfo: " + temp_resp + "\n")
 +
  end
 +
 
 +
  def SetVolumeInZone(level,zone)
 +
  # Sets Normalized Volume Level (0..100) in certain zone
 +
    normlevel = (level.to_i/10.0).round
 +
    log("SetVolumeInZone: volume: " + level.to_s + " ["+ normlevel.to_s + "] => Zone: " + zone + "\n")
 +
 
 +
    serial_command = sprintf("%02d",zone)+sprintf("%02d",normlevel+9);
 +
    Send_RS232_Command(serial_command);
 +
  #  log("313:Set Volume Command: Got Cmd with ID: " + cmd.id_.to_s + " from: " + cmd.devidfrom_.to_s + " to: " + cmd.devidto_.to_s + " | Zone : " + zone.to_s + "\n")
 +
    $ZoneVolumes[zone.to_s] = level.to_i
 +
 
 +
  end
 +
 
 +
  def SetZonesInitialVolumes()
 +
  log("####### Set Initial Volume Levels for zones\n")
 +
  device_.mapDevice_PortChannel_.each do |key, value|
 +
  volume = device_.childdevices_[key].devdata_[208]
 +
  log("Zone " + value.to_s + " => " + volume.to_s + "\n")
 +
                sleep 1.0
 +
  SetVolumeInZone(volume.to_i,value)
 +
    SetDeviceDataInDB( device_.childdevices_[key].devid_, 158, ($ZoneVolumes[value.to_s]).to_s ) # 158 = DEVICEDATA_Volume_Level_CONST
 +
    end
 +
 
 +
  end
 +
 
 +
 
 +
  def SetZones(state)
 +
  log("####### Setting all zones to " + state + "\n")
 +
  device_.mapDevice_PortChannel_.each do |key, value|
 +
  $ZoneStatus[value] = state
 +
  log("Zone " + value.to_s + " => " + state + "\n")
 +
  end
 +
  end
 +
 
 +
  def SetVolumes(level_int)
 +
  log("####### Set Volume Levels for all zones\n")
 +
  device_.mapDevice_PortChannel_.each do |key, value|
 +
  log("Zone " + value.to_s + " => " + level_int.to_s + "\n")
 +
  $ZoneVolumes[value.to_s] = level_int
 +
    SetDeviceDataInDB( device_.childdevices_[key].devid_, 158, ($ZoneVolumes[value.to_s]).to_s ) # 158 = DEVICEDATA_Volume_Level_CONST
 +
    end
 +
  end
  
def DecodeReceivedInfo(response)
 
# Examples: Mute/On/Off: @AMT:1,@MSP:2,@MAM:2,  Volume: @VOL:-25,@MSV:-56,@MVL:-56  Inputs:@MSC:2E,@SRC:22,@SRC:2E
 
  log("DecodeReceivedInfo1: " + response + "\n");
 
  avrcommand = response[1..3]
 
  avrvalue = response[5]
 
  log("DecodeReceivedInfo2: " + response + " -> Command:"+ avrcommand.to_s + " Value: " + avrvalue.to_s + "\n");
 
  
  case avrcommand
+
#351 Process IDLE
    when 'VOL'
+
    volume = response[5..-1].to_i();
+
    volume_norm = NormalizeVolume_MainZone(volume)
+
    log("Main Zone Volume Changed: " + volume.to_s + " Normalized Value: " + volume_norm.to_s + "\n");
+
#     device_id = device_.mapPortChannel_Device_.[0.to_s]
+
log("======= Determiming device ID for Zone 0 : " + device_id.to_s + "\n")
+
device_.mapPortChannel_Device_.each do |key, value|
+
log(key.to_s + " => " + value.to_s+ "\n")
+
if (key.to_s == "0")
+
device_id = value
+
end
+
end
+
    SetDeviceDataInDB( device_id, 158, volume_norm ) # 158 = DEVICEDATA_Volume_Level_CONST
+
    cmd = Command.new(device_id, -1001, 1, 2, 71);
+
    cmd.params_[30] = volume_norm
+
    SendCommand(cmd);
+
    when 'PWR'
+
    case avrvalue
+
    when '1'
+
    log("Device Power Changed: OFF")
+
    cmd.params_[10] = "0";
+
    $DevicePower = 0
+
    when '2'
+
    log("Device Power Changed: ON");
+
    cmd.params_[10] = "1";
+
    $DevicePower = 1
+
    end
+
    cmd = Command.new(device_.devid_, -1001, 1, 2, 48);
+
    SendCommand(cmd);
+
    else
+
    log("Yet not implemented: Handler for command: " + avrcommand.to_s)
+
    end
+
   
+
end   
+
  
 +
#350 Process Incoming Data
 +
ReceiveReport()
  
def ReceiveReportCommand()
+
#355 Process Initialize
@buff = conn_.RecvDelimited("\r", 1000)
+
log("Cmd response from Marantz: " + @buff + "\n")
+
log("Calling DecodeReceivedInfo: " + @buff + "\n")
+
temp_resp=@buff;
+
DecodeReceivedInfo(temp_resp)
+
log("End of DecodeReceivedInfo: " + temp_resp + "\n")
+
end
+
  
def SendIrCommand(command)
+
log("Initializing ESS GSD Device\n")
        log("SendIrCommand: " + command + "\n")
+
conn_.Send(command+ "\r")
+
        log("Sending Command: " + command + "\n")
+
ReceiveReportCommand()
+
end
+
  
def log(line)
+
sleep 1.0
#         $log = File.open("/var/log/pluto/" + device_.devid_.to_s + "_Generic_Serial_Device.log", "a")
+
Send_RS232_Command("0002")#All Zones OFF
  $log = File.open("/var/log/pluto/Marantz_SR5600.log", "a")
+
sleep 1.0
  logTime = Time.now
+
Send_RS232_Command("0020");  #All Zones Max Volume
  timeStr = logTime.strftime("%d-%m-%Y %H:%M:%S ")
+
sleep 1.0
 +
Send_RS232_Command("0003");  #All Zones Select A
 +
sleep 1.0
 +
$ZoneVolumes = Hash.new()
 +
  $ZoneStatus = Hash.new()
  
  $log.puts timeStr + "(***):" + line.to_s
+
SetZones("OFF")
          $log.close
+
SetZonesInitialVolumes()
end
+
  
def SendMultiZoneCommand(cmd, commandsArray, commandParam="")
+
log("Initialization of ESS GSD Device finished !!!!\n")
        log("Got Command with ID: " + cmd.id_.to_s + " from: " + cmd.devidfrom_.to_s + " to: " + cmd.devidto_.to_s + "\n")
+
zone = GetZone(cmd.devidto_)
+
if( zone >= 0 and zone < commandsArray.size )
+
        zone_desc=device_.childdevices_[cmd.devidto_].devdata_[186]
+
log("Determine zone for command : " + zone_desc + "[" + zone.to_s + "]\n")
+
SendIrCommand(commandsArray[zone] + commandParam.to_s)
+
else
+
log("SendMultiZoneCommand: invalid zone " + zone.to_s + ". Send commands only to valid child devices!!!")
+
end
+
end
+
  
  
def SendMultiZonePowerOnCommand(cmd, commandsArray, commandParam="")
 
#It will Unmute Zone first and then see also global power should be turned on...
 
        log("Got Power On Command with ID: " + cmd.id_.to_s + " from: " + cmd.devidfrom_.to_s + " to: " + cmd.devidto_.to_s + "\n")
 
zone = GetZone(cmd.devidto_)
 
if( zone >= 0 and zone < commandsArray.size )
 
        zone_desc=device_.childdevices_[cmd.devidto_].devdata_[186]
 
log("Determine zone for command : " + zone_desc + "[" + zone.to_s + "]\n")
 
SendIrCommand(commandsArray[zone] + commandParam.to_s)
 
$ZoneStatus[zone.to_s] = 'ON'
 
log("*** Setting zone : " + zone_desc + "[" + zone.to_s + "] to ON\n")
 
ShowZones()
 
if ($DevicePower == 0)
 
SendIrCommand("@PWR:2")
 
end
 
$DevicePower = 1
 
elsif (zone == -1)
 
if ($DevicePower == 0)
 
SendIrCommand("@PWR:2")
 
end
 
$DevicePower = 1
 
log("####### Powering device On\n")
 
else
 
log("SendMultiZonePowerOnCommand: invalid zone " + zone.to_s + ". Send commands only to valid child devices!!!")
 
end
 
end
 
  
def SendMultiZonePowerOffCommand(cmd, commandsArray, commandParam="")
 
#It will Mute Zone first and then see if also global power can be turned off...
 
        log("Got Power Off Command with ID: " + cmd.id_.to_s + " from: " + cmd.devidfrom_.to_s + " to: " + cmd.devidto_.to_s + "\n")
 
zone = GetZone(cmd.devidto_)
 
if( zone >= 0 and zone < commandsArray.size )
 
        zone_desc=device_.childdevices_[cmd.devidto_].devdata_[186]
 
log("Determine zone for command : " + zone_desc + "[" + zone.to_s + "]\n")
 
SendIrCommand(commandsArray[zone] + commandParam.to_s)
 
$ZoneStatus[zone.to_s] = 'OFF'
 
log("*** Setting zone : " + zone_desc + "[" + zone.to_s + "] to OFF\n")
 
ShowZones()
 
if (($ZoneStatus["0"] == 'OFF') and ($ZoneStatus["1"] == 'OFF') and ($ZoneStatus["2"] == 'OFF'))
 
SendIrCommand("@PWR:1")
 
$DevicePower = 0
 
log("####### All zones are muted - device is powered Off\n")
 
end
 
else
 
log("SendMultiZonePowerOffCommand: invalid zone " + zone.to_s + ". Send commands only to valid child devices!!!")
 
end
 
end
 
  
 +
#384 Process Receive Command For Child
 +
SendZoneCommand(cmd)
  
def GetZone(deviceDestination)
+
#356 Process Release
if( device_.mapDevice_PortChannel_.has_key?(deviceDestination) and
+
Send_RS232_Command("0002");  #All Zones OFF
device_.mapDevice_PortChannel_[deviceDestination] != nil and
+
!device_.mapDevice_PortChannel_[deviceDestination].empty? )
+
return device_.mapDevice_PortChannel_[deviceDestination].to_i
+
end
+
+
return -1
+
end
+
  
def ShowZonesDebug()
+
Send_RS232_Command("0020");  #All Zones Max Volume
log("####### Dev 2 PortChanel\n")
+
device_.mapDevice_PortChannel_.each do |key, value|
+
log(key.to_s + " => " + value.to_s + "\n")
+
end
+
+
log("======= PortChanel 2 Dev\n")
+
device_.mapPortChannel_Device_.each do |key, value|
+
log(key.to_s + " => " + value.to_s+ "\n")
+
end
+
end
+
  
 +
Send_RS232_Command("0003");  #All Zones Select A
  
  
def ShowZonesInitialVolumes()
+
log("Shutting Down ESS GSD Device finished !!!!\n")
log("####### Show Initial Volume Levels for zones\n")
+
device_.mapDevice_PortChannel_.each do |key, value|
+
        volume = device_.childdevices_[key].devdata_[208]
+
log("Zone " + value.to_s + " => " + volume.to_s + "\n")
+
SetVolumeInZone(volume,value)
+
end
+
+
end
+
  
 +
Misc
 +
#616 Select A
 +
  
def ShowZones()
+
#617 Select B
temp = "####### Zones Status : "
+
device_.mapDevice_PortChannel_.each do |key, value|
+
temp = temp + " | Zone " + value.to_s + " => " + $ZoneStatus[value]
+
end
+
temp = temp + "\n"
+
log(temp)
+
end
+
  
 +
Sound & Volume
 +
#97 Mute
 +
  
def SetZones(state)
+
#313 Set Volume
log("####### Setting all zones to " + state + "\n")
+
SendZoneCommand(cmd)
device_.mapDevice_PortChannel_.each do |key, value|
+
$ZoneStatus[value] = state
+
log("Zone " + value.to_s + " => " + state + "\n")
+
end
+
end
+
  
def InitZones()
+
#90 Vol Down
log("####### Initializing all zones to OFF\n")
+
device_.mapDevice_PortChannel_.each do |key, value|
+
$ZoneStatus[value] = 'OFF'
+
log("Zone " + value.to_s + " => " + $ZoneStatus[value] + "\n")
+
end
+
log("####### Print $ZoneStatus Hash:\n")
+
$ZoneStatus.each do |key, value|
+
log("Zone " + key.to_s + " => " + value.to_s + "\n")
+
end
+
end
+
  
def SetVolumeInZone(level,zone)
+
#89 Vol Up
# Sets Normalized Volume Level (0..100) in certain zone
+
log("SetVolumeInZone: volume: " + level.to_s + " => Zone: " + zone + "\n")
+
 
+
if ( zone == "0")
+
log("SetVolumeInZone0: volume: " + level.to_s + " => Zone: " + zone + "\n")
+
      new_level = DeNormalizeVolume_MainZone(level.to_i)
+
log("SetVolumeInZone00: volume: " + level.to_s + " => Zone: " + zone + "\n")
+
      temp_str="@VOL:0"+new_level.to_s;
+
      log("SetVolumeInZone: About to send command :" + temp_str + "\n")
+
      SendIrCommand(temp_str)
+
elsif ( zone == "1")
+
log("SetVolumeInZone1: volume: " + level.to_s + " => Zone: " + zone + "\n")
+
      new_level = DeNormalizeVolume_OtherZone(level.to_i)
+
log("SetVolumeInZone11: volume: " + level.to_s + " => Zone: " + zone + "\n")
+
      temp_str="@MSV:0"+new_level.to_s;
+
      log("SetVolumeInZone: About to send command :" + temp_str + "\n")
+
      SendIrCommand(temp_str)
+
elsif ( zone == "2")
+
log("SetVolumeInZone2: volume: " + level.to_s + " => Zone: " + zone + "\n")
+
      new_level = DeNormalizeVolume_OtherZone(level.to_i)
+
# log("SetVolumeInZone22: volume: " + level + " => Zone: " + zone + "\n")
+
      temp_str="@MVL:0"+new_level.to_s;
+
      log("SetVolumeInZone: About to send command :" + temp_str + "\n")
+
      SendIrCommand(temp_str)
+
else
+
      log("SetVolumeInZone: invalid zone " + zone.to_s + ". Send commands only to valid child devices!!!")
+
end
+
log("SetVolumeInZone End: volume: " + level.to_s + " => Zone: " + zone + "\n")
+
 
+
end
+
 
+
 
+
def NormalizeVolume_MainZone(level)
+
# Normalizes Marantz Volume Level (-99..+10) to 0-100 boundaries for Main Zone
+
temp = (level+100)/110.0*100.0
+
temp=temp.round
+
        log("Normalizing Main Zone Volume Level from : " + level.to_s + " to " + temp.to_s + "\n")
+
return temp
+
end
+
 
+
def NormalizeVolume_OtherZone(level)
+
# Normalizes Marantz Volume Level to 0-100 boundaries for Second Zone (-90..0)
+
temp = ((level+90.0)/90.0)*100.0
+
temp=temp.round
+
        log("Normalizing Other Zone Volume Level from : " + level.to_s + " to " + temp.to_s + "\n")
+
return temp
+
end
+
 
+
def DeNormalizeVolume_MainZone(level)
+
# DeNormalizes volume level in 0-100 boundaries to Marantz Volume Level (-99..+10) for Main Zone
+
temp = (level/100.0)*110.0-99
+
temp=temp.round
+
        log("DeNormalizing Main Zone Volume Level from : " + level.to_s + " to " + temp.to_s + "\n")
+
return temp
+
end
+
 
+
def DeNormalizeVolume_OtherZone(level)
+
# DeNormalizes volume level in 0-100 boundaries to Marantz Volume Level (-90..0) for Other Zone
+
temp = ((level/100.0)*90.0)-90.0
+
temp=temp.round
+
        log("DeNormalizing Other Zone Volume Level from : " + level.to_s + " to " + temp.to_s + "\n")
+
return temp
+
end
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
###########################################################Process Incoming Data
+
@buff = conn_.RecvDelimited("\r", 1000)
+
log("Info from Marantz: " + @buff + "\n")
+
temp_line=@buff
+
log("1Calling DecodeReceivedInfo: " + temp_line + "\n")
+
DecodeReceivedInfo(temp_line)
+
log("1End of DecodeReceivedInfo: " + temp_line+ "\n")
+
 
+
 
+
 
+
###########################################################Set Tuner Frequency
+
identifier = cmd.params_[216]
+
# Main Frequency "TFQ:0xxxxx"  from 08700 till + 10800
+
# Multiroom Frequency “MTF:0xxxxx“  from 08700 till + 10800
+
 
+
# switch to tuner mode first
+
SendMultiZoneCommand(cmd, ["@SRC:G", "@MSC:G", "@MSC:G"])
+
 
+
# tune to desired frequency
+
SendMultiZoneCommand(cmd, ["@TFQ:0"+identifier, "@MTF:0"+identifier, "@MTF:0"+identifier])
+
log("Sending Marantz : Setting tuner frequency to "+identifier+"\n")
+
 
+
 
+
###########################################################313 Set Volume command
+
level = cmd.params_[76]
+
# Main volume “VOL:0xxx”  from - 99 till + 18
+
# Multiroom Speakers "MSV:0xxx"  from - 99 till + 0
+
# Multiroom Speakers “MVL:0xxx”  from - 99 till + 0
+
zone = GetZone(cmd.devidto_)
+
log("313:Set Volume Command: Got Cmd with ID: " + cmd.id_.to_s + " from: " + cmd.devidfrom_.to_s + " to: " + cmd.devidto_.to_s + " | Zone : " + zone.to_s + "\n")
+
if ( zone == 0)
+
      new_level = DeNormalizeVolume_MainZone(level.to_i)
+
      temp_str = "@VOL:0"+new_level.to_s
+
      log("SetVolumeCommand: About to send command :" + temp_str + "\n")
+
      SendIrCommand(temp_str)
+
elsif ( zone == 1)
+
      new_level = DeNormalizeVolume_OtherZone(level.to_i)
+
      temp_str = "@MSV:0"+new_level.to_s
+
      SendIrCommand(temp_str)
+
elsif ( zone == 2)
+
      new_level = DeNormalizeVolume_OtherZone(level.to_i)
+
      temp_str = "@MVL:0"+new_level.to_s
+
      log("SetVolumeCommand: About to send command :" + temp_str + "\n")
+
      SendIrCommand(temp_str)
+
 
+
else
+
      log("SetVolumeCommand: invalid zone " + zone.to_s + ":" + zone.to_i + ". Send commands only to valid child devices!!!\n")
+
end
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
###########################################################Process Initialize
+
$DevicePower = 1
+
SendIrCommand("@PWR:2")
+
log("Initializing Marantz GSD Device\n")
+
 
+
 
+
 
+
SendIrCommand("@AST:F") # max level of feedback
+
 
+
SendIrCommand("@MPW:2") # multi room always on
+
 
+
SendIrCommand("@MSP:1") # turn multi room speakers off by default
+
 
+
SendIrCommand("@MAM:2") # mute multi room line out by default
+
 
+
SendIrCommand("@VOL:0-20") # default main volume -20
+
SendIrCommand("@MSV:0-60") # default multi speaker volume
+
SendIrCommand("@MVL:0-60") # default multi room line out volume
+
 
+
$ZoneStatus = Hash.new()
+
 
+
InitZones()
+
ShowZonesInitialVolumes()
+
 
+
 
+
#$DevicePower = 0
+
#SendIrCommand("@PWR:1")
+
log("Initialization of Marantz GSD Device finished - Device is powered off!!!!\n")
+
 
</nowiki></pre>
 
</nowiki></pre>
 
 
=== Important device data and parameters for "Zone" template (#1867)===
 
 
I've extended that template to be useful for multizone audio devices. Basically such device should include everything that is needed to control Audio Zone (input switching, A/B switching, volume control, On/Off Commands). But bevare, those devices are embedded - that means that they don't do anything - they are just empty boxes you add to parent device for representing each audio zone. All processing of commands should be done in parent device. But using Zones, you can easily connect devices via pipes, create scenarios with sending commands to certain devices/zones etc...
 
 
Important extensions to Zone template:
 
- added common audio inputs (so input switching can be implemented)
 
- added commands (volume, inputs, on/off, A/B switching)
 
- added "controlled by" category of Amps,...
 
 
Have added following command groups are used in Zone :
 
A/B switching          (select A, select B) 
 
Generic IO Commands (On, Off)
 
Inputs                 (common inputs on audio receiver)
 
Radio                 (command for tuning to certain radio frequency)
 
 
Zones are automatically added to parent device by having this on parent template :
 
 
Device Template Related
 
#1867 Zone My Child Extra 12|0|186|All Zone
 
#1867 Zone My Child Extra 12|1|186|Zone 1
 
 
Rest of zones have to added according to configuration used.
 
 
12|1 means that device data #12 (Port) will have value of 1 that represents zone number. Remember zone "0" is special zone for sending command to all zones, ordinary zones are numbered from 1-31.
 
 
== Using device ==
 
 
=== Add device based on created template ===
 
=== Specify COM port for it ===
 
=== Assign it to new room ===
 
 
Assign also child zones to rooms or EAs.
 
 
=== Specifiy Audio pipe connections to inputs on zones ===
 
In my case, I connnected output of Hybrid Core and one MD to different inputs (CD and DVD) and different zones. In that way, when you press play on each source, commands to setup inputs on SR5600 should be done automatically so you can hear music right away....
 
 
Now reload Router, regenerate Orbiters and new device should appear in specified room. On click you get into control screen where you can control device 'manually' beside automatic handling described above.
 
 
 
=== Prepare Media scenarios ===
 
 
Beside from commands that are sent from Orbiters, you can also create various media scenarios, with On/Off, Volume, A/B, Input switching (you can for example put both (all three) output zones to same source - no sync problems !!!...
 
 
== Where the problems arise ==
 
 
I'm describing few potential problems I'm facing in my setup. Will be happy on any opinion or guidance...
 
 
=== Proper support for connecting On screen media player (MD or Hybrid) to Amplifier ===
 
 
Currently it seems that media players cannot be connected to amplifier in a clean way. The problem is in routing of volume and on/off commands through audio pipe. In short, the problem is that currently you have to connect hybrid or MD to audio pipe, but when media ends in that EA, Off is sent to Xine player, but On command to Orbiter and this On command also gets propagated on pipe - so Amplifier turns on, instead of Off. Also when screen saver jumps in, there is also possibility that Off command will get to the amplifier, which is not correct if you're listening to media... There are at least few threads on forums and Mantis, if you want to read more...
 
 
=== Some older discussions ===
 
 
Below I'm leaving some older discussions about potential problems. Not sure if they still hold...
 
 
=== Proper support for MultiRoom feature ===
 
Device supports separate stereo device on two back channels. That means that with enabling multiroom feature, basically you get two devices (one 4+1 and other 2 channels) that can have separate or same sources and different volume levels. I'm not sure how to properly setup support for such feature - will try to add another child tuner and set it up to control only second separate channel. But it seems that I cannot get two screens for two tuners/zones in one zone, but as child devices they need to be in same zone (as I was advised from LinuxMCE guys or am I missing something).
 
 
=== Cannot add device to Entertainment areas in same room ===
 
I have SR5600 assigned to room. Assigning it to separate entertainment area in one of the rooms didn't work (menus didn't appear on Orbiters). Also I should put two child devices (tuners) into separate audio entities, but that would probably not work at this time...
 
 
I'm not sure what is the real problem and what is proper solution. I'll kindly ask LinuxMCE guys to guide me here....
 
 
=== Pipes can be connected only to one device ===
 
It seems logical, but in case of such devices it's also restriction. I have Hybrid and MD output connected to one input on receiver (although have virtually two devices and would like to have an option to be able to connect two audio pipes from one source). There is also Y connection splitting line audio signal from MD to TV and receiver. Also pipes feature of automatic handling of routing (if you for instance press play on MD, then receiver will be automatically switched to proper input, so you can hear music) can sometimes become restriction. For instance if I use two separate inputs, then hybrid is assigned to one of them and volume control will change main volume and play/stop will reroute always to hard-coded route, regardless of current state, where it might be better rerouted to second device output, cause first one is occupied with Movie....
 
 
=== Pipes and automatic routing is not always a good thing ===
 
For instance, user is listening to audio, then he decides to switch manually receiver to FM station. MD or hybrid keeps playing its music and shuts receiver off when it finishes... If we want to properly deal with this situation, we should have two way communication and a lot of code to sense user actions and set everything according to that - but that's a lot more of work to do....
 
 
 
 
This is schema for my setup and I'd kindly ask for further guidance. There are quite few potential problems with more sophisticated setups, but I feel that this will be common practical situation in deploying LinuxMCE system...
 
 
[[Image:Shema audio.png]]
 
 
== Discussion on how to properly implement template for such device ? ==
 
 
 
Currently, I've implemented such template using child Zone devices and it seems like a step in proper direction. But we will see. I'm also presenting discussion below that describes where the problem lies...
 
 
 
After a year or so, it is not clear to me how to properly implement template for such device that includes all sorts of "embedded devices".
 
 
Let me start with basic description of capabilities :
 
 
1. Outputs
 
 
A. device as single audio output
 
  A.1 7+1 channel amplified output
 
  A.2 Line out that has same music as A.1, but can be swithced on/off and volume controlled separately with serial commands
 
 
B. device as multiroom audio output (two separate and controlable outputs) - this is how I use it at the moment to feed 2 audio zones)
 
  A.1 5+1 channel amplified output
 
  A.1 2 channel amplified output on last two speakers terminals with own commands for volume,source and other control...
 
  A.3 Line out that has same music as A.1, but can be swithced on/off and volume controlled separately with serial commands
 
 
So basically to cover both cases I have 3 possible outputs :
 
  O.1 normal main audio output (either 7+1 if A and 5+1 if B)
 
  O.2 secondary stereo amplified output (last two outputs from A.1 are made as separate amplified stereo output with its own serial commands, volume and source control..)
 
  O.3 Line out that has same music as O.1, but can be switched on/off and volume controlled separately with own serial commands, so could be used for externally amplified replication of main output...
 
 
Basic distinction is that all 3 possible outputs have separate serial commands for volume and source control(not O3) etc...
 
 
2. Inputs
 
 
Outputs O.1 and O.2 can select one of possible sources independently - (I'm not sure if they can select separate FM stations on Tuner input) - so basically this is true multi channel audio feature. If they select same source, then same music is heard on each... But again serial commands for selecting inputs differ for each output....
 
 
LMCE has already prepared FM and AM Tuner devices templates (which I don't understand quite well, but no docs available), but I'm not sure if they are usable in such situation.
 
 
3. How to make usable template out of this ?
 
 
What I'm currently thinking is :
 
- Marantz receiver being parent device
 
- have child devices for each output (O.1-O.3), each from different template, cause they have separate commands for each
 
 
But questions arise :
 
- each output could be placed into separate audio zone - but I'm not sure if this can be done with children of same parent ?
 
- how to dispatch commands to each child ?
 
- is it convenient to use tuner devices as well ?
 
 
And probably there are some more problems to be solved.
 
 
Any opinion ?
 
 
(to be expanded)
 

Latest revision as of 10:25, 3 July 2016

Version Status Date Updated Updated By
710 Unknown N/A N/A
810 Unknown N/A N/A
1004 Unknown N/A N/A
1204 Unknown N/A N/A
1404 Unknown N/A N/A
Usage Information


Ess2468.gif

More info about device  :

This is nice multizone expandable speaker selector swithc, that can be IR or RS232 controlled. Each zone can also have separate control for 11 volume levels. Although recently it's price went significantly up, I still think it's quite usable (I'm yet about to try it in real life). I'll use it for connecting several speakers pairs to Multiroom secondary stereo amplified output on my Marantz SR5600. In this way I'll get nice configurable family home audio system with two audio channels distributed around house and fully controlled by LMCE (I produce second channel also on LMCE Hybrid, by using squeezeslave and another USB sound card)...

Device includes "base" module, to whom you simply attach different modules (they differ on volume control features) for each zone up to 31. Each zone is simply numbered, zone 0 is special zone - sending commands to that zone will have effect on all zones at once.

There is simple RS232 protocol for this device, described here It's pretty simple and ASCII based..

The whole idea of implementation is to have each zone represented by "Zone" child devices (template #1867), adding as much as you have them in your device. By default zone "0" will be represented by device it self - so sending commands to parent device will have effect on all zones. Sending commands only to Zone child devices, will have effect only in target zone. At the start, 1 child zone is added to parent device - I assume there will be at least one zone in system. Then you add more zones to suit your system.


Description of needed templates

I hope that this template will be available with stock LMCE distribution, so you won't have to create that template from scratch. Template is called "ESS Speaker Selector RS232" and was created from scratch but inspired by my work on "SR Marantz Multiroom RS232" template described on wiki.

For template to be functional I've made certain extensions to existing template for "Zone" that I hope will be usefull also to others. Extensions are described here [Marantz_SR5600]

Important device data and parameters for "ESS Speaker Selector RS232" template

Description : ESS Speaker Selector RS232
Implements DCE : yes
Command line : Generic_Serial_Device
Device Category  AV/preamps/... #103
Manufacturer HACS - Home Automation Control Systems

Also have added following parameters :

Device data
Current Data 	                                    Comments 	                                Default Value
#37 COM Port on PC                                                                              /dev/ttyS0 	
#76 COM Port ParityBit/Stop 	                                                                 N81
#78 COM Port BaudRate 	                                                                         B9600
#220 Process Child Commands In Pare                                                              1 (needed to have ability to send all commands to parent) 	
#157 Discrete Volume(bool)  	                                                                 1 					

Settings are factory defaults and should't be changed (9600 baud rate, 8 bits, 1 stop bit)...

'Edit Ruby code' (on template page)

'Determining proper set of inputs'

Device has two inputs for amplified stereo signals (A,B). Then each zone or all together can be switched on/off and set to input A or B. Zone modules can have no volume control, manual control or 11-level volume control controlled from RS232 interface. Each zone has two inputs for amplified signalI've added all inputs that are present on device. There is also a special input called "FM" where I connected Live TV embedded device to be able to control tuner functions (tuning/searching up/down, go to preset stations, etc...)


'Set proper set of commands'

Basically I've added command groups :

Ruby Internal Commands
Simple IO commands (on,off)
Volume Commands (vol+,vol-,set volume)
Inputs  (A,B)


'Add Ruby snippets to commands'

I've created new IR/Codeset group called "ESS Speaker Selector RS232", although I'm not sure how to handle it. Have also edited corresponding Ruby snippets for each command. Basically those are just simple strings that get sent on rs232 (they differ mainly on target zone) to take proper action on device according to received command from LinuxMCE...


More important parts with more Ruby code :

 Power
#193 Off
	SendZoneCommand(cmd) 	

#192 On
	SendZoneCommand(cmd) 	

Inputs
#953 A
	SendZoneCommand(cmd) 	

#954 B
	SendZoneCommand(cmd) 	

Internal
#373 Private Method Listing
  ###########################################################3 Internal Methods
  # To do list :
  # - ???
  #
  #
  # RS232 protocol, ZN=Zone Number (00 is all, 01-31 separate zones)
  # ZN 01 ON
  # ZN 02 OFF
  # ZN 03 A AMP
  # ZN 04 B AMP
  # ZN 07 VOLUME UP
  # ZN 08 VOLUME DOWN
  # ZN 09 SET LEVEL 0
  # ZN 10 SET LEVEL 1
  # ZN 11 SET LEVEL 2
  # ZN 12 SET LEVEL 3
  # ZN 13 SET LEVEL 4
  # ZN 14 SET LEVEL 5
  # ZN 15 SET LEVEL 6
  # ZN 16 SET LEVEL 7
  # ZN 17 SET LEVEL 8
  # ZN 18 SET LEVEL 9
  # ZN 19 SET LEVEL 10
  # ZN 20 SET LEVEL 11
  # ZN 32 ? STATUS
  # ZN 33 ? LEVEL
  # ZN 34 ? ON/OFF
  # ZN 35 ? A/B 
  
  
  def log(line)
           $log = File.open("/var/log/pluto/" + device_.devid_.to_s + "_Generic_Serial_Device.log", "a")
  #	  $log = File.open("/var/log/pluto/ESS.log", "a")
  	  logTime = Time.now
  	  timeStr = logTime.strftime("%d-%m-%Y  %H:%M:%S  ")
  
  	  $log.puts timeStr + "(***):" + line.to_s
            $log.close
  end 	
  
  def Send_RS232_Command(command)
          log("Send_RS232_Command: [" + command+ "]\n")
  #	conn_.Send("[" + command+ "]")
  end
  
  
  def SendZoneCommand(cmd)
  
    log("Got Command with ID: " + cmd.id_.to_s + " from: " + cmd.devidfrom_.to_s + " to: " + cmd.devidto_.to_s + "\n")
    zone = GetZone(cmd.devidto_)
    if( zone >= 0 )
  	        zone_desc=device_.childdevices_[cmd.devidto_].devdata_[186]
  		log("SendZoneCommand: Child Zone " + zone_desc + "[" + zone.to_s + "]\n")
    elsif (cmd.devidto_ == device_.devid_)
  		log("SendZoneCommand: All Zones " + zone.to_s + ". Sending command to all zones !!!")
  		zone=0
  		zone_desc="All Zones"
    else
  		log("SendZoneCommand: Invalid zone " + zone.to_s + ". Send commands only to valid devices!!!")
    end
     
    serial_command = ""
     
    case cmd.id_
       	when 192 #192 is ON 
       		serial_command = sprintf("%02d",zone)+sprintf("%02d",1);
       		Send_RS232_Command(serial_command);
       		temp_cmd = Command.new(cmd.id_, -1001, 1, 2, 48);
       		temp_cmd.params_[10] = "1";
       		SendCommand(temp_cmd);
       		$ZoneStatus[zone.to_s] = "ON";
       		if (zone==0)
       			SetZones("ON")
       		end
       	when 193 #193 is OFF
       		serial_command = sprintf("%02d",zone)+sprintf("%02d",2);
       		Send_RS232_Command(serial_command);
       		temp_cmd = Command.new(cmd.id_, -1001, 1, 2, 48);
       		temp_cmd.params_[10] = "0";
       		SendCommand(temp_cmd);
       		$ZoneStatus[zone.to_s] = "OFF";
       		if (zone==0)
       			SetZones("OFF")
       		end
  
       	when 90 #90 is Volume Down
       		serial_command = sprintf("%02d",zone)+sprintf("%02d",8);
       		Send_RS232_Command(serial_command);
       		$ZoneVolumes[zone.to_s] = $ZoneVolumes[zone.to_s] - 10
       		if ($ZoneVolumes[zone.to_s]<0)
       			$ZoneVolumes[zone.to_s]=0
  		end
       		SetDeviceDataInDB( cmd.devidto_, 158, ($ZoneVolumes[zone.to_s]).to_s ) # 158 = DEVICEDATA_Volume_Level_CONST
       		temp_cmd = Command.new(cmd.devidto_, -1001, 1, 2, 71);
       		temp_cmd.params_[30] = ($ZoneVolumes[zone.to_s]).to_s
       		SendCommand(temp_cmd);
  		
       	when 89 #90 is Volume Up
       		serial_command = sprintf("%02d",zone)+sprintf("%02d",7);
       		Send_RS232_Command(serial_command);
       		$ZoneVolumes[zone.to_s] = $ZoneVolumes[zone.to_s] + 10
       		if ($ZoneVolumes[zone.to_s]>=120)
       			$ZoneVolumes[zone.to_s]=110
  		end
       		SetDeviceDataInDB( cmd.devidto_, 158, ($ZoneVolumes[zone.to_s]).to_s ) # 158 = DEVICEDATA_Volume_Level_CONST
       		temp_cmd = Command.new(cmd.devidto_, -1001, 1, 2, 71);
       		temp_cmd.params_[30] = ($ZoneVolumes[zone.to_s]).to_s
       		SendCommand(temp_cmd);
       	when 313 #313 is Set Volume with Level as Param #76
  		log("313:Set Volume Command: Got Cmd with ID: " + cmd.id_.to_s + " from: " + cmd.devidfrom_.to_s + " to: " + cmd.devidto_.to_s + " | Zone : " + zone.to_s + "\n")
       		level=(cmd.params_[76]).to_i
       		if level >110 
       			level=110
  			log("313:Set Volume Command: Volume too high - truncated to max level of 110\n")
       		elsif level <0
       			level=0
  			log("313:Set Volume Command: Volume too low - truncated to min level of 0\n")
       		end
  		level = (level/10.0).round
       		serial_command = sprintf("%02d",zone)+sprintf("%02d",level+9);
       		Send_RS232_Command(serial_command);
       		$ZoneVolumes[zone.to_s] = (cmd.params_[76]).to_i
       		SetDeviceDataInDB( cmd.devidto_, 158, ($ZoneVolumes[zone.to_s]).to_s ) # 158 = DEVICEDATA_Volume_Level_CONST
       		temp_cmd = Command.new(cmd.devidto_, -1001, 1, 2, 71);
       		temp_cmd.params_[30] = ($ZoneVolumes[zone.to_s]).to_s
       		SendCommand(temp_cmd);
       		if (zone==0)
       			SetVolumes($ZoneVolumes[zone.to_s])
       		end
  
       	when 616 #616 is Select A
       		serial_command = sprintf("%02d",zone)+sprintf("%02d",3);
       		Send_RS232_Command(serial_command);
       	when 617 #617 is Select B
       		serial_command = sprintf("%02d",zone)+sprintf("%02d",4);
       		Send_RS232_Command(serial_command);
       	else
       		log("Yet not implemented: Handler for command: ID " + cmd.id.to_s)
       end
  	
  
  end
  
  
  def GetZone(deviceDestination)
  	if( device_.mapDevice_PortChannel_.has_key?(deviceDestination) and
  		device_.mapDevice_PortChannel_[deviceDestination] != nil and
  		!device_.mapDevice_PortChannel_[deviceDestination].empty? )
  		return device_.mapDevice_PortChannel_[deviceDestination].to_i
  	end
  	
  	return -1
  end
  
  
  def ReceiveReport()
  	@buff = conn_.RecvDelimited("]", 1000) 
  	log("Response from ESS: " + @buff + "\n")
  #	log("Calling DecodeReceivedInfo: " + @buff + "\n")
  #	temp_resp=@buff;
  #	DecodeReceivedInfo(temp_resp)
  #	log("End of DecodeReceivedInfo: " + temp_resp + "\n")
  end
  
  def SetVolumeInZone(level,zone)
  # Sets Normalized Volume Level (0..100) in certain zone 
    normlevel = (level.to_i/10.0).round
    log("SetVolumeInZone: volume: " + level.to_s + " ["+ normlevel.to_s + "] => Zone: " + zone + "\n")
  
    serial_command = sprintf("%02d",zone)+sprintf("%02d",normlevel+9);
    Send_RS232_Command(serial_command);
  #  log("313:Set Volume Command: Got Cmd with ID: " + cmd.id_.to_s + " from: " + cmd.devidfrom_.to_s + " to: " + cmd.devidto_.to_s + " | Zone : " + zone.to_s + "\n")
    $ZoneVolumes[zone.to_s] = level.to_i
  
  end
  
  def SetZonesInitialVolumes()
  	log("####### Set Initial Volume Levels for zones\n")
  	device_.mapDevice_PortChannel_.each do |key, value|
  		volume = device_.childdevices_[key].devdata_[208]
  		log("Zone " + value.to_s + " => " + volume.to_s + "\n")
                sleep 1.0
  		SetVolumeInZone(volume.to_i,value)
    		SetDeviceDataInDB( device_.childdevices_[key].devid_, 158, ($ZoneVolumes[value.to_s]).to_s ) # 158 = DEVICEDATA_Volume_Level_CONST
    	end
  	
  end
  
  
  def SetZones(state)
  	log("####### Setting all zones to " + state + "\n")
  	device_.mapDevice_PortChannel_.each do |key, value|
  		$ZoneStatus[value] = state
  		log("Zone " + value.to_s + " => " + state + "\n")
   	end
  end
  
  def SetVolumes(level_int)
  	log("####### Set Volume Levels for all zones\n")
  	device_.mapDevice_PortChannel_.each do |key, value|
  		log("Zone " + value.to_s + " => " + level_int.to_s + "\n")
  		$ZoneVolumes[value.to_s] = level_int
    		SetDeviceDataInDB( device_.childdevices_[key].devid_, 158, ($ZoneVolumes[value.to_s]).to_s ) # 158 = DEVICEDATA_Volume_Level_CONST
    	end
  end


#351 Process IDLE
		

#350 Process Incoming Data
	ReceiveReport() 	

#355 Process Initialize

 log("Initializing ESS GSD Device\n")

 sleep 1.0
 Send_RS232_Command("0002");  #All Zones OFF
 sleep 1.0
 Send_RS232_Command("0020");  #All Zones Max Volume
 sleep 1.0
 Send_RS232_Command("0003");  #All Zones Select A
 sleep 1.0
 $ZoneVolumes = Hash.new()
 $ZoneStatus  = Hash.new()

 SetZones("OFF")
 SetZonesInitialVolumes()

 log("Initialization of ESS GSD Device finished !!!!\n")




#384 Process Receive Command For Child
	SendZoneCommand(cmd) 	

#356 Process Release
 Send_RS232_Command("0002");  #All Zones OFF

 Send_RS232_Command("0020");  #All Zones Max Volume

 Send_RS232_Command("0003");  #All Zones Select A


 log("Shutting Down ESS GSD Device finished !!!!\n")

Misc
#616 Select A
		

#617 Select B
		

Sound & Volume
#97 Mute
		

#313 Set Volume
	SendZoneCommand(cmd) 	

#90 Vol Down
		

#89 Vol Up