mirror of
https://github.com/nicolabs/musiccast-repairkit.git
synced 2026-04-10 16:25:24 +02:00
Initial version
This commit is contained in:
parent
8a083b8440
commit
b0782184f3
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
/node_modules
|
||||
|
||||
.DS_Store
|
||||
32
README.md
Normal file
32
README.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# yamaha-sound-program-by-source
|
||||
|
||||
Instead of having to manually change the sound program when you're switching from e.g. TV to Spotify and vice verse, let the computer do it for you.
|
||||
|
||||
When the input source of your Yamaha receiver changes, the sound program and clear voice settings are automatically changed.
|
||||
|
||||
## Usage
|
||||
|
||||
Specify the following env variables before running `index.js`
|
||||
|
||||
YAMAHA_IP # The ip address to your receiver
|
||||
LOCAL_IP # Your local ip address to use, 0.0.0.0 could work in some setups
|
||||
PORT # Port listening for events from the receiver, defaults to 41100
|
||||
|
||||
Example
|
||||
|
||||
YAMAHA_IP=192.168.1.216 LOCAL_IP=192.168.1.187 node .
|
||||
|
||||
## Misc
|
||||
|
||||
Currently the following mappings from source to sound program are hard coded
|
||||
|
||||
tv => tv_program with clear_voice enabled
|
||||
bd_dvd => tv_program with clear_voice enabled
|
||||
spotify => music with clear_voice disabled
|
||||
airplay => music with clear_voice disabled
|
||||
|
||||
# References
|
||||
|
||||
http://habitech.s3.amazonaws.com/PDFs/YAM/MusicCast/Yamaha%20MusicCast%20HTTP%20simplified%20API%20for%20ControlSystems.pdf
|
||||
|
||||
https://www.pdf-archive.com/2017/04/21/yxc-api-spec-advanced/yxc-api-spec-advanced.pdf
|
||||
136
index.js
Normal file
136
index.js
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
const udp = require('dgram');
|
||||
const server = udp.createSocket('udp4');
|
||||
const http = require('http');
|
||||
|
||||
const YAMAHA_IP = process.env.YAMAHA_IP || '192.168.1.216';
|
||||
const LOCAL_IP = process.env.LOCAL_IP || '192.168.1.187';
|
||||
const INCOMING_EVENT_SERVER_PORT = parseInt(process.env.PORT) || 41100;
|
||||
|
||||
const inputSourceToSoundProgam = inputSource => {
|
||||
switch (inputSource) {
|
||||
case 'airplay':
|
||||
case 'spotify':
|
||||
return 'music';
|
||||
|
||||
case 'tv':
|
||||
case 'bd_dvd':
|
||||
return 'tv_program';
|
||||
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const inputSourceShouldUseClearVoice = inputSource => {
|
||||
switch (inputSource) {
|
||||
case 'tv':
|
||||
case 'bd_dvd':
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const send = (path, headers) =>
|
||||
http
|
||||
.get(
|
||||
{
|
||||
localAddress: LOCAL_IP,
|
||||
host: YAMAHA_IP,
|
||||
path,
|
||||
timeout: 3000,
|
||||
headers: {
|
||||
'User-Agent': 'yamaha-sound-program-by-source',
|
||||
Accept: 'application/vnd.musiccast.v1+json',
|
||||
...headers
|
||||
}
|
||||
},
|
||||
resp => {
|
||||
let data = '';
|
||||
|
||||
resp.on('data', chunk => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
resp.on('end', () => {
|
||||
// console.log(data);
|
||||
});
|
||||
}
|
||||
)
|
||||
.on('error', err => {
|
||||
console.error('Error', err.message);
|
||||
});
|
||||
|
||||
const sendSetSoundProgram = soundProgram =>
|
||||
send(
|
||||
`/YamahaExtendedControl/v1/main/setSoundProgram?program=${soundProgram}`
|
||||
);
|
||||
|
||||
const sendSetClearVoice = enabled =>
|
||||
send(
|
||||
`/YamahaExtendedControl/v1/main/setClearVoice?enabled=${
|
||||
enabled ? 'true' : 'false'
|
||||
}`
|
||||
);
|
||||
|
||||
const sendEventServerAddress = port =>
|
||||
send('/YamahaExtendedControl/v1', {
|
||||
'X-AppName': 'MusicCast/1',
|
||||
'X-AppPort': port
|
||||
});
|
||||
|
||||
const handleIncomingEvent = event => {
|
||||
const isInputChanged = event.main && typeof event.main.input !== 'undefined';
|
||||
|
||||
if (isInputChanged) {
|
||||
const soundProgram = inputSourceToSoundProgam(event.main.input);
|
||||
const setClearVoice = inputSourceShouldUseClearVoice(event.main.input);
|
||||
|
||||
if (soundProgram) {
|
||||
console.log('Changing sound program to', soundProgram);
|
||||
sendSetSoundProgram(soundProgram);
|
||||
}
|
||||
|
||||
console.log('Setting clear voice to', setClearVoice);
|
||||
sendSetClearVoice(setClearVoice);
|
||||
}
|
||||
};
|
||||
|
||||
server.on('close', () => {
|
||||
console.log('Server is closed!');
|
||||
});
|
||||
|
||||
server.on('error', error => {
|
||||
console.error('Error', error);
|
||||
server.close();
|
||||
});
|
||||
|
||||
server.on('message', (msg, _info) => {
|
||||
let body = '';
|
||||
|
||||
try {
|
||||
body = JSON.parse(msg.toString('utf8'));
|
||||
} catch (err) {
|
||||
console.warn('Could not parse event', msg.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
// console.log(body);
|
||||
handleIncomingEvent(body);
|
||||
});
|
||||
|
||||
server.on('listening', () => {
|
||||
const address = server.address();
|
||||
const port = address.port;
|
||||
const ipaddr = address.address;
|
||||
|
||||
console.log(
|
||||
'Incoming event server is listening at port',
|
||||
ipaddr + ':' + port
|
||||
);
|
||||
|
||||
sendEventServerAddress(port);
|
||||
});
|
||||
|
||||
server.bind(INCOMING_EVENT_SERVER_PORT, LOCAL_IP);
|
||||
9
package.json
Normal file
9
package.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "yamaha-sound-program-by-source",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"author": "axelo",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"dependencies": {}
|
||||
}
|
||||
Loading…
Reference in a new issue