From 8d5e9239028c493bb9111965feb719f7174880c6 Mon Sep 17 00:00:00 2001
From: Aiden
Date: Mon, 6 Jul 2026 19:35:34 +0800
Subject: [PATCH] Add infrared learning function
---
.DS_Store | Bin 0 -> 8196 bytes
athom-rf-ir-remote.yaml | 132 ++++++++++++++++++++++++++-
components/Flash_comp/Flash_comp.cpp | 28 ++++++
components/Flash_comp/Flash_comp.h | 77 ++++++++++++++++
components/Flash_comp/__init__.py | 14 +++
5 files changed, 250 insertions(+), 1 deletion(-)
create mode 100644 .DS_Store
create mode 100644 components/Flash_comp/Flash_comp.cpp
create mode 100644 components/Flash_comp/Flash_comp.h
create mode 100644 components/Flash_comp/__init__.py
diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..8d8fbf7e92e5b1b5ecd3fe07e3cb9aeb60477699
GIT binary patch
literal 8196
zcmeHMTWl0n7(U;$zzh>$s4XqXvMVc4p@gN-ilVUXwvkKAZCiTbvh3~*bi#C|?96Vd
zO-+4LKuvtnsCaoY>H}&NygZpGQ4`|>L882fG4Y8PZ}?zh{Lh@(LV-RJO^tJsbN+Mr
z|10}r4
z)ftjPhHV&uFaozmfYWC-%Twk)$cApr-*dz6P|k94wEjVE$TD3ok+=k@qH@N}S@JAd
zl@Fu`oZ+;W^@?t5wy=+nx*W&KmHXM;Ueg%PXw@ml_OhmJ6nq0yH%MiFk7?`9aH~_a
zb=S8|iv?Mcm5f$9I@;XS5Nk{{jWxtZH*9Ebh&4B_8yi#Pn)sTHdoo9@LEAYYyg_gx
zusJt*cXBG%EVq;ru2rPVu3av>rp#8=Igy^;zW#mtGn!U(jXj2y^~{pdmh}ubqs?>4
z)2(^O-c>M4t<2A7t$dM`jTXyFn~w_Ze8I7-U5;ydrepW@dd6{&uknp0
zE%o-hPQgq0oAsKsztF1U&)7M{jorOQ!6m6EXXe&M@2y|mc>hDm_N|?}FV55ESE*{v
zf}WD;nmNnp%er~P)=m3J_pqsZ1N{_S+cOWFMnO^LqoK>r4jLD$>J~1#dvPQpbgtW!
zcj+zW%wt8<8`0vjES?cr8dqfP?0%h`>~wU#qwcRnB1>1u@;*MFDj3v?X*IH{fef-;
zF1Z}MW@4?PXl~b0LVz$;o0?@s)4Q!~&ai^12RG8x_MVdPIGC*2EGxZvXK;|5am8FD
z)h5f@7YF%+JF>2~bI7nKUMaOjmiyg-Y{A$$O!zBXbjY&*G15FYY1d89DfrRYu{09t
z<6E`px}lTPi|Q6uMbeROS-U_snrW9zLOgv20{C*gjJbGnrRc^#IsBU6^J5%OCd72L
z5ECuG?;{m4Uyfg`X`LcknJg
z#;5oU=kYl%;d@-c54eioaSeY+ic}>nmLk#;X@yiTt&~`!8hk9{8>H{;f4q`D;9P2@0+U#Fy?XqlMWEKXwiBG`gz{b3jWoJ&02v%YjxcY*
zhJ#}$U=(A7_;EarlXwbG;~6}U7YO&K@iL+Q4ZMlB@E*?M96rQHKK{SLH@J)+Z$fnE
zWJKe)sSsUC7i`P1kI;IV4U+o%5he|kF+zFioBFamcJ0aUi7+gfRh
zsSYuoYsaV_qRI=`8yC_yp~g?gN&4wH$%#J id(filter_size)) {
+ id(signal_nvs).save_to_nvs(id(signal_select_index), x);
+ ESP_LOGI("IR_LEARNING", "Signal saved to slot %d, size: %d", id(signal_select_index), x.size());
+ id(is_learning_mode) = false;
+ id(status_text).publish_state("Signal learned!");
+ } else {
+ ESP_LOGW("IR_LEARNING", "Signal too short: %d", x.size());
+ }
+ }
remote_transmitter:
- pin:
@@ -251,6 +289,34 @@ button:
internal: false
entity_category: config
+ # IR learning button
+ - platform: template
+ name: "IR Learn"
+ icon: mdi:remote-tv
+ on_press:
+ - lambda: |-
+ id(is_learning_mode) = true;
+ ESP_LOGI("IR_LEARNING", "Learning mode activated for slot %d", id(signal_select_index));
+ id(status_text).publish_state("Ready to learn...");
+
+ # IR send button
+ - platform: template
+ name: "IR Send"
+ icon: mdi:send
+ on_press:
+ - script.execute: send_raw_signal
+
+ # Clear the signal in the current slot
+ - platform: template
+ name: "IR Clear Slot"
+ icon: mdi:delete
+ on_press:
+ - lambda: |-
+ std::vector empty_signal;
+ id(signal_nvs).save_to_nvs(id(signal_select_index), empty_signal);
+ ESP_LOGI("IR_LEARNING", "Cleared slot %d", id(signal_select_index));
+ id(status_text).publish_state("Slot cleared");
+
light:
- platform: status_led
name: "Status LED"
@@ -278,6 +344,70 @@ text_sensor:
icon: mdi:clock
entity_category: diagnostic
+ # IR learning status display
+ - platform: template
+ name: "IR Learning Status"
+ id: status_text
+ icon: mdi:information
+
+# Signal slot selector
+select:
+ - platform: template
+ name: "IR Signal Slot"
+ id: signal_slot_select
+ optimistic: true
+ options:
+ - "Signal 0"
+ - "Signal 1"
+ - "Signal 2"
+ - "Signal 3"
+ - "Signal 4"
+ - "Signal 5"
+ - "Signal 6"
+ - "Signal 7"
+ - "Signal 8"
+ - "Signal 9"
+ initial_option: "Signal 0"
+ on_value:
+ - lambda: |-
+ if (x == "Signal 0") id(signal_select_index) = 0;
+ else if (x == "Signal 1") id(signal_select_index) = 1;
+ else if (x == "Signal 2") id(signal_select_index) = 2;
+ else if (x == "Signal 3") id(signal_select_index) = 3;
+ else if (x == "Signal 4") id(signal_select_index) = 4;
+ else if (x == "Signal 5") id(signal_select_index) = 5;
+ else if (x == "Signal 6") id(signal_select_index) = 6;
+ else if (x == "Signal 7") id(signal_select_index) = 7;
+ else if (x == "Signal 8") id(signal_select_index) = 8;
+ else if (x == "Signal 9") id(signal_select_index) = 9;
+ ESP_LOGI("IR_LEARNING", "Selected slot: %d", id(signal_select_index));
+
+# Script for sending raw IR signals
+script:
+ - id: send_raw_signal
+ then:
+ - lambda: |-
+ std::vector signal_data = id(signal_nvs).load_from_nvs(id(signal_select_index));
+ if (signal_data.size() > 0) {
+ ESP_LOGI("IR_SEND", "Sending signal from slot %d, size: %d", id(signal_select_index), signal_data.size());
+ id(status_text).publish_state("Sending signal...");
+ auto transmit = id(ir_transmitter).transmit();
+ auto data = transmit.get_data();
+ data->set_carrier_frequency(38000);
+ for (int i = 0; i < signal_data.size(); i++) {
+ if (i % 2 == 0) {
+ data->mark(signal_data[i]);
+ } else {
+ data->space(signal_data[i]);
+ }
+ }
+ transmit.perform();
+ id(status_text).publish_state("Signal sent!");
+ } else {
+ ESP_LOGW("IR_SEND", "No signal stored in slot %d", id(signal_select_index));
+ id(status_text).publish_state("No signal in this slot");
+ }
+
time:
- platform: sntp
id: sntp_time
diff --git a/components/Flash_comp/Flash_comp.cpp b/components/Flash_comp/Flash_comp.cpp
new file mode 100644
index 0000000..badcae0
--- /dev/null
+++ b/components/Flash_comp/Flash_comp.cpp
@@ -0,0 +1,28 @@
+#include "Flash_comp.h"
+
+namespace esphome {
+namespace flash_component {
+
+void Flash_comp::setup() {
+ nvs_flash_init();
+}
+
+bool Flash_comp::clear_signal_by_index(int index) {
+ char key[12];
+ sprintf(key, "irsig_%d", index);
+
+ nvs_handle_t handle;
+ if (nvs_open("storage", NVS_READWRITE, &handle) != ESP_OK) {
+ ESP_LOGE("flash_comp", "Failed to open NVS handle");
+ return false;
+ }
+
+ nvs_erase_key(handle, key);
+ nvs_commit(handle);
+ nvs_close(handle);
+ return true;
+}
+
+
+} // namespace flash_component
+} // namespace esphome
diff --git a/components/Flash_comp/Flash_comp.h b/components/Flash_comp/Flash_comp.h
new file mode 100644
index 0000000..9d9dc26
--- /dev/null
+++ b/components/Flash_comp/Flash_comp.h
@@ -0,0 +1,77 @@
+#pragma once
+
+#include "esphome.h"
+#include "esphome/core/component.h"
+#include
+#include
+
+namespace esphome {
+namespace flash_component {
+
+class Flash_comp : public Component {
+public:
+
+ void setup() override;
+
+ template
+ bool save_to_nvs(int index, const std::vector data){
+ char key[12];
+ sprintf(key, "irsig_%d", index);
+
+ nvs_handle_t handle;
+ if (nvs_open("storage", NVS_READWRITE, &handle) != ESP_OK) {
+ ESP_LOGE("flash_comp", "Failed to open NVS handle");
+ return false;
+ }
+
+ // delete old data
+ nvs_erase_key(handle, key);
+
+ // store new data
+ if (data.size() > 0) {
+ esp_err_t err = nvs_set_blob(handle, key, data.data(), data.size() * sizeof(long int));
+ if (err != ESP_OK) {
+ ESP_LOGE("flash_comp", "Failed to write data: %d", err);
+ return false;
+ }
+ }
+ ESP_LOGI("flash_comp", "Saved %d data to NVS", data.size());
+ // commit changes
+ nvs_commit(handle);
+ nvs_close(handle);
+ return true;
+ }
+
+ template
+ std::vector load_from_nvs(int index) {
+ char key[12];
+ sprintf(key, "irsig_%d", index);
+ std::vector data;
+
+ nvs_handle_t handle;
+ if (nvs_open("storage", NVS_READONLY, &handle) != ESP_OK) {
+ ESP_LOGE("flash_comp", "Failed to open NVS handle");
+ return data;
+ }
+
+ // get data size
+ size_t required_size = 0;
+ esp_err_t err = nvs_get_blob(handle, key, nullptr, &required_size);
+
+ if (err == ESP_OK && required_size > 0) {
+ // add data to vector
+ data.resize(required_size / sizeof(long int));
+ nvs_get_blob(handle, key, data.data(), &required_size);
+ } else if (err != ESP_ERR_NVS_NOT_FOUND) {
+ ESP_LOGE("flash_comp", "Failed to read data: %d", err);
+ }
+ ESP_LOGI("flash_comp", "Loaded %d data from NVS", data.size());
+ nvs_close(handle);
+ return data;
+ }
+
+ bool clear_signal_by_index(int index);
+};
+
+} // namespace flash_component
+} // namespace esphome
diff --git a/components/Flash_comp/__init__.py b/components/Flash_comp/__init__.py
new file mode 100644
index 0000000..8ef4441
--- /dev/null
+++ b/components/Flash_comp/__init__.py
@@ -0,0 +1,14 @@
+import esphome.codegen as cg
+import esphome.config_validation as cv
+from esphome.const import CONF_ID
+
+flash_comp_ns = cg.esphome_ns.namespace('flash_component')
+Flash_comp = flash_comp_ns.class_('Flash_comp', cg.Component)
+
+CONFIG_SCHEMA = cv.Schema({
+ cv.GenerateID(): cv.declare_id(Flash_comp),
+}).extend(cv.COMPONENT_SCHEMA)
+
+async def to_code(config):
+ var = cg.new_Pvariable(config[CONF_ID])
+ await cg.register_component(var, config)