esphome / issues

Issue Tracker for ESPHome
https://esphome.io/
290 stars 36 forks source link

New NEC remote protocol does not work #2815

Closed seanomat closed 2 years ago

seanomat commented 2 years ago

The problem

Since the update to ESPhome 2021.12 and flashing the device with the new firmware the service remote_transmitter.transmit_nec does not work anymore.

As instructed I reversed the codes for "address" and "command". Either way (original/reversed) does not work. It worked before the update.

Which version of ESPHome has the issue?

2021.12

What type of installation are you using?

Home Assistant Add-on

Which version of Home Assistant has the issue?

2021.12.0

What platform are you using?

ESP32

Board

nodemcu

Component causing the issue

remote_transmitter

Example YAML snippet

remote_transmitter:
  pin: "${component_remote_transmitter_pin}"
  # Set to 100% when working with RF signals, and 50% if working with IR leds
  carrier_duty_percent: 50%

api:
  services:
    - service: send_ir
      variables:
        address_str: string
        command_str: string
      then:
        - remote_transmitter.transmit_nec:
            address: !lambda 'return (int)strtol(address_str.c_str(), NULL, 16);'
            command: !lambda 'return (int)strtol(command_str.c_str(), NULL, 16);'

Anything in the logs that might be useful for us?

nothing

Additional information

To easily reproduce and test, I created a user defined service (see above) and passed address and command as parameters. This worked as expected before the firmware upgrade to the current version:

service: esphome.livingroom_hub_sculpture_send_ir
data:
  address_str: '0x4040'
  command_str: '0x807F'

but not after:

service: esphome.livingroom_hub_sculpture_send_ir
data:
  address_str: '0x0101'
  command_str: '0xFE01'
petergebruers commented 2 years ago

Please try address_str: '0x0202'

def reverse_mask(x):
    x = ((x & 0x5555) << 1) | ((x & 0xAAAA) >> 1)
    x = ((x & 0x3333) << 2) | ((x & 0xCCCC) >> 2)
    x = ((x & 0x0F0F) << 4) | ((x & 0xF0F0) >> 4)
    x = ((x & 0x00FF) << 8) | ((x & 0xFF00) >> 8)
    return x

print(f"0x{reverse_mask(0x4040):04X}")

Gives: 0x0202

The command_str seems to be correct.

print(f"0x{reverse_mask(0x807F):04X}") 0xFE01

Was facing the same issue. I am not good at reversing bits, that's why I grabbed some code from the www...

seanomat commented 2 years ago

EDIT: Oh, wait, I tried with the wrong firmware.

I tried your suggestion to no avail:

service: esphome.livingroom_hub_sculpture_send_ir
data:
  address_str: '0x0202'
  command_str: '0xFE01'

I had problems to understand what has to be done, to revert the values. In the end I converted hex to bits, reversed the order and converted to hex again, as discussed in https://community.home-assistant.io/t/ir-change-in-nec-what-do-i-need-to-do/365840

seanomat commented 2 years ago

Kaum macht man's richtig geht's auch 😁

Your tip was spot on! I did the conversion wrong. I'm not sure how to use your function jet, as I am more of a windows guy, but I'm sure I'll find out. I have to convert about 30 commands. Thank you!

petergebruers commented 2 years ago

What I did was use that python script. What it does is convert the 4 characters to a 16 bit number (so bit 15, 14, ... 0) then reverse the bit order (0, 1, ... 15) and convert that to 4 hex characters

hex (old firmware): 4040 binary: 0100 0000 0100 0000 binary, from right to left (swap all bits, like a mirror): 0000 0010 0000 0010 hex (new firmware): 0202

deftdawg commented 2 years ago

For the benefit of others who hit this challenge... A script that will do most of the work for you with minimal damage to your .yaml files 😎

#!/usr/bin/env python3
# DeftDawg's script for converting ESP NEC IR Remote commands and addresses in place
# 
# In the 2021.12 release of ESPHome the order of bits used in NEC commands was reversed
# https://esphome.io/changelog/2021.12.0.html#nec-remote-protocol
# 
# This script takes your yaml file and applies the coversion automatically while attempting to preserve formatting as much as possible
#
# pip3 install ruamel.yaml pathlib

# chmod +x esphome-yaml-nec-converter.py # make this script executable
# cd esphome
# mkdir new
# for f in $(grep transmit_nec *.yaml | cut -d: -f1 | sort -u); do ../esphome-yaml-nec-converter.py $f > new/$f; done
# 
# These steps will install the required deps, create a new directory and convert each yaml saving them to the new directory.
# Next compare the exsting yaml files with new and replace if everything looks good (apt install colordiff wdiff; wdiff esp.yaml new/esp.yaml | colordiff)
# Finally if everything looks okay, using ESPHome run validate on the changed config and then install it

import sys
from ruamel.yaml import YAML
from pathlib import Path

# @petergebruers method for converting from old to new format
def reverse_mask(x):
    x = ((x & 0x5555) << 1) | ((x & 0xAAAA) >> 1)
    x = ((x & 0x3333) << 2) | ((x & 0xCCCC) >> 2)
    x = ((x & 0x0F0F) << 4) | ((x & 0xF0F0) >> 4)
    x = ((x & 0x00FF) << 8) | ((x & 0xFF00) >> 8)
    return x

# For example, address: 0x84ED , command: 0x13EC becomes 0xB721 and 0x37C8 respectively.
# print(f"0x{reverse_mask(0x84ED):04X} should be 0xB721")
# print(f"0x{reverse_mask(0x13EC):04X} should be 0x37C8")

def convert_file(file_name):
    path = Path(file_name)
    yaml = YAML()
    doc = yaml.load(path)

    for switch in doc['switch']:
        if 'turn_on_action' in switch:
            for action in switch['turn_on_action']:
                if 'remote_transmitter.transmit_nec' in action:
                    action['remote_transmitter.transmit_nec']['address'] = f"0x{reverse_mask(action['remote_transmitter.transmit_nec']['address']):04X}"
                    action['remote_transmitter.transmit_nec']['command'] = f"0x{reverse_mask(action['remote_transmitter.transmit_nec']['command']):04X}"
    yaml.dump(doc, sys.stdout)

convert_file(sys.argv[1])