Suggestions on an RF Media Remote to Control HA

I’ve been searching and poking at this for awhile and not finding any threads that seem to really relate to what I’m trying to do. I’m trying to retire my Harmony Hub (that comes with a little RF remote) to control my TV and move to something truly and completely local. I bought one of the new Unfolded Circle RM2s, but it’s ending up to be a real frustration to use (longer story that isn’t really relevant for this thread). The good news from the RM2 experiment, is I now have HA fully controlling my media center stuff, so all I need is a way to control HA from a physical media remote (doing on my phone makes me want to stab things).

What I’m looking at now is an RF transceiver running ESPHome:

And then a RF media remote like this Tivo Roamio Replacement Remote:

https://www.amazon.com/TiVo-C00270-Roamio-Replacement-Frequency/dp/B00NQL8ZCU?th=1

I could also try the RF remote from the Harmony too.

One last thing. I run HA in Docker on a Synology NAS that’s in the closet in another room, so things that require a USB dongle probably aren’t an option for me, although if I get desperate I might be willing to figure out how to get USB working via the Docker container.

I can’t find any reference to anyone who’s done anything like this, so before I buy the stuff to try, I thought I’d see if anyone actually has done something like this that I just couldn’t find, has suggestions on alternate hardware, or has any thoughts on the chance of it succeeding.

Thanks.

My latest setup:

  • Use an Amazon Fire TV remote (BLE) (e.g. Temu )
  • Standalone ESP32 reading BLE events from the remote (bonus: acts as a bluetooth proxy)
substitutions:
  device_name: esphid

###########################################

esphome:
  name: "${device_name}"
  friendly_name: ESP32 HID
  # libraries:
  #   - file:///config/custom_libraries/FastLED-idf/components/FastLED-idf

esp32:
  # board: esp32doit-devkit-v1
  board: wemos_d1_mini32
  framework:
    type: esp-idf

external_components:
  # use ble_client_hid from this master branch in GitHub
  - source: github://koying/esphome-ble-remote@master
    components: [ ble_client_hid ]
    refresh: 5min
  # - source: github://koying/esphome-blekeyboard@no_nimble
  #   refresh: always

packages:
  base: !include common/base_nomq.yaml

logger:
#  level: VERBOSE

esp32_ble_tracker:
  scan_parameters:
    interval: 1100ms
    window: 1100ms
    active: true

bluetooth_proxy:
  active: true

# ble_keyboard:
#   id: esp_keyboard_2
#   name: "ESPKeyboard2"
#   manufacturer_id: "ESP"
#   battery_level: 50
#   reconnect: true
#   buttons: true
#   use_default_libs: false

ble_client:
  - id: ble_client_1
    mac_address: "18:BF:1C:88:9D:C4"    #AFTV Alexa remote

ble_client_hid:
  - id: ble_client_hid_1
    ble_client_id: ble_client_1

sensor:
  - platform: template
    id: esp_memory
    icon: mdi:memory
    name: Free Memory
    lambda: return heap_caps_get_free_size(MALLOC_CAP_INTERNAL) / 1024;
    unit_of_measurement: 'kB'
    state_class: measurement
    entity_category: "diagnostic"
  - platform: uptime
    name: Uptime
    id: sys_uptime
  - platform: wifi_signal 
    name: RSSI
    id: wifi_signal_db
    entity_category: "diagnostic"

  - platform: ble_client_hid
    type: battery
    ble_client_hid_id: ble_client_hid_1
    name: "Battery"
  - platform: ble_client_hid
    type: last_event_value
    ble_client_hid_id: ble_client_hid_1
    name: "Last Event Value"

text_sensor:
  - platform: wifi_info
    ip_address:
      name: IP Address
  # - platform: ble_scanner
  #   name: "BLE Devices Scanner"
  - platform: ble_client_hid
    ble_client_hid_id: ble_client_hid_1
    name: "Last Event Usage"

light:
  - platform: status_led
    name: "led"
    pin: GPIO2

binary_sensor:    
  - platform: status
    name: Status

button:
  - platform: restart
    name: "Restart"
  • Events get into HA as esphome.hid_events

Interesting. I have an ESP32 laying around, and the remote for my Sony TV is Bluetooth. I have to figure out to block the IR from the remote and unpair it from the TV so none of those signals get to the TV, but it might be worth a go since I have all the pieces here already.

Note that my setup works with BLE remotes.
The creator of the original esphome component only tested it with AFTV and Shield remotes.

I’m willing to bet the Sony remote is not BLE, but I’d still like to try. I’ve run into a bit of a compile problem on my MacOS machine. I had to use the latest ESP framework version and the platform version 6.5.0 just to get the ESP-IDF framework to work at all on my machine. For completeness, here’s the YAML I’m using:

substitutions:
  # the first is meant to be overridden on the command line
  name: default
  # these can be left alone unless they really need to be changed
  dname: ${name}-hid
  uname: ${name}_hid

esphome:
  name: ${dname}

esp32:
  board: featheresp32
  framework:
    type: esp-idf
    version: latest
    platform_version: 6.5.0

api:
  reboot_timeout: 5min

ota:
  password: ''

logger:

time:
  - platform: sntp
    id: the_time

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_ssid_password

external_components:
  # use ble_client_hid from this master branch in GitHub
  - source: github://koying/esphome-ble-remote@master
    components: [ ble_client_hid ]
    refresh: 5min
  # - source: github://koying/esphome-blekeyboard@no_nimble
  #   refresh: always


esp32_ble_tracker:
  scan_parameters:
    interval: 1100ms
    window: 1100ms
    active: true

bluetooth_proxy:
  active: true

# ble_keyboard:
#   id: esp_keyboard_2
#   name: "ESPKeyboard2"
#   manufacturer_id: "ESP"
#   battery_level: 50
#   reconnect: true
#   buttons: true
#   use_default_libs: false

ble_client:
  - id: ble_client_1
    mac_address: "20:9E:79:3C:67:FB"    #Sony Remote

ble_client_hid:
  - id: ble_client_hid_1
    ble_client_id: ble_client_1

sensor:
  - platform: template
    id: ${uname}_esp_memory
    icon: mdi:memory
    name: ${uname} Free Memory
    lambda: return heap_caps_get_free_size(MALLOC_CAP_INTERNAL) / 1024;
    unit_of_measurement: 'kB'
    state_class: measurement
    entity_category: "diagnostic"
  - platform: wifi_signal
    name:  ${uname} wifi signal
    update_interval: 60s
  - platform: uptime
    id: uptime_seconds
    internal: true
  - platform: ble_client_hid
    type: battery
    ble_client_hid_id: ble_client_hid_1
    name: ${uname} Battery
  - platform: ble_client_hid
    type: last_event_value
    ble_client_hid_id: ble_client_hid_1
    name: ${uname} Last Event Value

text_sensor:
  - platform: template
    name: ${uname} uptime
    entity_category: diagnostic
    lambda: |-
      int seconds = (id(uptime_seconds).state);
      int days = seconds / (24 * 3600);
      seconds = seconds % (24 * 3600);
      int hours = seconds / 3600;
      seconds = seconds % 3600;
      int minutes = seconds /  60;
      seconds = seconds % 60;
      if ( days > 3650 ) {
        return { "Starting up" };
      } else if ( days ) {
        return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
      } else if ( hours ) {
        return { (String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
      } else if ( minutes ) {
        return { (String(minutes) +"m "+ String(seconds) +"s").c_str() };
      } else {
        return { (String(seconds) +"s").c_str() };
      }
    icon: mdi:clock-start
  # - platform: ble_scanner
  #   name: "BLE Devices Scanner"
  - platform: ble_client_hid
    ble_client_hid_id: ble_client_hid_1
    name: ${uname} Last Event Usage

button:
  - platform: restart
    name: ${uname} restart
  - platform: safe_mode
    name: ${uname} restart safe
    icon: mdi:restart-alert

binary_sensor:
  - platform: status
    name: ${uname} status

Anyway, here’s the error from the build log. If it’s an easy fix, great. If not, I’ll just go back to plan A and try the radio remote.

Compiling .pioenvs/livingroom-hid/src/esphome/components/button/button.o
In file included from src/esphome/components/ble_client_hid/hid_parser.cpp:4:
src/esphome/components/ble_client_hid/hid_parser.cpp: In static member function 'static esphome::ble_client_hid::HIDReportMap* esphome::ble_client_hid::HIDReportMap::parse_report_map_data(const uint8_t*, uint16_t)':
src/esphome/components/ble_client_hid/hid_parser.cpp:178:25: error: format '%X' expects argument of type 'unsigned int', but argument 5 has type 'uint32_t' {aka 'long unsigned int'} [-Werror=format=]
  178 |           ESP_LOGD(TAG, "Usage page: %X", report_item_data);
      |                         ^~~~~~~~~~~~~~~~
src/esphome/core/log.h:72:36: note: in definition of macro 'ESPHOME_LOG_FORMAT'
   72 | #define ESPHOME_LOG_FORMAT(format) format
      |                                    ^~~~~~
src/esphome/core/log.h:152:28: note: in expansion of macro 'esph_log_d'
  152 | #define ESP_LOGD(tag, ...) esph_log_d(tag, __VA_ARGS__)
      |                            ^~~~~~~~~~
src/esphome/components/ble_client_hid/hid_parser.cpp:178:11: note: in expansion of macro 'ESP_LOGD'
  178 |           ESP_LOGD(TAG, "Usage page: %X", report_item_data);
      |           ^~~~~~~~
src/esphome/components/ble_client_hid/hid_parser.cpp:178:39: note: format string is defined here
  178 |           ESP_LOGD(TAG, "Usage page: %X", report_item_data);
      |                                      ~^
      |                                       |
      |                                       unsigned int
      |                                      %lX
Compiling .pioenvs/livingroom-hid/src/esphome/components/esp32/core.o
In file included from src/esphome/components/sensor/sensor.h:3,
                 from src/esphome/components/ble_client_hid/ble_client_hid.h:4,
                 from src/esphome/components/ble_client_hid/ble_client_hid.cpp:1:
src/esphome/components/ble_client_hid/ble_client_hid.cpp: In member function 'void esphome::ble_client_hid::BLEClientHID::send_input_report_event(esp_ble_gattc_cb_param_t*)':
src/esphome/components/ble_client_hid/ble_client_hid.cpp:207:19: error: format '%d' expects argument of type 'int', but argument 6 has type 'int32_t' {aka 'long int'} [-Werror=format=]
  207 |     ESP_LOGD(TAG, "Send HID event to HomeAssistant: usage: %s, value: %d", usage.c_str(), value.value);
      |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/esphome/core/log.h:72:36: note: in definition of macro 'ESPHOME_LOG_FORMAT'
   72 | #define ESPHOME_LOG_FORMAT(format) format
      |                                    ^~~~~~
src/esphome/core/log.h:152:28: note: in expansion of macro 'esph_log_d'
  152 | #define ESP_LOGD(tag, ...) esph_log_d(tag, __VA_ARGS__)
      |                            ^~~~~~~~~~
src/esphome/components/ble_client_hid/ble_client_hid.cpp:207:5: note: in expansion of macro 'ESP_LOGD'
  207 |     ESP_LOGD(TAG, "Send HID event to HomeAssistant: usage: %s, value: %d", usage.c_str(), value.value);
      |     ^~~~~~~~
src/esphome/components/ble_client_hid/ble_client_hid.cpp:207:72: note: format string is defined here
  207 |     ESP_LOGD(TAG, "Send HID event to HomeAssistant: usage: %s, value: %d", usage.c_str(), value.value);
      |                                                                       ~^
      |                                                                        |
      |                                                                        int
      |                                                                       %ld
Compiling .pioenvs/livingroom-hid/src/esphome/components/esp32/gpio.o
cc1plus: some warnings being treated as errors
*** [.pioenvs/livingroom-hid/src/esphome/components/ble_client_hid/hid_parser.o] Error 1
cc1plus: some warnings being treated as errors
*** [.pioenvs/livingroom-hid/src/esphome/components/ble_client_hid/ble_client_hid.o] Error 1

Looks like some 32 vs. 64 bits compilation issue.
Are you not using the esphome docker container?

No. In order to be able to use the USB port on my Mac, I can’t use the ESPHome Docker container (Docker on MacOS doesn’t support the USB port). So I have ESPHome installed directly on my Mac. This is the first time I’ve used the ESP-IDF framework for anything. The Arduino framework has worked fine, but I know I can’t use that for this.

I know my OS choices sometimes make things harder for me, but I guess it wouldn’t be a hobby if there weren’t some challenges. '-)

I got it to compile. I found this that led me in the right direction:

I forked your repo and made the updates, and that got me past the compile errors on the custom hid component. I ran into a couple other compile errors that are probably related to using the latest instead of recommended framework, but I got around those by just removing the couple of sensors using the lambda function (one yours, one mine, which I don’t think I need anyway). Next step is to actually get it on the ESP32 and see what happens with the remote.

Just to kind of close the loop on this, I finally got everything working with a different ESP board (the ESP-IDF definitely isn’t ready to deal with S3s, so I grabbed an S2 I had), and the Sony remote is definitely NOT BLE. So while I learned lots of interesting things, I’m still at the same place I was at the start of this thread. Since nobody else has indicated one way or the other if my RF idea will work, I’m just going to buys some stuff later this month and try.

Well, if you really want to go RF, a previous setup I had is with one of those cheap “airmouses”: aliexpress

They come with an USB dongle and are seen as keyboard by HA

1 Like

I’ve marked the post above as the solution, as it will be a good solution for many people. In my case I’m running HA in Docker on a Synology NAS, and getting USB to work on that can be tricky. So I wrote a little python script that will take the keyboard scan codes from a Raspberry Pi with the dongle plugged into it and send them to MQTT where I could then use them in an HA automation to get the remote to control everything. So you get two solutions in one post. '-)