#!/usr/bin/perl # -*- mode: cperl; coding: utf-8; -*- use strict; use warnings; use utf8; use lib "/h/hamren/src/post/lib", "."; my $rval = do "common.pm" || die "$0: common.pm failed ($!) [$@]"; #--- Single-line common initializer #--- End of header post( header(), p('My Yamaha RX-V577 receiver came with quite a nice Android app for remote control, but no desktop program. ', 'Looking around, I could find no existing desktop program, so I decided to write my own. '), p('The receiver implements something called the "Yamaha Network Control Alias" protocol, YNCA. ', 'There exists a 1500+ pages reference document, but that is hard to find, so I ', pub_link('yamaha', 'have made a copy available'), '.'), p('The reveiver listens for text commands on TCP port 50000. ', 'Here is the initial conversation between the program and the receiver. ', 'Replies from the receiver are indented relative to commands:'), source_codeq(<<'EOF'), @MAIN:VOL=? @MAIN:VOL=-22.0 @SYS:INPNAME=? @SYS:INPNAMEHDMI1= HDMI1 @SYS:INPNAMEHDMI2= HDMI2 @SYS:INPNAMEHDMI3= HDMI3 @SYS:INPNAMEHDMI4= HDMI4 @SYS:INPNAMEHDMI5= HDMI5 @SYS:INPNAMEHDMI6= HDMI6 @SYS:INPNAMEAV1= TV @SYS:INPNAMEAV2= AV2 @SYS:INPNAMEAV3= AV3 @SYS:INPNAMEAV4= QUAD @SYS:INPNAMEAV5= AV5 @SYS:INPNAMEAV6= AV6 @SYS:INPNAMEUSB= USB @MAIN:SCENENAME=? @MAIN:SCENE1NAME=BD/DVD @MAIN:SCENE2NAME=TV @MAIN:SCENE3NAME=NET @MAIN:SCENE4NAME=RADIO @MAIN:VOL=-22.0 @MAIN:BASIC=? @MAIN:PWR=On @MAIN:SLEEP=Off @MAIN:VOL=-22.0 @MAIN:MUTE=Off @MAIN:SWFRTRIM=0.0 @MAIN:INP=AV2 @MAIN:STRAIGHT=Off @MAIN:ENHANCER=Off @MAIN:SOUNDPRG=Standard @MAIN:3DCINEMA=Off @MAIN:TONEBASS=0.5 @MAIN:TONETREBLE=0.0 @SYS:HDMIOUT1=On @MAIN:EXBASS=Off @MAIN:ADAPTIVEDRC=Off EOF p('There are five commands in that conversation:'), ul('«@MAIN:VOL=?» asks for the current sound volume.', '«@SYS:INPNAME=?» asks for the name of the inputs. These names are configurable.', '«@MAIN:SCENENAME=?» asks for the names of the "scenes". These are also configurable.', '«@MAIN:VOL=-22.0» sets the volume. There is no reply.', '«@MAIN:BASIC=?» asks for a number of basic parameters. The program sends this command every few seconds.'), p('All commands and replies take the form of assignments: «@»group«:»identifier«=»value'), p('There is no additional protocol. A client can connect, send a single text command like "«@MAIN:VOL=-22.0»" and immediately disconnect. ', 'A command must end in a carriage-return followed by a line-feed ("«\r\n»").'), p('To test communication, use «nc». These command lines switch power on and off:'), source_codeq(<<'EOF'), $ echo -e '@MAIN:PWR=On\r' | nc yamaha 50000 $ echo -e '@MAIN:PWR=Standby\r' | nc yamaha 50000 EOF p('The receiver only accepts a single connection. '), h2('Installation'), p('To compile it you need gcc 4.8.0 or later, and either Qt4 or Qt5. ', 'Build instructions:'), ul(pub_link('yamaha', 'Downloaded the archive') . '. Unpack.', 'Modify compiler-related variables in «yamaha.pro».', '«$ qmake»', '«$ make»', 'The executable is «./i386/gcc/bin/yamaha.elf».'), p('Beware of a subtle detail here. ', '«qmake» generates «Makefile», but the makefile that gets used by make is «makefile». ', 'The lowercase «makefile» includes the capitalized «Makefile», and adds a few targets and settings. ', 'If that is confusing, you can change the filename of the generated makefile: '), source_codeq(<<'EOF'), $ qmake -o Makefile.generated EOF p('Remember to change the «include» statementin «makefile».'), p('There is no «install» target. ', 'To install, copy these files: '), ul('«./i386/gcc/bin/yamaha.elf»', '«yamaha.qtss»'), p('The «.qtss» (Qt Style Sheet) file must be in the same directory as the executable, or any parent or (great) grandparent directory. ', 'The application will search for it all the way up to the root directory. '), h2('Running the remote control'), p('Program options: '), ul('«-a» address : The hostname or IP address of the receiver. Defaults to «192.168.222.41».', '«-p» port : The port number that the receiver listens on. Defaults to «50000».', '«-v» : Verbose. Show YNCA commands and replies.', '«-d» : Debug. Lots of output.'), p('To overcome the receivers limitation of a single connection, the program listens on port «50000» and forwards everything to the receiver. ', 'Replies are NOT returned; this is pure one-way communication.'), p('The labels on the mute and power buttons show what will be done if they are pushed. ', 'If the receiver is on, then the button will say "Off".'), footer() );