Need remote transmitter lambda example for sending raw code

I’m trying to make a remote transmitter lambda call using a raw code. There is a good example in the docs using the Pioneer protocol as show below:

// Example - transmit using the Pioneer protocol
auto call = id(my_transmitter).transmit();
esphome::remote_base::PioneerData data = { rc_code_1, rc_code_2 };
esphome::remote_base::PioneerProtocol().encode(call.get_data(), data);
call.set_send_times(2);
call.perform();

Looking for a similar example for raw code. This is what I have tried to put together so far but I’m at my limit of my coding knowledge and ability to make connection between RemoteTransmitData and the TransmitCall.

script:
  - id: thermostat_script
    then:
    - lambda: |-               
        std::vector<int32_t> hvac_code;
        hvac_code = {+4500,-2700...+4450,-50100};
        // Call remote transmitter using selected code
        auto call = id(my_remote_transmitter).transmit();
        esphome::remote_base::RemoteTransmitData * RTD;
        RTD.set_data(hvac_code);
        RTD.set_carrier_frequency(38);
        esphome::remote_base::RawAction<esphome::remote_base::RemoteTransmitData>().encode(RTD);
        call.get_data(RTD);
        call.perform();

Not sure if this would work for you but ESPHome has a method to send raw data: Remote Transmitter - raw.

Yes, I have used that before but in this case I had a bunch of lambda code in order to pre-process so I wanted to call it from within the lambda. I also wasn’t aware I could template the code value, but have since learned that I can. So I altered my approach and now pre-process and determine the code in the lambda and then return the code. I originally was trying to use a string-vector dictionary in the lambda to store the various codes with a corresponding string key, but I struggled quite a bit and gave up and just went with an if statement.

I think it would still be useful to see how to make the call from within the lambda. I found it difficult to navigate the API and figure out how to connect the various classes that appeared to be needed.

script:
  - id: thermostat_script
    then:
    - remote_transmitter.transmit_raw:
        carrier_frequency: 38kHz
        code: !lambda |-
          if (id(houghton).mode == CLIMATE_MODE_OFF || id(houghton).action == CLIMATE_ACTION_IDLE || id(houghton).action == CLIMATE_ACTION_OFF) {
            ESP_LOGD("thermostat", "Transmitting IR code for OFF");
            return {+4550,-2700,..+4450,-50100};
          } else if (id(houghton).mode == CLIMATE_MODE_COOL && id(houghton).fan_mode == CLIMATE_FAN_LOW) {
            ESP_LOGD("thermostat", "Transmitting IR code for COOL-LOW");
            return {+4500,-2700,..4450,-50100};
......
          } else { // UNKNOWN therefore OFF
            ESP_LOGD("thermostat", "Transmitting IR code for OFF");
            return {+4550,-2700,..4450,-50100};
          }

1 Like

@ncarney - Any progress on this?

For anyone who comes across this now, what worked for me was creating buttons using the button component to transmit the raw ir code (remote_transmitter.transmit_raw) and then using lambda in whatever other component I wanted. You can use id(my_button).press(); inside lambda to trigger it. I also gave the buttons entity_category: config so that they are not forward facing.

button:
  - platform: template
    name: "Phillips Speaker Power"
    id: phillips_power
    entity_category: config
    on_press:
      - remote_transmitter.transmit_raw:
          code: [2706, -859, 481, -866, 479, -418, 484, -415, 481, -864, 925, -421, 481, -417, 933, -861, 480, -418, 482, -415, 481, -417, 480, -418, 480, -420, 479, -416, 457, -443, 479, -417, 936, -411, 456, -890, 480, -418, 481]
          carrier_frequency: 38kHz
  - platform: template
    name: "Phillips Speaker Bluetooth"
    id: phillips_bluetooth
    entity_category: config
    on_press:
      - remote_transmitter.transmit_raw:
          code: [2677, -891, 452, -893, 454, -445, 451, -446, 1346, -1320, 454, -445, 902, -892, 453, -447, 452, -445, 452, -446, 453, -445, 901, -894, 453, -445, 453, -445, 453, -444, 901, -445, 454, -893, 455]
          carrier_frequency: 38kHz
  - platform: template
    name: "Phillips Speaker USB"
    id: phillips_usb
    entity_category: config
    on_press:
      - remote_transmitter.transmit_raw:
          code: [2704, -861, 482, -864, 482, -416, 480, -418, 1374, -1293, 479, -418, 930, -865, 481, -417, 480, -418, 482, -416, 480, -417, 929, -866, 482, -417, 482, -415, 480, -418, 930, -416, 481, -417, 481]
          carrier_frequency: 38kHz

# Select component for Phillips Speaker modes
select:
  - platform: template
    name: "Phillips Speaker"
    id: phillips_mode
    options:
      - "on"
      - "off"
      - "bluetooth"
      - "usb"
    optimistic: True
    set_action: 
      then:
        - lambda: |-
            if (x == "on") {
              id(phillips_power).press();
            } else if (x == "off") {
              id(phillips_power).press();
            } else if (x == "bluetooth") {
              id(phillips_bluetooth).press();
            } else if (x == "usb") {
              id(phillips_usb).press();
            }

I had the same problem. The solution was the code:

auto call = id(ir_transmitter).transmit();
auto data = call.get_data();
esphome::remote_base::RawTimings ir_code = {364, -625, 156, -286, 156, -468, 156, -390, 156, -546, 156};
data->set_data(ir_code);
data->set_carrier_frequency(38000);
call.perform();

Thanks to @ncarney’s script example I was able to do something similar for my Heat & Glo fireplace. The raw codes are very long for this remote (104 raw codes for each button, 16 buttons), so I spent awhile compacting it down; turns out it’s a 4-bit header and 6 bytes of data, 3 of which are constant. I ended up with a nice compact representation and a script to decode and transmit called with a parameter.

# Heat and Glo RF fireplace remote 2166-330 for RC300
# https://www.amazon.com/Universal-Fireplace-2166-330-Replacement-Transmitter/dp/B0DKTJ5KSK
  
# 4-bit header pattern, then 6 bytes of data:
#	F4E133015EA1	on
#	F4E133025FA0	off
#	F4E133318E71	flame1
#	F4E133328F70	flame2
#	F4E13333906F	flame3
#	F4E13334916E	flame4
#	F4E13335926D	flame5
#	F4E133409D62	fan0
#	F4E133419E61	fan1
#	F4E133429F60	fan2
#	F4E13343A05F	fan3
#	F4E13350AD52	light 0
#	F4E13351AE51	light 1
#	F4E13352AF50	light 2
#	F4E13353B04F	light 3
#	F4E13354B14E	light 4
#	F4E13361BE41	aux off

number:
  - platform: template
    name: Fan Level
    icon: mdi:fan
    min_value: 0
    max_value: 3
    step: 1
    optimistic: True
    restore_value: True
    set_action:
      - script.execute:
          id: decode
          code: !lambda |-
            std::string codes[]={"409D62","419E61","429F60","43A05F"}; 
            return codes[(int)x];

  - platform: template
    name: Flame Level
    icon: mdi:fire
    min_value: 1
    max_value: 5
    step: 1
    optimistic: True
    restore_value: True
    set_action:
      - script.execute:
          id: decode
          code: !lambda |-
            std::string codes[]={"empty","318E71","328F70","33906F","34916E","35926D"};
            return codes[(int)x];

  - platform: template
    name: Light Level
    icon: mdi:candelabra-fire
    min_value: 0
    max_value: 3
    step: 1
    optimistic: True
    restore_value: True
    set_action:
      - script.execute:
          id: decode
          code: !lambda  |-
            std::string codes[]={"50AD52","51AE51","52AF50","53B04F"};
            return codes[(int)x];

switch:
  - platform: template
    name: Fireplace Switch
    id: fireplace
    icon: mdi:fireplace
    optimistic: True
    restore_mode: DISABLED
    device_class: switch
    turn_on_action: 
      then:
        - script.execute:
            id: decode
            code: "015EA1"
    turn_off_action: 
      then:
        - script.execute:
            id: decode
            code: "025FA0"

button:
  - platform: template
    name: Fireplace On
    icon: mdi:fireplace
    on_press:
      - switch.turn_on: fireplace
  - platform: template
    name: Fireplace Off
    icon: mdi:fireplace-off
    on_press:
      - switch.turn_off: fireplace

script:
  - id: decode
    mode: single
    parameters:
      code: string
    then:
      - remote_transmitter.transmit_raw:
          repeat:
            times: 11
            wait_time: 92ms
          code: !lambda |-
            // Add common prefix
            std::string fullcode = "F4E133" + code;
            std::vector<int> rawcodes;
            // 4-bit header with distinct off times
            std::vector<int> header = {900,-460,380,-460,900,-460,380,-1280};
            for (int num : header) {
              rawcodes.push_back(num);
            }
            // Decode hex to raw: bytes to bits to rawcode
            // 1=900, 0=380, end-of-bit=-680 EndOfByte=-1500
            for (size_t i = 0; i < fullcode.length(); i += 2) {
              std::string byteString = fullcode.substr(i, 2);
              char byteChar = (char)stoi(byteString, nullptr, 16);
              for (int j = 7; j >= 0; --j) {
                char bit = ((byteChar >> j) & 1);
                if (bit == 1) {
                  rawcodes.push_back(900);
                } else {
                  rawcodes.push_back(380);
                }
                if (j)
                  rawcodes.push_back(-680);
              }
              rawcodes.push_back(-1500);
            }
            ESP_LOGD("fireplace", "hexcode %s, rawcode count %d", fullcode.c_str(), rawcodes.size());
            std::string csv;
            for (int num : rawcodes) {
              csv += std::to_string(num) + " ";    
            }
            ESP_LOGD("fireplace", "rawcodes: %s", csv.c_str());
            return rawcodes;