Tomei os dois conselhos e tentei fazer isso de outra maneira. No final, decidi fazê-lo como uma página da Web, pois eu poderia adicionar funcionalidades adicionais além do volume e ligar / desligar. Eu confiei em muitos pedaços de várias fontes, obrigado a todos eles. Eu usei node.js com sockets.io e depois de um pouco de tentativa e erro consegui algo que funciona, com feedback do dispositivo (para que ele possa inicializar seu estado quando a página carrega) e que não duplique incrementalmente respostas aninhadas (que levei alguns a descobrir como eu não sabia nada sobre nó ou soquetes até alguns dias atrás!) Aqui está. Provavelmente não é muito bonito para quem conhece essas coisas corretamente, mas parece fazer o que eu quero! Para usá-lo, instale o nó e execute o arquivo node.js usando o comando: nodejs main.js Coloque os arquivos index.html e style.css em um subdiretório (relativo à pasta com node.js) chamado "public" ( sem as aspas). Em seguida, aponte seu navegador para o host (executando main.js) com o número da porta anexado à URL (: 8080, neste caso).
A propósito, isso é para um Onkyo TX-SR804, mas também deve funcionar para seus outros receptores controlados por RS-232, usando um adaptador RS-232 para USB (alguns dólares na Amazon).
Este é o arquivo node.js:
var express = require('express');
app = express();
server = require('http').createServer(app);
io = require('socket.io').listen(server);
var SerialPort = require("serialport")
var serialPort = new SerialPort("/dev/ttyUSB0", {
baudRate: 9600,
dataBits: 8,
parity: 'none',
stopBits: 1
}
);
server.listen(8080);
app.use(express.static('public'));
var paramVal = 0;
var countRep = 0;
var countSend = 0;
var buf = new Buffer(16);
var global_socket;
io.sockets.on('connection', function (socket) {
global_socket = socket;
global_socket.on('toOnkyo', function (data) {
paramVal = data.value;
buf.write(paramVal, "utf-8");
serialPort.write(buf);
console.log(paramVal.toString().substr(0,7) + " (" + parseInt(paramVal.toString().substr(5,2),16) + ")\r\n");
global_socket.emit('toOnkyo', {value: paramVal});
console.log('new'+paramVal);
countSend=countSend+1;
console.log('count send '+ countSend);
}
);
}
);
serialPort.on('data', function(data) {
console.log('data received: ' + data.toString().substr(0,7) + " (" + parseInt(data.toString().substr(5,2),16) + ")");
global_socket.emit('onkyoReply', {value: data.toString().substr(0,7)});
countRep=countRep+1;
console.log('count '+ countRep);
}
);
console.log("running");
Este é o arquivo HTML index.html no qual você aponta seu navegador. Ele deve estar em uma pasta chamada public, uma subpasta da que contém node.js. quando você apontar seu navegador para o servidor que executa o node.js, inclua o número da porta (8080 neste caso)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Onkyo Controller</title>
<meta name="viewport" content="width=400px" />
<script src="socket.io/socket.io.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!--
Sent: <span id="sliderVolText"></span><br>
Reply: <span id="replyTextHex"></span>
(Decimal: <span id="replyText10"></span>)<br>
Mode: <span id="modeText"></span><br>
PowerText: <span id="powerText"></span><br>
Power: <span id="power"></span><br>
onoffText: <span id="onoffText"></span><br>
onoff: <span id="onoff"></span>
-->
<span id="sliderVolText" style="display:none"></span>
<span id="replyTextHex" style="display:none"></span>
<span id="replyText10" style="display:none"></span>
<span id="modeText" style="display:none"></span>
<span id="sourceText" style="display:none"></span>
<span id="powerText" style="display:none"></span>
<span id="power" style="display:none"></span>
<span id="onoffText" style="display:none"></span>
<span id="onoff" style="display:none"></span>
<script>
function setCheckedValue(radioObj, newValue) {
if(!radioObj)
return;
var radioLength = radioObj.length;
if(radioLength == undefined) {
radioObj.checked = (radioObj.value == newValue.toString());
return;
}
for(var i = 0; i < radioLength; i++) {
radioObj[i].checked = false;
if(radioObj[i].value == newValue.toString()) {
radioObj[i].checked = true;
}
}
}
</script>
<form class="onoffswitch" >
<input type="checkbox" name="onoffswitch" class="onoffswitch-checkbox" id="myonoffswitch" onclick="showOnoff(checked)">
<label class="onoffswitch-label" for="myonoffswitch">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</form>
<!--
<form name="powerForm" method="get" action="" onsubmit="return false;">
<p> <label for="power0"><input type="radio" value="0x00" name="powerForm" id="power0" onclick="showPower(this.value)"> Off</label>
<label for="power1"><input type="radio" value="0x01" name="powerForm" id="power1" onclick="showPower(this.value)"> On</label>
</form>
-->
<form name="modeForm" method="get" action="" onsubmit="return false;">
<p> <label for="mode0"><input type="radio" value="0x00" name="modeForm" id="mode0" onclick="showMode(this.value)"> Stereo</label>
<label for="mode1"><input type="radio" value="0x01" name="modeForm" id="mode1" onclick="showMode(this.value)"> Direct</label>
<label for="mode2"><input type="radio" value="0x0C" name="modeForm" id="mode2" onclick="showMode(this.value)"> All Ch stereo</label>
<label for="mode3"><input type="radio" value="0x42" name="modeForm" id="mode3" onclick="showMode(this.value)"> THX Cinema</label>
<label for="mode4"><input type="radio" value="0x84" name="modeForm" id="mode4" onclick="showMode(this.value)"> PLllx THX Cinema</label>
<label for="mode5"><input type="radio" value="0x11" name="modeForm" id="mode5" onclick="showMode(this.value)"> Pure</label>
</form>
<br>
<form name="sourceForm" method="get" action="" onsubmit="return false;">
<p> <label for="source0"><input type="radio" value="0x00" name="sourceForm" id="source0" onclick="showSource(this.value)"> Computer</label>
<label for="source2"><input type="radio" value="0x24" name="sourceForm" id="source2" onclick="showSource(this.value)"> FM radio</label>
<!--
<label for="source1"><input type="radio" value="0x01" name="sourceForm" id="source1" onclick="showSource(this.value)"> Video 2</label>
<label for="source3"><input type="radio" value="0x26" name="sourceForm" id="source3" onclick="showSource(this.value)"> Tuner</label>
-->
</form>
<br>
<form name="slideForm" method="get" action="" onsubmit="return false;">
<input type="range" id= "inputSlider" min="0" max="100" value="vol" step="1" oninput="showVolume(this.value)" />
</form>
<br>
<div class="results"></div>
<script type="text/javascript">
// function toggle(checked) {
// var elm = document.getElementById('checkbox');
// if (checked != elm.checked) {
// elm.click();
// }
// }
var socket = io.connect();
var ctrlType = "";
socket.on('toOnkyo', function (data) {
ctrlType = data.value.toString().substr(2,3);
if (ctrlType == "MVL" && !(data.value.toString().substr(5,4)=="QSTN")){
document.getElementById("inputSlider").value = parseInt(data.value.toString().substr(5,2),16);
document.getElementById("sliderVolText").innerHTML = data.value;
}
if (ctrlType == "LMD" && !(data.value.toString().substr(5,4)=="QSTN")){
document.getElementById("mode").value = parseInt(data.value.toString().substr(5,2),16);
document.getElementById("modeText").innerHTML = data.value;
}
if (ctrlType == "PWR" && !(data.value.toString().substr(5,4)=="QSTN") ){
document.getElementById("power").value = parseInt(data.value.toString().substr(5,2),16);
document.getElementById("powerText").innerHTML = data.value;
}
if (ctrlType == "PWR" && !(data.value.toString().substr(5,4)=="QSTN") ){
document.getElementById("onoff").value = parseInt(data.value.toString().substr(5,2),16);
document.getElementById("onoffText").innerHTML = data.value;
}
if (ctrlType == "SLI" && !(data.value.toString().substr(5,4)=="QSTN")){
document.getElementById("source").value = parseInt(data.value.toString().substr(5,2),16);
document.getElementById("sourceText").innerHTML = data.value;
}
});
socket.on('onkyoReply', function (data) {
var done = false;
ctrlType = data.value.toString().substr(2,3);
document.getElementById("replyTextHex").innerHTML = data.value;
document.getElementById("replyText10").innerHTML = parseInt(data.value.toString().substr(5,2),16);
if (ctrlType == "LMD"){
setCheckedValue(document.forms['modeForm'].elements['modeForm'],"0x"+data.value.toString().substr(5,2));
}
if (ctrlType == "SLI"){
setCheckedValue(document.forms['sourceForm'].elements['sourceForm'],"0x"+data.value.toString().substr(5,2));
}
if (ctrlType == "PWR"){
var val = parseInt(data.value.toString().substr(5,2),16);
// setCheckedValue(document.forms['powerForm'].elements['powerForm'],"0x"+data.value.toString().substr(5,2));
document.getElementById("myonoffswitch").checked = (data.value.toString().substr(6,1) != 0);
// console.log(ctrlType);
// If (val == 1) {
// document.getElementById("myonoffswitch").checked = true;
// }
// If (data.value.toString().substr(6,1)=='0') {
// document.getElementById("myonoffswitch").checked = false;
// } else {
// document.getElementById("myonoffswitch").checked = true;
// };
// document.getElementById('myonoffswitch').click();
}
if (ctrlType == "MVL" && done == false){
document.getElementById("inputSlider").value = parseInt(data.value.toString().substr(5,2),16);
document.querySelector('.results').innerHTML = parseInt(data.value.toString().substr(5,2),16);
done = true;
}
});
function showVolume(newValue) {
document.getElementById("sliderVolText").innerHTML="\!1MVL"+("0" + Number(newValue).toString(16)).slice(-2)+"\r\n";
socket.emit('toOnkyo', { value: "\!1MVL"+("0" + Number(newValue).toString(16)).slice(-2)+"\r\n" });
}
function showMode(newValue) {
document.getElementById("modeText").innerHTML="\!1LMD"+("0" + Number(newValue).toString(16)).slice(-2)+"\r\n";
socket.emit('toOnkyo', { value: "\!1LMD"+("0" + Number(newValue).toString(16)).slice(-2)+"\r\n" });
}
function showSource(newValue) {
document.getElementById("sourceText").innerHTML="\!1SLI"+("0" + Number(newValue).toString(16)).slice(-2)+"\r\n";
socket.emit('toOnkyo', { value: "\!1SLI"+("0" + Number(newValue).toString(16)).slice(-2)+"\r\n" });
}
// function showPower(newValue) {
// document.getElementById("powerText").innerHTML="\!1PWR"+("0" + Number(newValue).toString(16)).slice(-2)+"\r\n";
// socket.emit('toOnkyo', { value: "\!1PWR"+("0" + Number(newValue).toString(16)).slice(-2)+"\r\n" });
// }
function showOnoff(newValue) {
document.getElementById("onoffText").innerHTML="\!1PWR"+("0" + Number(newValue).toString(16)).slice(-2)+"\r\n";
socket.emit('toOnkyo', { value: "\!1PWR"+("0" + Number(newValue).toString(16)).slice(-2)+"\r\n" });
}
socket.emit('toOnkyo', { value: "\!1LMDQSTN"+"\r\n" });
socket.emit('toOnkyo', { value: "\!1MVLQSTN"+"\r\n" });
socket.emit('toOnkyo', { value: "\!1PWRQSTN"+"\r\n" });
socket.emit('toOnkyo', { value: "\!1SLIQSTN"+"\r\n" });
</script>
</body>
</html>
Finalmente o arquivo style.css. Deve ser colocado na mesma pasta que o arquivo index.html.
body {
text-align: center;
margin-top: 50px;
background: #50D0A0;
}
input[type=range]{
-webkit-appearance: none;
width: 80%;
}
input[type=range]::-webkit-slider-runnable-track {
height: 10px;
background: #ddd;
border: none;
border-radius: 3px;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
border: none;
height: 32px;
width: 32px;
border-radius: 50%;
background: /* goldenrod */ #34A7C1;
margin-top: -12px;
}
input[type=range]:focus {
outline: none;
}
input[type=range]:focus::-webkit-slider-runnable-track {
background: #ccc;
}
.radioLeft
{
text-align:left;
}
.onoffswitch {
position: relative; width: 90px;
-webkit-user-select:none; -moz-user-select:none; -ms-user-select: none;
left: 50%;
margin-right: -50%;
transform: translate(-50%, -50%)
}
.onoffswitch-checkbox {
display: none;
}
.onoffswitch-label {
display: block; overflow: hidden; cursor: pointer;
border: 2px solid #999999; border-radius: 20px;
}
.onoffswitch-inner {
display: block; width: 200%; margin-left: -100%;
transition: margin 0.3s ease-in 0s;
}
.onoffswitch-inner:before, .onoffswitch-inner:after {
display: block; float: left; width: 50%; height: 30px; padding: 0; line-height: 30px;
font-size: 14px; color: white; font-family: Trebuchet, Arial, sans-serif; font-weight: bold;
box-sizing: border-box;
}
.onoffswitch-inner:before {
content: "ON";
padding-left: 10px;
background-color: #34A7C1; color: #FFFFFF;
}
.onoffswitch-inner:after {
content: "OFF";
padding-right: 10px;
background-color: #EEEEEE; color: #999999;
text-align: right;
}
.onoffswitch-switch {
display: block; width: 18px; margin: 6px;
background: #FFFFFF;
position: absolute; top: 0; bottom: 0;
right: 56px;
border: 2px solid #999999; border-radius: 20px;
transition: all 0.3s ease-in 0s;
}
.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {
margin-left: 0;
}
.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {
right: 0px;
}