With this project the Logitech Harmony Hub should be rebuilt as a proof of concept.
It should be possible to start different activities (watch TV, watch Fire TV, watch Bluray, ...) from a web UI/app (virtual openHARMONIE remote), Alexa voice commands or a physical remote.
The Logitech Harmony was deprecated a few years ago, no new hardware is sold anymore. The servers are still running, but it's not clear how long this will be the case.
My home automation heavily relies on the integration of the Harmony into openHAB, e.g. to automatically dim/brighten lights if a stream is played on the Fire TV (done with complicated adb magic...). Or to mute certain notification events on the LaMetric time while watching TV/streaming.
Additionally I have a complex setup with HDMI switches and splitters for sound and a self built Ambilight clone.
That's why I'm looking for an alternative. And what could be better than building it on my own :)
- Logic implementation (which activity needs which devices in which states, ...) will be done with openHAB running on a Raspberry Pi 5.
- Sending of IR commands to devices will be done with a Raspberry Pi Zero W.
- Receiving of IR commands will also be done with the Pi Zero W.
- Communication between both Raspberrys will be done with MQTT.
- ✅ Hardware setup
- ✅ MQTT setup
- ✅ Recording of IR commands from physical remotes
- ✅ Send remote control command from openHAB to a device
- ✅ Receive remote control command from additional physical remote
- ✅ Activity logic
- ✅ UI on smartphone (openHAB sitemaps)
- ✅ Alexa commands
- 0️⃣ 3D printed housing
- 0️⃣ Refactor code to node modules
- 🚧 Refactor documentation and move to a repository
- ✅ Add pictures to the documentation
- 🚧 Bluetooth support
Building this is for experts and a good amount of time, nerves and coffee is necessary. It took me almost three months to figure everything out and implement it.
- openHAB (Openhabian) is set up and running on Raspberry Pi 5 (Bookworm with openHAB 4.2.1)
- openHAB has the JavaScript binding installed and the openhab_rules_tools
- Knowledge about how to use openHAB is already available
- Raspbian Lite is set up and running on Raspberry Pi Zero W (Bullseye, Bookworm does not work properly for this as of March 2025!)
- Knowledge about basic Linux commands is already available
- Knowledge how to solder stuff on a circuit board is already available
- Raspberry Pi 5 (or anything else where openHAB is running)
- Raspberry Pi Zero W
- Raspberry Pi Pico W (for optional Bluetooth)
- IR Receiver VS1838B
- IR LEDs (3 at least)
- some resistors
- 2N2222 transistor
- some (2-wire) cables
- circuit board for IR hardware
- Micro USB to Micro USB cable to connect the Zero and Pico
- soldering equipment
- spare remote control, e.g. from a no longer used TV (will be the "physical" openHARMONIE remote control)
- Broker (Mosquitto) is running on openHAB Raspberry Pi
- openHAB is Publisher (MQTT Client)
- Shell Script on Pi Zero W is Subscriber (MQTT Client)
Note: with this setup, you can easily add multiple Pi Zero W for different rooms and control them all from openHAB.
-
Install
mosquitto
:$ sudo apt update && sudo apt install mosquitto
(possible via openhabian-config as well) -
Configure mosquitto:
$ sudo vi /etc/mosquitto/conf.d/local.conf
Add lines:
listener 1883 allow_anonymous false password_file /etc/mosquitto/credentials
-
Create credentials for openHAB client and shell script client (optional):
$ sudo mosquitto_passwd -b /etc/mosquitto/credentials pi pi
$ sudo mosquitto_passwd -b /etc/mosquitto/credentials oh oh
-
Restart broker
$ sudo systemctl restart mosquitto
- Install MQTT Binding
- Add Bridge MQTT Broker with IP 127.0.0.1 and
oh
/oh
as user/password - Add Thing topic for sending IR commands to devices (topic to publish)
- Add a channel to this thing
- Add Thing topic for receiving IR commands from additonal hardware remote control (optional, topic to subscribe)
- Add a channel to this thing
Note: We use two separate topics instead of one topic with a commandTopic and stateTopic, because communcation is fire & forget. There is no direct "response" to the stateTopic after the commandTopic was published.
See /etc/openhab/things/mqttopenharmonie.things
- Create an item for the
Pi0WHarmonie
channel which can later be updated from rules - Create an item for the
OHHarmonie
channel which can later be used to process context specific IR commands from the additional hardware remote
See /etc/openhab/items/openharmonie.items
-
Install
mosquitto_sub
:$ sudo apt install mosquitto-clients
Note: Use the IP of the openHAB Raspberry Pi where mosquitto
is running instead of broker.home
.
On Pi Zero W:
-
Subscribe to the topic
$ mosquitto_sub -h broker.home -t devices/pi0w/harmonie -u pi -P pi
In openHAB:
- Open API Explorer -> Items -> POST /items/{itemname} -> Try out
- Enter the item name
Send_Harmonie_Command
created above - Enter string
hello world
in request body - Execute (= publish to the topic)
➡️ hello world
is shown on the Pi Zero W command line
For openHAB we need several Things, Items and Rules. Additionally a Sitemap is necessary.
- 2 String Items for publishing/subscribing to MQTT topics. These necessary items were already created in the previous step for the MQTT setup.
Necessary for each physical remote control belonging to a physical device:
-
1 Number Item which has all the buttons of the remote as metadata and updates the
Send_Harmonie_Command
MQTT String Item with theDEVICE BUTTON
combination to be sentTV_HISENSE_EN2BF27H_RemoteControl
,BLURAY_PLAYER_PIONEER_RemoteControl
, ...
Necessary for each physical device (TV, Receiver, HDMI switches ...)
-
1 Switch item for power state. If switch item is turned ON/OFF, the physical remote number item is updated to send a power signal
TV_HISENSE_EN2BF27H_OnOff
,BLURAY_PLAYER_PIONEER_OnOff
, ... -
1 Number item for the input source of the device (HDMI1, HDMI2, ...), e.g. necessary for TVs or HDMI switches
TV_HISENSE_EN2BF27H_Input
,BLURAY_PLAYER_PIONEER_Input
, ...
Note: if the "physical remote" number Item of the device is updated directly via UI (i.e. "a button is pressed on the physical remote"), the switch Item is NOT changed. That's how we can correct device status if IR commands failed
- 1 Number item
OPENHARMONIE_RemoteControl
for the buttons on the virtual openHARMONIE remote, which will be used to control the devices based on the current activity
Necessary for Activities (Watch TV, watch Fire TV, ...)
-
1 String Item "Activity starting" which gets an update as soon as an activity is starting
OPENHARMONIE_Activity_Starting
-
1 String Item "Activity started" which gets an update as soon as an activity is fully started
OPENHARMONIE_Activity_Started
Activites are triggered with the Starting
item. This equals the activity starting channel of the real Harmony.
If the activity is started, we execute different phases and queue their tasks with a Gatekeeper
from the openhab_rules_tools
.
These phases are defined for each activity:
onExit
: tasks to do before the current activity is changedpowerStates
: tasks to set the correct power states for the new activityinputStates
: tasks to set the correct input states for the new activityonEnter
: tasks to do to finish starting the activityonFinish
: tasks which can dynamically be added, e.g. to directly start a favorite TV programm or app
All possible activities are predefined in the metadata of the OPENHARMONIE_Activity_Starting
item to be selected from the UI.
Activities can then be triggered either by buttons of the physical/virtual openHARMONIE or by Alexa voice commands (or anything else available in openHAB).
We need some additional groups to add the virtual remotes of the devices to a sitemap.
See /etc/openhab/etc/sitemaps/openharmonie.sitemap
Note: The sitemap uses icons which are saved in /etc/openhab/icons/classic
. This was the only way to get the icons in the openHAB Android app and in the Basic UI.
The sitemap includes a web view to load a CSS for the Basic UI.
Additionally, the sitemap uses light controls which are not part of the setup here.
We need a group with switches which will be available for Alexa.
See /etc/openhab/etc/items/openharmonie.items
Necessary rules:
- Rule to send a remote control command to the Pi Zero W if a remote control button was pushed (
Remote_Control_Button_Pushed
) (= update MQTT Item with state if a remote control item changes its state) - Rule to send a command to a target device if the button on the physical/virtual openHARMONIE was pushed (
openHARMONIE_Button_Pushed
) - Rule to send a command if the power state item of a device changes (
Device_Power_Pushed
) - Rule to send a command if the input state item of a device changes (
Device_Input_Pushed
) - Rule to process an incoming IR command via MQTT from the physical openHARMONIE (
IR_Command_Received_Via_MQTT
) - Rule to start an activity (
openHARMONIE_Activity_Starting
) - Rule for voice commands (
openHARMONIE_Voice_Commands
)
Rules are written in JavaScript.
Note that some Items (e.g. groups for TV/Reciever, light controls) already exist in my setup from other configuration files.
See /etc/openhab/items/mqttopenharmonie.items
See /etc/openhab/items/openharmonie.items
See /etc/openhab/automation/js/openharmonie.js
See /etc/openhab/sitemaps/openharmonie.sitemap
-
Add a new user with name
openharmonie
$ sudo adduser --no-create-home openharmonie
-
Enter
openharmonie
as password and confirm all other values -
Add the user to the groups
dialout
to send commands via serial connection$ sudo adduser openharmonie dialout
-
Add the user to the group
video
to send commands via IR$ sudo adduser openharmonie video
Note: The files will be placed in the pi
user home folder and owned for simplicity.
Add the following line in /etc/fstab
.
$ sudo vi /etc/fstab
tmpfs /tmp tmpfs defaults,size=64M 0 0
A shell script openharmonie.sh
is running on the Pi Zero W which is receiving MQTT updates and calls another script to send out IR commands (or adb commands for Fire TV).
The script watches if the mosquitto_sub
process is still running every 5 seconds; if not, the script exits.
$ touch ~/openharmonie/openharmonie.sh && chmod +x ~/openharmonie/openharmonie.sh
See /home/pi/openharmonie/openharmonie.sh
-
Create a new file
openharmonie.service
in~/openharmonie
See
/home/pi/openharmonie/openharmonie.service
-
Copy the file to the
systemd
configuration folder$ sudo cp openharmonie.service /etc/systemd/system
-
Reload the systemd configuration
$ sudo systemctl daemon-reload
-
Enable and start the mqtt subscriber service
$ sudo systemctl enable openharmonie.service
$ sudo systemctl start openharmonie.service
-
Check that it's running
$ systemctl status openharmonie.service
● openharmonie.service - MQTT Subscriber for remote control commands Loaded: loaded (/etc/systemd/system/openharmonie.service; enabled; vendor preset: enabled) Active: active (running) since Fri 2024-01-12 12:53:06 CET; 4s ago Main PID: 22855 (openharmonie.sh) Tasks: 5 (limit: 414) CPU: 94ms CGroup: /system.slice/openharmonie.service ├─22855 /bin/bash /home/pi/openharmonie/openharmonie.sh ├─22856 /bin/bash /home/pi/openharmonie/openharmonie.sh ├─22857 /bin/bash /home/pi/openharmonie/openharmonie.sh ├─22859 sleep 5 ├─22860 mosquitto_sub -h broker.home -t devices/pi0w/harmonie -u pi -P pi └─22931 sleep .1 Jan 03 13:35:20 pi0w systemd[1]: Started MQTT Subscriber for remote control commands. Jan 03 13:35:20 pi0w openharmonie.sh[18885]: Listening...
Note that LIRC
is not necessary on Bullseye. Instead, the kernel already supports multiple IR protocols.
Receiving/sending can be done with ir-keytable
and ir-ctl
on Bullseye.
- VS1838B IR Receiver
- Connect VCC of IR Receiver to Raspberry Pi Zero W GPIO 5V
- Connect GND of IR Receiver to Raspberry Pi Zero W GPIO Ground
- Connect OUT of IR Receiver to Raspberry Pi Zero W GPIO 17
- 5mm IR Emitter LED (IF = 20 mA, UF = 1.2 to 1.5 V)
- Resistor with (5 V - 1.2 V) / 20 mA = 175 to 190 Ohm (1.5 to 1.2 V LED voltage range)
- 2N2222 transistor
- 1 kOhm resistor for transistor
Connect IR LED to 5 V pin and use a transistor as switch. The transistor is toggled by the GPIO 18 pin.
- Connect the 190 Ohm resistor to 5 V pin
- Connect anode of LED to resistor
- Connect cathode of LED to collector of 2N2222 transistor
- Connect emitter of transistor to GND pin
- Connect 1 kOhm resistor to GPIO 18 and base of transistor
Note: You can use three IR Emitter LEDs in a row. The resistor then has to be changed to be in the range of 27 - 75 Ohm (1.5 to 1.2 V LED voltage) If more than three LEDs are necessary, they have to be put parallel to the existing three. More current is then drawn from the 5 V pin. Research needs to be done how high this current can be :)
The wiring diagram shows the setup with three IR leds and two resistors with 22 and 10 Ohm.
-
Boot Raspberry Pi Zero W and edit the boot config with the editor of your choice, e.g.
$ sudo vi /boot/config.txt
-
Uncomment lines:
dtoverlay=gpio-ir,gpio_pin=17 dtoverlay=gpio-ir-tx,gpio_pin=18
-
Save & exit vi, then reboot
The IR Receiver and IR LED are now controllable with GPIO pin 17 and 18.
The IR receiver/sender devices are randomly assigned at boot by the kernel to devices /dev/lirc0
and /dev/lirc1
, that's why we create symlinks for them.
With the symlinks, we have a static name for the devices.
-
Get attributes for
/dev/lirc0
$ udevadm info --attribute-walk --path $(udevadm info --query path --name=/dev/lirc0)
-
Get attributes for
/dev/lirc1
$ udevadm info --attribute-walk --path $(udevadm info --query path --name=/dev/lirc1)
With this we get the DRIVERS=="gpio_ir_recv"
and DRIVERS=="gpio-ir-tx"
.
Note the dashes and minus for the DRIVERS!
With this information, we can create a udev configuration file.
-
Create a file
/etc/udev/rules.d/80-lirc.rules
with the following content:$ sudo vi /etc/udev/rules.d/80-lirc.rules
SUBSYSTEM=="lirc", KERNEL=="lirc*", DRIVERS=="gpio-ir-tx", SYMLINK+="lirc-tx" SUBSYSTEM=="lirc", KERNEL=="lirc*", DRIVERS=="gpio_ir_recv", SYMLINK+="lirc-recv"
-
Then save and exit and reboot the Pi.
$ sudo reboot
After the reboot, the IR receiver should be available at /dev/lirc-recv
and the IR sender should be available at /dev/lirc-tx
.
$ ls -l /dev/lirc*
crw-rw---- 1 root video 251, 0 Dec 30 09:56 /dev/lirc0
crw-rw---- 1 root video 251, 1 Dec 30 09:56 /dev/lirc1
lrwxrwxrwx 1 root root 5 Dec 30 09:56 /dev/lirc-recv -> lirc1
lrwxrwxrwx 1 root root 5 Dec 30 09:56 /dev/lirc-tx -> lirc0
We can now use /dev/lirc-tx
to send IR signals and /dev/lirc-recv
to receive commands.
-
Install
ir-keytable
$ sudo apt install ir-keytable
Note: at time of writing, version 1.20.0
of ir-keytable
is used.
-
Confirm it's running
$ ir-keytable
Found /sys/class/rc/rc1/ with: Name: gpio_ir_recv Driver: gpio_ir_recv Default keymap: rc-rc6-mce Input device: /dev/input/event0 LIRC device: /dev/lirc1 Attached BPF protocols: Operation not permitted Supported kernel protocols: lirc rc-5 rc-5-sz jvc sony nec sanyo mce_kbd rc-6 sharp xmp imon Enabled kernel protocols: lirc rc-5 rc-5-sz jvc sony nec sanyo mce_kbd rc-6 sharp xmp imon bus: 25, vendor/product: 0001:0001, version: 0x0100 Repeat delay = 500 ms, repeat period = 125 ms
In the example above, the IR receiver is recognized as /dev/lirc1
and available via the kernel as rc1
.
This may change after each reboot. We use rc1
for the following commands.
-
Enable all supported kernel protocols for the test
$ sudo ir-keytable -p all -s rc1
-
Start the
ir-keytable
test and send an IR command from the remote to the IR receiver. This is the input of the IR receiver already passed through the kernel.$ ir-keytable -t -s rc1 Testing events. Please, press CTRL-C to abort. 1253.120088: lirc protocol(rc5): scancode = 0x1c3d toggle=1 1253.120135: event type EV_MSC(0x04): scancode = 0x1c3d 1253.120135: event type EV_SYN(0x00). 1253.230098: lirc protocol(rc5): scancode = 0x1c3d toggle=1 1253.230144: event type EV_MSC(0x04): scancode = 0x1c3d 1253.230144: event type EV_SYN(0x00). 1254.250104: lirc protocol(rc5): scancode = 0x1c3b 1254.250150: event type EV_MSC(0x04): scancode = 0x1c3b 1254.250150: event type EV_SYN(0x00). 1255.440082: lirc protocol(rc5): scancode = 0x1c1f toggle=1 1255.440121: event type EV_MSC(0x04): scancode = 0x1c1f 1255.440121: event type EV_SYN(0x00). ...
Test different keys of remote. If the the keycodes are different for different remote control buttons, the kernel can decode the IR signal.
➡️ See section "Codes for supported remotes"
If the keycodes are the same for every button, then the kernel does not support the remote protocol and raw codes need to be captured
➡️ See "Raw codes for unsupported remotes"
If the kernel supports the remote control protocol, the key codes can be written down in a toml
configuration file for each remote.
While running $ ir-keytable -t -s rc1
, press each button of the remote and note down its hex code and the protocol of the remote (see example above).
The kernel already decodes the IR command.
Write this into a toml
file for your remote, e.g.
/home/pi/openharmonie/remotes/toml/HAUPPAUGE_PTR005.toml
If a remote sends two codes for each button press, we can note them as e.g. REMOTE_POWER_1
and REMOTE_POWER_2
in the toml
file.
Note: the toml
file can also be loaded to the kernel. Then it's possible to react to the IR control on the Raspberry Pi, e.g. power down the Raspberry Pi when KEY_POWER
is pressed. If you want to control the Raspberry Pi with a remote control, you have to give key/button names which the kernel can map to commands (e.g. KEY_POWER will shut down the Pi).
We will use this to receive IR commands from a remote control which is not used by any device. The IR command received on the Pi Zero W can be sent to openHAB via MQTT and from there we can execute context specific commands. E.g. send back play/pause depending on the current activity to either the cable receiver, the Fire TV or the Bluray player via MQTT to the Pi Zero W.
If the kernel does not support the remote control protocol, the raw IR signal needs to be captured and transmitted. Capturing/sending has to be done in the mode2
format. I couldn't get toml
files with raw codes working.
All in all capturing the raw signals was really challenging most likely because of the "analogue" path from original remote control to the IR receiver.
-
Capture a button press to a file in mode2 format
$ ir-ctl -d /dev/lirc-recv --mode2 --receive=REMOTE_POWER.txt
-
After the button on the remote was pressed, end the recording with
Ctrl+c
.
The file REMOTE_POWER.txt
then contains multiple raw IR signals which consist of pulse
and pause
lines and are separated by a timeout
between two signals.
The timeout
is ignored during sending.
After the file for one button was recorded, test immediately if the signal can be sent to the device, see section "Sending commands not supported by the kernel" below.
If the device does not react correctly or not at all, try to record the raw signal again (e.g. try with longer/shorter button press). Repeat this until the device recognizes the command.
This can be really annoying and I had to capture the signals of the buttons several times until the signals were properly received at the device.
For some buttons, I even recorded their signal 10-20 times in separate files for each button press. Then I checked which of the files could be used to send the command to the device. From these files, I calculated the average pulse/pause length of each pulse/pause line and saved this in a new file. Then I tested if this "average length" file could be used to send the command, which was fortunately always ok.
Sending is done with ir-ctl
.
Sending a code can be done with:
$ ir-ctl -d /dev/lirc-tx -k TV_HISENSE_EN2BF27H.toml -K REMOTE_POWER
The configuration file and the button need to be passed.
If a remote sends two codes for a button press, we have to add them both with another -K
:
$ ir-ctl -d /dev/lirc-tx -k BLURAY_PLAYER_PIONEER.toml -K BUTTON_1 -K BUTTON_2
Sending a code can be done with:
$ ir-ctl -d /dev/lirc-tx --send=KEY_NAME.txt
The file with the pulses and spaces needs to be passed.
Now we bring all of this together in one script which can be called by the MQTT subscriber script running on the Raspberry Pi Zero W.
To realize this, put all files in a folder with the following structure. Supported remotes are put into the toml
folder, unsupported remotes are put in the raw
folder.
Additionally, for each device a shell script is written which gets the key name passed. In the script, the correct amount of codes to be sent has to be configured. Make the scripts executable with chmod
.
Files which were not explained yet will be covered later.
~
|
- openharmonie
|
- mqttsubscriber.sh
- mqttsubscriber.service
|
- remotes
|
- toml
| |
| - TV_HISENSE_EN2BF27H.toml
| - BLURAY_PLAYER_PIONEER.toml
| ...
- raw
| |
| - RECEIVER_SAMSUNG_KD
| | |
| | - REMOTE_POWER.txt
| | - REMOTE_MENU.txt
| | - ...
| - ...
- thd
| |
| - openharmonie.conf
|
- keys
| |
| - keymap.sh
| - keymap_fallback.sh
|
- receive_command.sh
- send_command.sh
- TV_HISENSE_EN2BF27H.sh
- BLURAY_PLAYER_PIONEER.sh
- RECEIVER_SAMSUNG_KD.sh
- FIRETV.sh
...
Examples for different types of devices.
-
Remote control with raw codes:
/home/pi/openharmonie/remotes/RECEIVER_SAMSUNG_KD.sh
-
Remote control with kernel supported codes:
/home/pi/openharmonie/remotes/TV_HISENSE_EN2BF27H.sh
-
Remote control with mixed button behavior:
- buttons which send a code
0xa6a7
first - buttons which send a code
0xa6a0
first - buttons which send the code once
- buttons wich send the code twice
/home/pi/openharmonie/remotes/BLURAY_PLAYER_PIONEER.sh
- buttons which send a code
Additonally we create a send_command.sh
shell script which is called from the MQTT listener:
$ touch ~/remotes/send_command.sh && chmod +x ~/remotes/send_command.sh
See /home/pi/openharmonie/remotes/send_command.sh
An additional remote will be used to control the whole system depending on the current activity. For this we use a spare remote, e.g. from a device which is no longer used.
- Create a
toml
file for this additional remote. See/home/pi/openharmonie/remotes/toml/OPENHARMONIE_TV_PHILIPS.toml
Note: To not control the Raspberry Pi Zero W with the button presses, we have to choose button names which are not used by the Linux system.
I chose BTN_TRIGGER_HAPPY
keys and KEY_MACRO
keys.
-
To permanently load this file after a reboot, copy it to the folder
/etc/rc_keymaps
$ sudo cp /home/pi/openharmonie/remotes/toml/OPENHARMONIE_TV_PHILIPS.toml /etc/rc_keymaps
-
Then add it in the
rc_maps.cfg
$ sudo vi /etc/rc_maps.cfg
Add the following line to the existing maps:
... # Table to automatically load the rc maps for the bundled IR's provided with the # devices supported by the linux kernel #driver table file gpio_ir_recv * OPENHARMONIE_TV_PHILIPS.toml ...
Now the IR commands sent by the remote can be processed as input events.
Processing the input events is done with triggerhappy
. The same keycodes have to be used like in the openHARMONIE toml
file.
-
Create a configuration file for
triggerhappy
/home/pi/openharmonie/remotes/thd/openharmonie.conf
-
Copy the file to the triggerhappy config folder
$ sudo cp /home/pi/openharmonie/remotes/thd/openharmonie.conf /etc/triggerhappy/triggers.d
-
Restart the triggerhappy service
$ sudo systemctl restart triggerhappy.service
Every input event triggered by the remote executes the receive_command.sh
with the generic button name.
Forwarding the button name via MQTT to openHAB can be done with a script.
-
Create it in the remotes folder
$ touch ~/remotes/receive_command.sh && chmod +x ~/remotes/receive_command.sh
See
/home/pi/openharmonie/remotes/receive_command.sh
Note: If using DNS names to communicate with the MQTT broker, it's recommended to install dnsmasq
to have a DNS cache.
Otherwise, each time the mosquitto_pub
is called a DNS lookup is necessary which can cost time.
$ sudo apt install dnsmasq
Then reboot the Pi Zero W. If everything works, you should see an update of the Receive_Harmonie_Command
item after pressing a button on the remote.
The Fire TV can be controlled with adb and Bluetooth. We will use a mixture depending on the commands.
-
Install
adb
on the Pi Zero W$ sudo apt install adb
-
Start the Fire TV and activate developer options somewhere in the settings menu
-
Execute the following command to initialize the connection
adb connect firetv.home:5555
Note: Change
firetv.home
to the IP address of your Fire TV -
A popup is shown on the Fire TV, make sure to set the checkmark and accept the incoming connection.
From now on, we can connect to adb, execute commands and disconnect if the Fire TV is not used.
To control the Fire TV, we will use adb shell sendevent
to send keycodes to the keyboard input device available on the Fire TV.
This is faster than adb shell input keyevent
because it doesn't use the Java layer of Android.
Note: This input device amzkeyboard
suddenly disappeared from my Fire TV after a few days. That's why I added the input keyevent
as fallback.
With adb shell sendevent
we can send the key codes available from the Linux kernel: https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h
If the amzkeyboard
input is not available, we use input keyevent
as fallback. For this we need to map the key codes of the Linux kernel to the key events of Android: https://developer.android.com/reference/android/view/KeyEvent
To start specific apps, we will use adb shell am start
.
-
Create a shell script for Fire TV commands on the Pi Zero W and make it executable
$ touch ~/remotes/FIRETV.sh && chmod +x ~/remotes/FIRETV.sh
See
/home/pi/openharmonie/remotes/FIRETV.sh
-
Additionally create two keymap files to look up the numbers of the key names. One for the Linux key codes, the other one to map them to Android key events.
See
/home/pi/openharmonie/remotes/keys/keymap.sh
and/home/pi/openharmonie/remotes/keys/keymap_fallback.sh
Note: The correct activites for apps can be found via adb:
-
Connect to the Fire TV
$ adb connect firetv.home:5555
-
Search the package name, e.g. for an app called
streamingapp
$ adb shell pm list packages | grep streamingapp
-
If the app cannot be found, list all packages and search it manually
$ adb shell pm list packages
-
Get the activity
$ adb shell cmd package resolve-activity --brief com.mystreamingapp.app
-
If no activity can be found, try
$ adb shell pm dump com.mystreamingapp.app | grep -A 1 "MAIN" | grep com.mystreamingapp.app
Note: the supported keycodes for the amzkeyboard
input can be found with
$ adb shell getevent -lp
Note:
Sending the input keyevent
is quite slow, because the input.jar
needs to be started every time.
Sending an event with sendevent
is faster, but for this the virtual keyboard must be available.
As an alternative to the virtual keyboard, the sendevent
can be sent to the device which appears if the Fire TV remote was used.
But this is not always available.
The best way to control the Fire TV is via Bluetooth. The FIRETV.sh
tries to use the best available way to send the commands.
The Pi Pico W acts as a Bluetooth keyboard to send commands to a Fire TV. It receives the key to send via a serial USB connection from the Pi Zero W. With the provided sketch, it's possible to pair and connect only one device.
See openHARMONIE.ino
.
TODO: Make it possible to pair multiple devices and connect only the one which is relevant for the current activity.
-
Download and install Arduino IDE: https://www.arduino.cc/en/software
-
Open Arduino IDE and go to
File
>Preferences...
>Additional Boards Manager URLs
. Pastehttps://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
there and clickOK
. -
Open the
openHARMONIE.ino
sketch. -
Go to
Tools
>Board
>Boards Manager...
, search forRaspberry Pi Pico
and clickINSTALL
. -
Connect the Pico W with a USB cable to your computer which is running Arduino IDE.
-
In the
Select Board
dropdown, go toSelect Other Board and Port
, selectRaspberry Pi Pico W
in the Boards list. The port should be discoverd as/dev/ttyACM0
on Linux. -
Go to
Tools
>IP/Bluetooth Stack
and selectIPv4 + Bluetooth
. -
Click the
Verify
checkmark and if no errors are shown, click theUpload
checkmark to flash the code to the Pi Pico W. -
After this you should see a device
openHARMONIE
e.g. in the Bluetooth pairing menu of your mobile phone (but don't connect to it).
Simlar to the IR sender/receiver devices a named device for the serial connection between Pi Zero W and Pi Pico W is created.
-
Create a file
/etc/udev/rules.d/80-pico.rules
with the following content:$ sudo vi /etc/udev/rules.d/80-pico.rules
KERNEL=="tty*", ATTRS{manufacturer}=="Raspberry Pi", ATTRS{product}=="Pico W", SYMLINK+="picow"
-
Then save and exit and shut down the Pi Zero W.
-
Connect the Pi Zero W and Pi Pico W with a Micro USB to Micro USB cable. Use the USB port on the Pi Zero W which is not used for power.
-
Start up the Pi Zero W. The serial device should be available as
/dev/picow
and can be used by theFIRETV.sh
.
-
In the settings of the Fire TV, go to Bluetooth and game controllers and search for a new device named
openHARMONIE
. -
Connect to it!
To make everything controllable from Alexa voice commands, a mixture of openHAB items and Alexa routines is necessary.
For every command (e.g. start an activity, start a favorite, pause, play, ...) we need a Switch item with Alexa ToggleState
metadata.
All these items are in one group with Alexa Other
metadata.
With this approach, we don't spam the devices inbox of the Alexa app.
The group and its items can then be discovered in the Alexa app. Afterwards, create routines for each voice command.
E.g. for the voice command "TV on" create a routine which turns the "TV" toggle of the "openHARMONIE" device to ON and so on. For "Pause with openHARMONIE" create a routine which then turns the "Pause" toggle to ON and so on. This has to be done for every activity, favourite, button press, ...
As alternative, you can move the activity and favorite Items out of the group and have them as standalone "Devices" in Alexa. Then you can directly say on/off for them. But for single button presses, the Switch items should be part of the group.
Note: If Alexa says that no new device was found, try to change the label texts. They won't be used anyways for the voice commands, just to identify the correct "switch" when creating a routine.
After everything is configured and set up, place the Raspberry Pi Zero W somewhere where it has WIFI access and point the IR LEDs towards your devices. It's best if the distance to the devices is short.
Then have fun with your new setup!
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.