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)