Sony Bravia TV의 웹 EPG 만들기 (두번째)
XMLHttpRequest로 SOAP 메시지 보내기

2013-04-28   //   alexken작성   //   기술  //  No Comments

이전글에 이어서,

이전 방식(Ruby + Server)의 단점은 집에 웹서버를 운영해야 한다는 점이다.
NAS도 어엿한 웹서버이므로 크게 문제될 것은 없지만, 그냥 단말기에서 동작하도록, 즉 HTML측에서 동작하도록 javascript를 이용해 XMLHttpRequest로 soap메시지를 전달하도록 전부 재 작성하였다.

단순 웹페이지라, 브라우저만 있는 기기라면, 스마트폰/태블릿/PC 어디서나, iOS/Android/WP7.5/Windows/MacOSX 무관하게 동작할 것이나, 현재 하늘이 핸드폰인 Lumia 710(WP7.5)의 IE에서만 동작하지 않았고, 나머지 기기에서는 완벽 동작한다.
사용하기 정말 편하며, 만들어 놓고 아주 뿌듯해 하고 있다.

그럼 본문

ircc Command 목록을 조회하는 코드는 다음과 같다.
중요한 점은 “X-CERS-DEVICE-ID”라는 헤더에 TV에 페어링을 통해 등록된 Mac주소를 함께 전송해야 한다. 없으면 403을 반환한다.

var tv_ip = 'XXX.XXX.XXX.XXX';
var client_mac_addr = 'XX-XX-XX-XX-XX-XX';

function getRemoteCommandList(){
  var xhr = new XMLHttpRequest();
  xhr.open('GET', 'http://' + tv_ip + '/cers/api/getRemoteCommandList',true);
  xhr.setRequestHeader("X-CERS-DEVICE-ID", 'MediaRemote:' + client_mac_addr);
  xhr.onreadystatechange=function() {
    if(xhr.status == 200){
      if (xhr.readyState==4) {
        alert(xhr.responseText);
      }
    }else{
      alert(xhr.status);
    }
  }
  xhr.send(null);
}

getRemoteCommandList를 호출하면 TV는 아래와 같은 xml 형태로 리모콘 값에 해당하는 값의 Base64로 인코딩된 값을 반환한다.
전체 커맨드 목록은 이 글 맨 아래에 정리하였다.

< ?xml version="1.0"?>
<remotecommandlist>
 <command name="Confirm" type="ircc" value="AAAAAQAAAAEAAABlAw==" />
 <command name="Up" type="ircc" value="AAAAAQAAAAEAAAB0Aw==" />
 <command name="Down" type="ircc" value="AAAAAQAAAAEAAAB1Aw==" />
  ...
 <command name="OneTouchRecStop" type="ircc" value="AAAAAgAAABoAAABjAw==" />
 <command name="MuteOn" type="url" value="http://_:80/cers/command/MuteOn"/>
 <command name="MuteOff" type="url" value="http://_:80/cers/command/MuteOff"/>    
</remotecommandlist>

마지막 줄에 type이 “ircc”가 아닌 “url”인 MuteOn과 MuteOff가 있는데, 이 url 유형의 command는 soap으로 보내지 않고, 해당 url에 GET만 수행해도 동작하며, 요청 기기의 MAC주소도 확인하지 않는다.
즉 코딩할 필요 없이 웹브라우저에서 해당주소를 입력하고 Enter만 쳐도 동작한다는 얘기다.
특이점은 command는 MuteOn과 MuteOff가 있지만 MuteOff는 동작하지 않고, MuteOn만 토글 형태로 동작한다.
물리적인 리모콘 버튼에서도 [조용히] 하나만 있기 때문에 일맥상통한다.

[조용히]/[조용히 해제]에 해당하는 코드는 아래와 같다.
getRemoteCommandList함수와의 차이점은 커스텀 헤더인 “X-CERS-DEVICE-ID”는 필요치 않다.

function muteOnOff(){          
  var xhr = new XMLHttpRequest();
  xhr.open('GET', 'http://' + tv_ip + '/cers/command/MuteOn',false);
  xhr.send(null);
}

위에서 작성한 getRemoteCommandList()를 통해서 얻은 ircc code를 가지고 직접 리모콘 키를 누르는 것과 동일한 효과를 내는 함수는 다음과 같다. sendIrccCommand에 ircc 코드를 보낼때 마다 리모콘 키 하나를 누르는 행위와 일치한다.

function sendIrccCommand(ircc){
  var body =
    '< ?xml version="1.0"?>'+
    '<s:envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" '+
    ' s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'+
    '  <s:body>'+
    '    <u:x_sendircc xmlns:u="urn:schemas-sony-com:service:IRCC:1">'+
    '      <ircccode>' + ircc + '</ircccode>'+
    '    </u:x_sendircc>'+
    '  </s:body>'+
    '</s:envelope>';
  var xhr = new XMLHttpRequest();
  xhr.open('POST', 'http://' + tv_ip + '/IRCC',false);
  xhr.setRequestHeader('X-CERS-DEVICE-ID', 'MediaRemote:' + client_mac_addr);
  xhr.setRequestHeader('SOAPAction',     
                        '"urn:schemas-sony-com:service:IRCC:1#X_SendIRCC"');
  xhr.setRequestHeader('Content-Type', 'text/xml; charset=utf-8');
  xhr.send(body);
}

하지만 SBS를 튜닝하기 위해서는 디지털인 경우에 ‘6’, ‘.’, ‘1’과 같이 연속으로 3개의 키를 입력해야 한다.
sendIrccCommand를 좀더 추상화한 setChannel은 아래와 같고, 입력은 ‘6.1’, ‘10.1’ 과 같은 키 시퀀스에 해당하는 문자열을 입력으로 받는다.

function setChannel(ch){

  var num = [
            "AAAAAQAAAAEAAAAJAw==", //0
            "AAAAAQAAAAEAAAAAAw==", //1
            "AAAAAQAAAAEAAAABAw==", //2
            "AAAAAQAAAAEAAAACAw==", //3
            "AAAAAQAAAAEAAAADAw==", //4
            "AAAAAQAAAAEAAAAEAw==", //5
            "AAAAAQAAAAEAAAAFAw==", //6
            "AAAAAQAAAAEAAAAGAw==", //7
            "AAAAAQAAAAEAAAAHAw==", //8
            "AAAAAQAAAAEAAAAIAw=="  //9
  ];
  var dot = "AAAAAgAAAJcAAAAdAw=="  //.

  var c = ch.split("");
  for( var i in c ){
    switch(c[i]){
      case '.' :
        sendIrccCommand( dot );
        break;
      default:
        sendIrccCommand( num[ch[i]] );
        break;
    }
  }
}

type name value
ircc Confirm AAAAAQAAAAEAAABlAw==
ircc Up AAAAAQAAAAEAAAB0Aw==
ircc Down AAAAAQAAAAEAAAB1Aw==
ircc Right AAAAAQAAAAEAAAAzAw==
ircc Left AAAAAQAAAAEAAAA0Aw==
ircc Home AAAAAQAAAAEAAABgAw==
ircc Options AAAAAgAAAJcAAAA2Aw==
ircc Return AAAAAgAAAJcAAAAjAw==
ircc Num1 AAAAAQAAAAEAAAAAAw==
ircc Num2 AAAAAQAAAAEAAAABAw==
ircc Num3 AAAAAQAAAAEAAAACAw==
ircc Num4 AAAAAQAAAAEAAAADAw==
ircc Num5 AAAAAQAAAAEAAAAEAw==
ircc Num6 AAAAAQAAAAEAAAAFAw==
ircc Num7 AAAAAQAAAAEAAAAGAw==
ircc Num8 AAAAAQAAAAEAAAAHAw==
ircc Num9 AAAAAQAAAAEAAAAIAw==
ircc Num0 AAAAAQAAAAEAAAAJAw==
ircc Num11 AAAAAQAAAAEAAAAKAw==
ircc Num12 AAAAAQAAAAEAAAALAw==
ircc Power AAAAAQAAAAEAAAAVAw==
ircc Display AAAAAQAAAAEAAAA6Aw==
ircc VolumeUp AAAAAQAAAAEAAAASAw==
ircc VolumeDown AAAAAQAAAAEAAAATAw==
ircc Mute AAAAAQAAAAEAAAAUAw==
ircc Audio AAAAAQAAAAEAAAAXAw==
ircc SubTitle AAAAAgAAAJcAAAAoAw==
ircc Yellow AAAAAgAAAJcAAAAnAw==
ircc Blue AAAAAgAAAJcAAAAkAw==
ircc Red AAAAAgAAAJcAAAAlAw==
ircc Green AAAAAgAAAJcAAAAmAw==
ircc Play AAAAAgAAAJcAAAAaAw==
ircc Stop AAAAAgAAAJcAAAAYAw==
ircc Pause AAAAAgAAAJcAAAAZAw==
ircc Rewind AAAAAgAAAJcAAAAbAw==
ircc Forward AAAAAgAAAJcAAAAcAw==
ircc Prev AAAAAgAAAJcAAAA8Aw==
ircc Next AAAAAgAAAJcAAAA9Aw==
ircc Replay AAAAAgAAAJcAAAB5Aw==
ircc Advance AAAAAgAAAJcAAAB4Aw==
ircc TopMenu AAAAAgAAABoAAABgAw==
ircc PopUpMenu AAAAAgAAABoAAABhAw==
ircc Eject AAAAAgAAAJcAAABIAw==
ircc Rec AAAAAgAAAJcAAAAgAw==
ircc SyncMenu AAAAAgAAABoAAABYAw==
ircc ClosedCaption AAAAAgAAAKQAAAAQAw==
ircc Teletext AAAAAQAAAAEAAAA/Aw==
ircc ChannelUp AAAAAQAAAAEAAAAQAw==
ircc ChannelDown AAAAAQAAAAEAAAARAw==
ircc Input AAAAAQAAAAEAAAAlAw==
ircc GGuide AAAAAQAAAAEAAAAOAw==
ircc EPG AAAAAgAAAKQAAABbAw==
ircc DOT AAAAAgAAAJcAAAAdAw==
ircc Analog AAAAAgAAAHcAAAANAw==
ircc Exit AAAAAQAAAAEAAABjAw==
ircc Digital AAAAAgAAAJcAAAAyAw==
ircc BS AAAAAgAAAJcAAAAsAw==
ircc CS AAAAAgAAAJcAAAArAw==
ircc BSCS AAAAAgAAAJcAAAAQAw==
ircc Ddata AAAAAgAAAJcAAAAVAw==
ircc InternetWidgets AAAAAgAAABoAAAB6Aw==
ircc InternetVideo AAAAAgAAABoAAAB5Aw==
ircc SceneSelect AAAAAgAAABoAAAB4Aw==
ircc Mode3D AAAAAgAAAHcAAABNAw==
ircc iManual AAAAAgAAABoAAAB7Aw==
ircc Wide AAAAAgAAAKQAAAA9Aw==
ircc Jump AAAAAQAAAAEAAAA7Aw==
ircc PAP AAAAAgAAAKQAAAB3Aw==
ircc MyEPG AAAAAgAAAHcAAABrAw==
ircc ProgramDescription AAAAAgAAAJcAAAAWAw==
ircc WriteChapter AAAAAgAAAHcAAABsAw==
ircc TrackID AAAAAgAAABoAAAB+Aw==
ircc TenKey AAAAAgAAAJcAAAAMAw==
ircc AppliCast AAAAAgAAABoAAABvAw==
ircc acTVila AAAAAgAAABoAAAByAw==
ircc DeleteVideo AAAAAgAAAHcAAAAfAw==
ircc EasyStartUp AAAAAgAAAHcAAABqAw==
ircc OneTouchTimeRec AAAAAgAAABoAAABkAw==
ircc OneTouchView AAAAAgAAABoAAABlAw==
ircc OneTouchRec AAAAAgAAABoAAABiAw==
ircc OneTouchRecStop AAAAAgAAABoAAABjAw==
url MuteOn http://192.168.1.116:80/cers/command/MuteOn
url MuteOff http://192.168.1.116:80/cers/command/MuteOff