Telnet isn't dead
- Home Assistant, Denon, telnet, audio
- 5
- 5
- finished
I bought Denon RCD-N12 AVR (audio/video receiver) and to my surprise I discovered that it supports a beautiful API accessible via… Telnet. In 2025 Telnet seems almost forgotten, with even Python 3.13 removing telnetlib, but here we are.
This is great news, because Telnet’s simplicity makes it almost trivial to script the AVR. Changing equalizer settings is so much easier via telnet than digging through menus and submenus with a remote. I think I’ll build a dedicated menu in Bash just for fun.
Frankly, I didn’t expect to integrate my AVR with Home Assistant. I won’t lie that the thought didn’t cross my mind, but when buying Denon I didn’t research this aspect at all and it wasn’t decisive about the purchase. I thought that maybe new AVR surprise me, the same as my cheap kitchen radio has. (sidenote: It has Frontier Silicon chipset.) With Denon it’s even better: nearly all settings can be controlled through telnet. Just look at the official protocol specification.
There are 2 Home Assistant integrations available for Denon AVRs: denonavr and heos. Only the former one works with my RCD-N12 and unfortunately Media Player exposed by it doesn’t support a simple ON/OFF toggle. Probably because there’s no real “OFF” state, only “STANDBY” From the end-user perspective “standby” works like a real “off”: everything’s off, except wi-fi, which consumes ~2 W of energy. To have this functionality, I had to resort to creating a “telnet switch”.
Basic Telnet
Telnet is a simple plain text protocol used to access a terminal on a remote server. You can asynchronously send commands and receive responses or status updates. Denon protocol uses this functionality extensively. For example, when you listen to FM radio, it continuously communicates updates of RDS (sidenote: Radio Data System) messages.
To connect we type telnet <ip>. Let’s say that IP of my Denon AVR is
192.168.1.206:
$ telnet 192.168.1.206
Trying 192.168.1.206...
Connected to 192.168.1.206.
Escape character is '^]'.
To disconnect press <ctrl> + ] and type close. Otherwise, just type commands
from the AVR protocol and see what happens.
Don’t mind that the text you’re typing in is constantly overwritten, just type
the command and press enter.
For power management, the commands we’re interested in are (<CR> means
carriage return, or \r character):
PWRON<CR>- turns the AVR onPWRSTANDBY<CR>- turns the AVR offPW?<CR>- reports whether AVR is turned on or off
Unfortunately, the simplicity of telnet can bite our asses and we might be
unable to send single commands like echo PWON | telnet <ip>, because telnet
client will process the input immediately, even before the connection. This is
the reality of telnet, there’s no way around it, you have to deal with it. More
sophisticated telnet servers display prompt, so the clients know when server is
ready to accept commands. Denon doesn’t do such things, you have to guess.
Standard tool which works great with telnet is expect. We can either wait for
a PW.* status report (epect -re "PW.*"), which Denon sends automatically
every 10 seconds, or just set some short sleep interval. Even short sleeps work
surprisingly well.
expect << EOF
spawn telnet 192.168.1.206
sleep 0.5
send "PWON\r"
EOF
Telnet in Home Assistant
Fortunately Home Assistant supports sending telnet commands via telnet integration. For now I have created only one switch for Denon, but I suspect that I might create more of them in the future, so I split a configuration to many files. In the main configuration.yaml I have this:
switch: !include_dir_merge_list switches/
and in switches/denon.yaml I have this:
- platform: telnet
switches:
denon_pw_toggle:
resource: 192.168.1.206
port: 23
command_on: "PWON"
command_off: "PWSTANDBY"
command_state: "PW?"
value_template: '{{ value == "PWON" }}'
timeout: 1.0
This creates a boolean switch called “denon_pw_toggle”, which we can add to a dashboard. I don’t know if it’s possible to modify existing media player which HEOS integration exposes, or at least bind the color of media player’s icon and switch state, but I’ll continue to experiment and report back if I discover something. So far I’m happy that I can turn on/off my AVR with a single click.
Next: integrating TTS (Text To Speech) messages in media player, which requires a custom script which will clear the media player’s playlist before sending a new audio file.