mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Add Micronova component (#4760)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Graham Brown <grahambrown11@gmail.com>
This commit is contained in:
		| @@ -184,6 +184,7 @@ esphome/components/mcp9808/* @k7hpn | ||||
| esphome/components/md5/* @esphome/core | ||||
| esphome/components/mdns/* @esphome/core | ||||
| esphome/components/media_player/* @jesserockz | ||||
| esphome/components/micronova/* @jorre05 | ||||
| esphome/components/microphone/* @jesserockz | ||||
| esphome/components/mics_4514/* @jesserockz | ||||
| esphome/components/midea/* @dudanov | ||||
|   | ||||
							
								
								
									
										69
									
								
								esphome/components/micronova/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								esphome/components/micronova/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import pins | ||||
| from esphome.components import uart | ||||
| from esphome.const import ( | ||||
|     CONF_ID, | ||||
| ) | ||||
|  | ||||
| CODEOWNERS = ["@jorre05"] | ||||
|  | ||||
| DEPENDENCIES = ["uart"] | ||||
|  | ||||
| CONF_MICRONOVA_ID = "micronova_id" | ||||
| CONF_ENABLE_RX_PIN = "enable_rx_pin" | ||||
| CONF_MEMORY_LOCATION = "memory_location" | ||||
| CONF_MEMORY_ADDRESS = "memory_address" | ||||
|  | ||||
| micronova_ns = cg.esphome_ns.namespace("micronova") | ||||
|  | ||||
| MicroNovaFunctions = micronova_ns.enum("MicroNovaFunctions", is_class=True) | ||||
| MICRONOVA_FUNCTIONS_ENUM = { | ||||
|     "STOVE_FUNCTION_SWITCH": MicroNovaFunctions.STOVE_FUNCTION_SWITCH, | ||||
|     "STOVE_FUNCTION_ROOM_TEMPERATURE": MicroNovaFunctions.STOVE_FUNCTION_ROOM_TEMPERATURE, | ||||
|     "STOVE_FUNCTION_THERMOSTAT_TEMPERATURE": MicroNovaFunctions.STOVE_FUNCTION_THERMOSTAT_TEMPERATURE, | ||||
|     "STOVE_FUNCTION_FUMES_TEMPERATURE": MicroNovaFunctions.STOVE_FUNCTION_FUMES_TEMPERATURE, | ||||
|     "STOVE_FUNCTION_STOVE_POWER": MicroNovaFunctions.STOVE_FUNCTION_STOVE_POWER, | ||||
|     "STOVE_FUNCTION_FAN_SPEED": MicroNovaFunctions.STOVE_FUNCTION_FAN_SPEED, | ||||
|     "STOVE_FUNCTION_STOVE_STATE": MicroNovaFunctions.STOVE_FUNCTION_STOVE_STATE, | ||||
|     "STOVE_FUNCTION_MEMORY_ADDRESS_SENSOR": MicroNovaFunctions.STOVE_FUNCTION_MEMORY_ADDRESS_SENSOR, | ||||
|     "STOVE_FUNCTION_WATER_TEMPERATURE": MicroNovaFunctions.STOVE_FUNCTION_WATER_TEMPERATURE, | ||||
|     "STOVE_FUNCTION_WATER_PRESSURE": MicroNovaFunctions.STOVE_FUNCTION_WATER_PRESSURE, | ||||
|     "STOVE_FUNCTION_POWER_LEVEL": MicroNovaFunctions.STOVE_FUNCTION_POWER_LEVEL, | ||||
|     "STOVE_FUNCTION_CUSTOM": MicroNovaFunctions.STOVE_FUNCTION_CUSTOM, | ||||
| } | ||||
|  | ||||
| MicroNova = micronova_ns.class_("MicroNova", cg.PollingComponent, uart.UARTDevice) | ||||
|  | ||||
| CONFIG_SCHEMA = ( | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(MicroNova), | ||||
|             cv.Required(CONF_ENABLE_RX_PIN): pins.gpio_output_pin_schema, | ||||
|         } | ||||
|     ) | ||||
|     .extend(uart.UART_DEVICE_SCHEMA) | ||||
|     .extend(cv.polling_component_schema("60s")) | ||||
| ) | ||||
|  | ||||
|  | ||||
| def MICRONOVA_LISTENER_SCHEMA(default_memory_location, default_memory_address): | ||||
|     return cv.Schema( | ||||
|         { | ||||
|             cv.GenerateID(CONF_MICRONOVA_ID): cv.use_id(MicroNova), | ||||
|             cv.Optional( | ||||
|                 CONF_MEMORY_LOCATION, default=default_memory_location | ||||
|             ): cv.hex_int_range(), | ||||
|             cv.Optional( | ||||
|                 CONF_MEMORY_ADDRESS, default=default_memory_address | ||||
|             ): cv.hex_int_range(), | ||||
|         } | ||||
|     ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await cg.register_component(var, config) | ||||
|     await uart.register_uart_device(var, config) | ||||
|     enable_rx_pin = await cg.gpio_pin_expression(config[CONF_ENABLE_RX_PIN]) | ||||
|     cg.add(var.set_enable_rx_pin(enable_rx_pin)) | ||||
							
								
								
									
										44
									
								
								esphome/components/micronova/button/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								esphome/components/micronova/button/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import button | ||||
|  | ||||
| from .. import ( | ||||
|     MicroNova, | ||||
|     MicroNovaFunctions, | ||||
|     CONF_MICRONOVA_ID, | ||||
|     CONF_MEMORY_LOCATION, | ||||
|     CONF_MEMORY_ADDRESS, | ||||
|     MICRONOVA_LISTENER_SCHEMA, | ||||
|     micronova_ns, | ||||
| ) | ||||
|  | ||||
| MicroNovaButton = micronova_ns.class_("MicroNovaButton", button.Button, cg.Component) | ||||
|  | ||||
| CONF_CUSTOM_BUTTON = "custom_button" | ||||
| CONF_MEMORY_DATA = "memory_data" | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(CONF_MICRONOVA_ID): cv.use_id(MicroNova), | ||||
|         cv.Optional(CONF_CUSTOM_BUTTON): button.button_schema( | ||||
|             MicroNovaButton, | ||||
|         ) | ||||
|         .extend( | ||||
|             MICRONOVA_LISTENER_SCHEMA( | ||||
|                 default_memory_location=0xA0, default_memory_address=0x7D | ||||
|             ) | ||||
|         ) | ||||
|         .extend({cv.Required(CONF_MEMORY_DATA): cv.hex_int_range()}), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     mv = await cg.get_variable(config[CONF_MICRONOVA_ID]) | ||||
|  | ||||
|     if custom_button_config := config.get(CONF_CUSTOM_BUTTON): | ||||
|         bt = await button.new_button(custom_button_config, mv) | ||||
|         cg.add(bt.set_memory_location(custom_button_config.get(CONF_MEMORY_LOCATION))) | ||||
|         cg.add(bt.set_memory_address(custom_button_config.get(CONF_MEMORY_ADDRESS))) | ||||
|         cg.add(bt.set_memory_data(custom_button_config[CONF_MEMORY_DATA])) | ||||
|         cg.add(bt.set_function(MicroNovaFunctions.STOVE_FUNCTION_CUSTOM)) | ||||
							
								
								
									
										18
									
								
								esphome/components/micronova/button/micronova_button.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/micronova/button/micronova_button.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| #include "micronova_button.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace micronova { | ||||
|  | ||||
| void MicroNovaButton::press_action() { | ||||
|   switch (this->get_function()) { | ||||
|     case MicroNovaFunctions::STOVE_FUNCTION_CUSTOM: | ||||
|       this->micronova_->write_address(this->memory_location_, this->memory_address_, this->memory_data_); | ||||
|       break; | ||||
|     default: | ||||
|       break; | ||||
|   } | ||||
|   this->micronova_->update(); | ||||
| } | ||||
|  | ||||
| }  // namespace micronova | ||||
| }  // namespace esphome | ||||
							
								
								
									
										23
									
								
								esphome/components/micronova/button/micronova_button.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								esphome/components/micronova/button/micronova_button.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/micronova/micronova.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/button/button.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace micronova { | ||||
|  | ||||
| class MicroNovaButton : public Component, public button::Button, public MicroNovaButtonListener { | ||||
|  public: | ||||
|   MicroNovaButton(MicroNova *m) : MicroNovaButtonListener(m) {} | ||||
|   void dump_config() override { LOG_BUTTON("", "Micronova button", this); } | ||||
|  | ||||
|   void set_memory_data(uint8_t f) { this->memory_data_ = f; } | ||||
|   uint8_t get_memory_data() { return this->memory_data_; } | ||||
|  | ||||
|  protected: | ||||
|   void press_action() override; | ||||
| }; | ||||
|  | ||||
| }  // namespace micronova | ||||
| }  // namespace esphome | ||||
							
								
								
									
										148
									
								
								esphome/components/micronova/micronova.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								esphome/components/micronova/micronova.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| #include "micronova.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace micronova { | ||||
|  | ||||
| void MicroNova::setup() { | ||||
|   if (this->enable_rx_pin_ != nullptr) { | ||||
|     this->enable_rx_pin_->setup(); | ||||
|     this->enable_rx_pin_->pin_mode(gpio::FLAG_OUTPUT); | ||||
|     this->enable_rx_pin_->digital_write(false); | ||||
|   } | ||||
|   this->current_transmission_.request_transmission_time = millis(); | ||||
|   this->current_transmission_.memory_location = 0; | ||||
|   this->current_transmission_.memory_address = 0; | ||||
|   this->current_transmission_.reply_pending = false; | ||||
|   this->current_transmission_.initiating_listener = nullptr; | ||||
| } | ||||
|  | ||||
| void MicroNova::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "MicroNova:"); | ||||
|   if (this->enable_rx_pin_ != nullptr) { | ||||
|     LOG_PIN("  Enable RX Pin: ", this->enable_rx_pin_); | ||||
|   } | ||||
|  | ||||
|   for (auto &mv_sensor : this->micronova_listeners_) { | ||||
|     mv_sensor->dump_config(); | ||||
|     ESP_LOGCONFIG(TAG, "    sensor location:%02X, address:%02X", mv_sensor->get_memory_location(), | ||||
|                   mv_sensor->get_memory_address()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void MicroNova::update() { | ||||
|   ESP_LOGD(TAG, "Schedule sensor update"); | ||||
|   for (auto &mv_listener : this->micronova_listeners_) { | ||||
|     mv_listener->set_needs_update(true); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void MicroNova::loop() { | ||||
|   // Only read one sensor that needs update per loop | ||||
|   // If STOVE_REPLY_DELAY time has passed since last loop() | ||||
|   // check for a reply from the stove | ||||
|   if ((this->current_transmission_.reply_pending) && | ||||
|       (millis() - this->current_transmission_.request_transmission_time > STOVE_REPLY_DELAY)) { | ||||
|     int stove_reply_value = this->read_stove_reply(); | ||||
|     if (this->current_transmission_.initiating_listener != nullptr) { | ||||
|       this->current_transmission_.initiating_listener->process_value_from_stove(stove_reply_value); | ||||
|       this->current_transmission_.initiating_listener = nullptr; | ||||
|     } | ||||
|     this->current_transmission_.reply_pending = false; | ||||
|     return; | ||||
|   } else if (!this->current_transmission_.reply_pending) { | ||||
|     for (auto &mv_listener : this->micronova_listeners_) { | ||||
|       if (mv_listener->get_needs_update()) { | ||||
|         mv_listener->set_needs_update(false); | ||||
|         this->current_transmission_.initiating_listener = mv_listener; | ||||
|         mv_listener->request_value_from_stove(); | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void MicroNova::request_address(uint8_t location, uint8_t address, MicroNovaSensorListener *listener) { | ||||
|   uint8_t write_data[2] = {0, 0}; | ||||
|   uint8_t trash_rx; | ||||
|  | ||||
|   if (this->reply_pending_mutex_.try_lock()) { | ||||
|     // clear rx buffer. | ||||
|     // Stove hickups may cause late replies in the rx | ||||
|     while (this->available()) { | ||||
|       this->read_byte(&trash_rx); | ||||
|       ESP_LOGW(TAG, "Reading excess byte 0x%02X", trash_rx); | ||||
|     } | ||||
|  | ||||
|     write_data[0] = location; | ||||
|     write_data[1] = address; | ||||
|     ESP_LOGV(TAG, "Request from stove [%02X,%02X]", write_data[0], write_data[1]); | ||||
|  | ||||
|     this->enable_rx_pin_->digital_write(true); | ||||
|     this->write_array(write_data, 2); | ||||
|     this->flush(); | ||||
|     this->enable_rx_pin_->digital_write(false); | ||||
|  | ||||
|     this->current_transmission_.request_transmission_time = millis(); | ||||
|     this->current_transmission_.memory_location = location; | ||||
|     this->current_transmission_.memory_address = address; | ||||
|     this->current_transmission_.reply_pending = true; | ||||
|     this->current_transmission_.initiating_listener = listener; | ||||
|   } else { | ||||
|     ESP_LOGE(TAG, "Reply is pending, skipping read request"); | ||||
|   } | ||||
| } | ||||
|  | ||||
| int MicroNova::read_stove_reply() { | ||||
|   uint8_t reply_data[2] = {0, 0}; | ||||
|   uint8_t checksum = 0; | ||||
|  | ||||
|   // assert enable_rx_pin is false | ||||
|   this->read_array(reply_data, 2); | ||||
|  | ||||
|   this->reply_pending_mutex_.unlock(); | ||||
|   ESP_LOGV(TAG, "Reply from stove [%02X,%02X]", reply_data[0], reply_data[1]); | ||||
|  | ||||
|   checksum = ((uint16_t) this->current_transmission_.memory_location + | ||||
|               (uint16_t) this->current_transmission_.memory_address + (uint16_t) reply_data[1]) & | ||||
|              0xFF; | ||||
|   if (reply_data[0] != checksum) { | ||||
|     ESP_LOGE(TAG, "Checksum missmatch! From [0x%02X:0x%02X] received [0x%02X,0x%02X]. Expected 0x%02X, got 0x%02X", | ||||
|              this->current_transmission_.memory_location, this->current_transmission_.memory_address, reply_data[0], | ||||
|              reply_data[1], checksum, reply_data[0]); | ||||
|     return -1; | ||||
|   } | ||||
|   return ((int) reply_data[1]); | ||||
| } | ||||
|  | ||||
| void MicroNova::write_address(uint8_t location, uint8_t address, uint8_t data) { | ||||
|   uint8_t write_data[4] = {0, 0, 0, 0}; | ||||
|   uint16_t checksum = 0; | ||||
|  | ||||
|   if (this->reply_pending_mutex_.try_lock()) { | ||||
|     write_data[0] = location; | ||||
|     write_data[1] = address; | ||||
|     write_data[2] = data; | ||||
|  | ||||
|     checksum = ((uint16_t) write_data[0] + (uint16_t) write_data[1] + (uint16_t) write_data[2]) & 0xFF; | ||||
|     write_data[3] = checksum; | ||||
|  | ||||
|     ESP_LOGV(TAG, "Write 4 bytes [%02X,%02X,%02X,%02X]", write_data[0], write_data[1], write_data[2], write_data[3]); | ||||
|  | ||||
|     this->enable_rx_pin_->digital_write(true); | ||||
|     this->write_array(write_data, 4); | ||||
|     this->flush(); | ||||
|     this->enable_rx_pin_->digital_write(false); | ||||
|  | ||||
|     this->current_transmission_.request_transmission_time = millis(); | ||||
|     this->current_transmission_.memory_location = location; | ||||
|     this->current_transmission_.memory_address = address; | ||||
|     this->current_transmission_.reply_pending = true; | ||||
|     this->current_transmission_.initiating_listener = nullptr; | ||||
|   } else { | ||||
|     ESP_LOGE(TAG, "Reply is pending, skipping write"); | ||||
|   } | ||||
| } | ||||
|  | ||||
| }  // namespace micronova | ||||
| }  // namespace esphome | ||||
							
								
								
									
										164
									
								
								esphome/components/micronova/micronova.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								esphome/components/micronova/micronova.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,164 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/uart/uart.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/defines.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| #include <vector> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace micronova { | ||||
|  | ||||
| static const char *const TAG = "micronova"; | ||||
| static const int STOVE_REPLY_DELAY = 60; | ||||
|  | ||||
| static const std::string STOVE_STATES[11] = {"Off", | ||||
|                                              "Start", | ||||
|                                              "Pellets loading", | ||||
|                                              "Ignition", | ||||
|                                              "Working", | ||||
|                                              "Brazier Cleaning", | ||||
|                                              "Final Cleaning", | ||||
|                                              "Standby", | ||||
|                                              "No pellets alarm", | ||||
|                                              "No ignition alarm", | ||||
|                                              "Undefined alarm"}; | ||||
|  | ||||
| enum class MicroNovaFunctions { | ||||
|   STOVE_FUNCTION_VOID = 0, | ||||
|   STOVE_FUNCTION_SWITCH = 1, | ||||
|   STOVE_FUNCTION_ROOM_TEMPERATURE = 2, | ||||
|   STOVE_FUNCTION_THERMOSTAT_TEMPERATURE = 3, | ||||
|   STOVE_FUNCTION_FUMES_TEMPERATURE = 4, | ||||
|   STOVE_FUNCTION_STOVE_POWER = 5, | ||||
|   STOVE_FUNCTION_FAN_SPEED = 6, | ||||
|   STOVE_FUNCTION_STOVE_STATE = 7, | ||||
|   STOVE_FUNCTION_MEMORY_ADDRESS_SENSOR = 8, | ||||
|   STOVE_FUNCTION_WATER_TEMPERATURE = 9, | ||||
|   STOVE_FUNCTION_WATER_PRESSURE = 10, | ||||
|   STOVE_FUNCTION_POWER_LEVEL = 11, | ||||
|   STOVE_FUNCTION_CUSTOM = 12 | ||||
| }; | ||||
|  | ||||
| class MicroNova; | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////////// | ||||
| // Interface classes. | ||||
| class MicroNovaBaseListener { | ||||
|  public: | ||||
|   MicroNovaBaseListener() {} | ||||
|   MicroNovaBaseListener(MicroNova *m) { this->micronova_ = m; } | ||||
|   virtual void dump_config(); | ||||
|  | ||||
|   void set_micronova_object(MicroNova *m) { this->micronova_ = m; } | ||||
|  | ||||
|   void set_function(MicroNovaFunctions f) { this->function_ = f; } | ||||
|   MicroNovaFunctions get_function() { return this->function_; } | ||||
|  | ||||
|   void set_memory_location(uint8_t l) { this->memory_location_ = l; } | ||||
|   uint8_t get_memory_location() { return this->memory_location_; } | ||||
|  | ||||
|   void set_memory_address(uint8_t a) { this->memory_address_ = a; } | ||||
|   uint8_t get_memory_address() { return this->memory_address_; } | ||||
|  | ||||
|  protected: | ||||
|   MicroNova *micronova_{nullptr}; | ||||
|   MicroNovaFunctions function_ = MicroNovaFunctions::STOVE_FUNCTION_VOID; | ||||
|   uint8_t memory_location_ = 0; | ||||
|   uint8_t memory_address_ = 0; | ||||
| }; | ||||
|  | ||||
| class MicroNovaSensorListener : public MicroNovaBaseListener { | ||||
|  public: | ||||
|   MicroNovaSensorListener() {} | ||||
|   MicroNovaSensorListener(MicroNova *m) : MicroNovaBaseListener(m) {} | ||||
|   virtual void request_value_from_stove() = 0; | ||||
|   virtual void process_value_from_stove(int value_from_stove) = 0; | ||||
|  | ||||
|   void set_needs_update(bool u) { this->needs_update_ = u; } | ||||
|   bool get_needs_update() { return this->needs_update_; } | ||||
|  | ||||
|  protected: | ||||
|   bool needs_update_ = false; | ||||
| }; | ||||
|  | ||||
| class MicroNovaNumberListener : public MicroNovaBaseListener { | ||||
|  public: | ||||
|   MicroNovaNumberListener(MicroNova *m) : MicroNovaBaseListener(m) {} | ||||
|   virtual void request_value_from_stove() = 0; | ||||
|   virtual void process_value_from_stove(int value_from_stove) = 0; | ||||
|  | ||||
|   void set_needs_update(bool u) { this->needs_update_ = u; } | ||||
|   bool get_needs_update() { return this->needs_update_; } | ||||
|  | ||||
|  protected: | ||||
|   bool needs_update_ = false; | ||||
| }; | ||||
|  | ||||
| class MicroNovaSwitchListener : public MicroNovaBaseListener { | ||||
|  public: | ||||
|   MicroNovaSwitchListener(MicroNova *m) : MicroNovaBaseListener(m) {} | ||||
|   virtual void set_stove_state(bool v) = 0; | ||||
|   virtual bool get_stove_state() = 0; | ||||
|  | ||||
|  protected: | ||||
|   uint8_t memory_data_on_ = 0; | ||||
|   uint8_t memory_data_off_ = 0; | ||||
| }; | ||||
|  | ||||
| class MicroNovaButtonListener : public MicroNovaBaseListener { | ||||
|  public: | ||||
|   MicroNovaButtonListener(MicroNova *m) : MicroNovaBaseListener(m) {} | ||||
|  | ||||
|  protected: | ||||
|   uint8_t memory_data_ = 0; | ||||
| }; | ||||
|  | ||||
| ///////////////////////////////////////////////////////////////////// | ||||
| // Main component class | ||||
| class MicroNova : public PollingComponent, public uart::UARTDevice { | ||||
|  public: | ||||
|   MicroNova() {} | ||||
|  | ||||
|   void setup() override; | ||||
|   void loop() override; | ||||
|   void update() override; | ||||
|   void dump_config() override; | ||||
|   void register_micronova_listener(MicroNovaSensorListener *l) { this->micronova_listeners_.push_back(l); } | ||||
|  | ||||
|   void request_address(uint8_t location, uint8_t address, MicroNovaSensorListener *listener); | ||||
|   void write_address(uint8_t location, uint8_t address, uint8_t data); | ||||
|   int read_stove_reply(); | ||||
|  | ||||
|   void set_enable_rx_pin(GPIOPin *enable_rx_pin) { this->enable_rx_pin_ = enable_rx_pin; } | ||||
|  | ||||
|   void set_current_stove_state(uint8_t s) { this->current_stove_state_ = s; } | ||||
|   uint8_t get_current_stove_state() { return this->current_stove_state_; } | ||||
|  | ||||
|   void set_stove(MicroNovaSwitchListener *s) { this->stove_switch_ = s; } | ||||
|   MicroNovaSwitchListener *get_stove_switch() { return this->stove_switch_; } | ||||
|  | ||||
|  protected: | ||||
|   uint8_t current_stove_state_ = 0; | ||||
|  | ||||
|   GPIOPin *enable_rx_pin_{nullptr}; | ||||
|  | ||||
|   struct MicroNovaSerialTransmission { | ||||
|     uint32_t request_transmission_time; | ||||
|     uint8_t memory_location; | ||||
|     uint8_t memory_address; | ||||
|     bool reply_pending; | ||||
|     MicroNovaSensorListener *initiating_listener; | ||||
|   }; | ||||
|  | ||||
|   Mutex reply_pending_mutex_; | ||||
|   MicroNovaSerialTransmission current_transmission_; | ||||
|  | ||||
|   std::vector<MicroNovaSensorListener *> micronova_listeners_{}; | ||||
|   MicroNovaSwitchListener *stove_switch_{nullptr}; | ||||
| }; | ||||
|  | ||||
| }  // namespace micronova | ||||
| }  // namespace esphome | ||||
							
								
								
									
										110
									
								
								esphome/components/micronova/number/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								esphome/components/micronova/number/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import number | ||||
| from esphome.const import ( | ||||
|     DEVICE_CLASS_TEMPERATURE, | ||||
|     UNIT_CELSIUS, | ||||
|     CONF_STEP, | ||||
| ) | ||||
|  | ||||
| from .. import ( | ||||
|     MicroNova, | ||||
|     MicroNovaFunctions, | ||||
|     CONF_MICRONOVA_ID, | ||||
|     CONF_MEMORY_LOCATION, | ||||
|     CONF_MEMORY_ADDRESS, | ||||
|     MICRONOVA_LISTENER_SCHEMA, | ||||
|     micronova_ns, | ||||
| ) | ||||
|  | ||||
| ICON_FLASH = "mdi:flash" | ||||
|  | ||||
| CONF_THERMOSTAT_TEMPERATURE = "thermostat_temperature" | ||||
| CONF_POWER_LEVEL = "power_level" | ||||
| CONF_MEMORY_WRITE_LOCATION = "memory_write_location" | ||||
|  | ||||
| MicroNovaNumber = micronova_ns.class_("MicroNovaNumber", number.Number, cg.Component) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(CONF_MICRONOVA_ID): cv.use_id(MicroNova), | ||||
|         cv.Optional(CONF_THERMOSTAT_TEMPERATURE): number.number_schema( | ||||
|             MicroNovaNumber, | ||||
|             unit_of_measurement=UNIT_CELSIUS, | ||||
|             device_class=DEVICE_CLASS_TEMPERATURE, | ||||
|         ) | ||||
|         .extend( | ||||
|             MICRONOVA_LISTENER_SCHEMA( | ||||
|                 default_memory_location=0x20, default_memory_address=0x7D | ||||
|             ) | ||||
|         ) | ||||
|         .extend( | ||||
|             { | ||||
|                 cv.Optional( | ||||
|                     CONF_MEMORY_WRITE_LOCATION, default=0xA0 | ||||
|                 ): cv.hex_int_range(), | ||||
|                 cv.Optional(CONF_STEP, default=1.0): cv.float_range(min=0.1, max=10.0), | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional(CONF_POWER_LEVEL): number.number_schema( | ||||
|             MicroNovaNumber, | ||||
|             icon=ICON_FLASH, | ||||
|         ) | ||||
|         .extend( | ||||
|             MICRONOVA_LISTENER_SCHEMA( | ||||
|                 default_memory_location=0x20, default_memory_address=0x7F | ||||
|             ) | ||||
|         ) | ||||
|         .extend( | ||||
|             {cv.Optional(CONF_MEMORY_WRITE_LOCATION, default=0xA0): cv.hex_int_range()} | ||||
|         ), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     mv = await cg.get_variable(config[CONF_MICRONOVA_ID]) | ||||
|  | ||||
|     if thermostat_temperature_config := config.get(CONF_THERMOSTAT_TEMPERATURE): | ||||
|         numb = await number.new_number( | ||||
|             thermostat_temperature_config, | ||||
|             min_value=0, | ||||
|             max_value=40, | ||||
|             step=thermostat_temperature_config.get(CONF_STEP), | ||||
|         ) | ||||
|         cg.add(numb.set_micronova_object(mv)) | ||||
|         cg.add(mv.register_micronova_listener(numb)) | ||||
|         cg.add( | ||||
|             numb.set_memory_location( | ||||
|                 thermostat_temperature_config[CONF_MEMORY_LOCATION] | ||||
|             ) | ||||
|         ) | ||||
|         cg.add( | ||||
|             numb.set_memory_address(thermostat_temperature_config[CONF_MEMORY_ADDRESS]) | ||||
|         ) | ||||
|         cg.add( | ||||
|             numb.set_memory_write_location( | ||||
|                 thermostat_temperature_config.get(CONF_MEMORY_WRITE_LOCATION) | ||||
|             ) | ||||
|         ) | ||||
|         cg.add( | ||||
|             numb.set_function(MicroNovaFunctions.STOVE_FUNCTION_THERMOSTAT_TEMPERATURE) | ||||
|         ) | ||||
|  | ||||
|     if power_level_config := config.get(CONF_POWER_LEVEL): | ||||
|         numb = await number.new_number( | ||||
|             power_level_config, | ||||
|             min_value=1, | ||||
|             max_value=5, | ||||
|             step=1, | ||||
|         ) | ||||
|         cg.add(numb.set_micronova_object(mv)) | ||||
|         cg.add(mv.register_micronova_listener(numb)) | ||||
|         cg.add(numb.set_memory_location(power_level_config[CONF_MEMORY_LOCATION])) | ||||
|         cg.add(numb.set_memory_address(power_level_config[CONF_MEMORY_ADDRESS])) | ||||
|         cg.add( | ||||
|             numb.set_memory_write_location( | ||||
|                 power_level_config.get(CONF_MEMORY_WRITE_LOCATION) | ||||
|             ) | ||||
|         ) | ||||
|         cg.add(numb.set_function(MicroNovaFunctions.STOVE_FUNCTION_POWER_LEVEL)) | ||||
							
								
								
									
										45
									
								
								esphome/components/micronova/number/micronova_number.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								esphome/components/micronova/number/micronova_number.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| #include "micronova_number.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace micronova { | ||||
|  | ||||
| void MicroNovaNumber::process_value_from_stove(int value_from_stove) { | ||||
|   float new_sensor_value = 0; | ||||
|  | ||||
|   if (value_from_stove == -1) { | ||||
|     this->publish_state(NAN); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   switch (this->get_function()) { | ||||
|     case MicroNovaFunctions::STOVE_FUNCTION_THERMOSTAT_TEMPERATURE: | ||||
|       new_sensor_value = ((float) value_from_stove) * this->traits.get_step(); | ||||
|       break; | ||||
|     case MicroNovaFunctions::STOVE_FUNCTION_POWER_LEVEL: | ||||
|       new_sensor_value = (float) value_from_stove; | ||||
|       break; | ||||
|     default: | ||||
|       break; | ||||
|   } | ||||
|   this->publish_state(new_sensor_value); | ||||
| } | ||||
|  | ||||
| void MicroNovaNumber::control(float value) { | ||||
|   uint8_t new_number = 0; | ||||
|  | ||||
|   switch (this->get_function()) { | ||||
|     case MicroNovaFunctions::STOVE_FUNCTION_THERMOSTAT_TEMPERATURE: | ||||
|       new_number = (uint8_t) (value / this->traits.get_step()); | ||||
|       break; | ||||
|     case MicroNovaFunctions::STOVE_FUNCTION_POWER_LEVEL: | ||||
|       new_number = (uint8_t) value; | ||||
|       break; | ||||
|     default: | ||||
|       break; | ||||
|   } | ||||
|   this->micronova_->write_address(this->memory_write_location_, this->memory_address_, new_number); | ||||
|   this->micronova_->update(); | ||||
| } | ||||
|  | ||||
| }  // namespace micronova | ||||
| }  // namespace esphome | ||||
							
								
								
									
										28
									
								
								esphome/components/micronova/number/micronova_number.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								esphome/components/micronova/number/micronova_number.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/micronova/micronova.h" | ||||
| #include "esphome/components/number/number.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace micronova { | ||||
|  | ||||
| class MicroNovaNumber : public number::Number, public MicroNovaSensorListener { | ||||
|  public: | ||||
|   MicroNovaNumber() {} | ||||
|   MicroNovaNumber(MicroNova *m) : MicroNovaSensorListener(m) {} | ||||
|   void dump_config() override { LOG_NUMBER("", "Micronova number", this); } | ||||
|   void control(float value) override; | ||||
|   void request_value_from_stove() override { | ||||
|     this->micronova_->request_address(this->memory_location_, this->memory_address_, this); | ||||
|   } | ||||
|   void process_value_from_stove(int value_from_stove) override; | ||||
|  | ||||
|   void set_memory_write_location(uint8_t l) { this->memory_write_location_ = l; } | ||||
|   uint8_t get_memory_write_location() { return this->memory_write_location_; } | ||||
|  | ||||
|  protected: | ||||
|   uint8_t memory_write_location_ = 0; | ||||
| }; | ||||
|  | ||||
| }  // namespace micronova | ||||
| }  // namespace esphome | ||||
							
								
								
									
										172
									
								
								esphome/components/micronova/sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								esphome/components/micronova/sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,172 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import sensor | ||||
| from esphome.const import ( | ||||
|     DEVICE_CLASS_TEMPERATURE, | ||||
|     DEVICE_CLASS_PRESSURE, | ||||
|     STATE_CLASS_MEASUREMENT, | ||||
|     UNIT_CELSIUS, | ||||
|     UNIT_REVOLUTIONS_PER_MINUTE, | ||||
| ) | ||||
|  | ||||
| from .. import ( | ||||
|     MicroNova, | ||||
|     MicroNovaFunctions, | ||||
|     CONF_MICRONOVA_ID, | ||||
|     CONF_MEMORY_LOCATION, | ||||
|     CONF_MEMORY_ADDRESS, | ||||
|     MICRONOVA_LISTENER_SCHEMA, | ||||
|     micronova_ns, | ||||
| ) | ||||
|  | ||||
| UNIT_BAR = "bar" | ||||
|  | ||||
| MicroNovaSensor = micronova_ns.class_("MicroNovaSensor", sensor.Sensor, cg.Component) | ||||
|  | ||||
| CONF_ROOM_TEMPERATURE = "room_temperature" | ||||
| CONF_FUMES_TEMPERATURE = "fumes_temperature" | ||||
| CONF_STOVE_POWER = "stove_power" | ||||
| CONF_FAN_SPEED = "fan_speed" | ||||
| CONF_WATER_TEMPERATURE = "water_temperature" | ||||
| CONF_WATER_PRESSURE = "water_pressure" | ||||
| CONF_MEMORY_ADDRESS_SENSOR = "memory_address_sensor" | ||||
| CONF_FAN_RPM_OFFSET = "fan_rpm_offset" | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(CONF_MICRONOVA_ID): cv.use_id(MicroNova), | ||||
|         cv.Optional(CONF_ROOM_TEMPERATURE): sensor.sensor_schema( | ||||
|             MicroNovaSensor, | ||||
|             unit_of_measurement=UNIT_CELSIUS, | ||||
|             device_class=DEVICE_CLASS_TEMPERATURE, | ||||
|             state_class=STATE_CLASS_MEASUREMENT, | ||||
|             accuracy_decimals=1, | ||||
|         ).extend( | ||||
|             MICRONOVA_LISTENER_SCHEMA( | ||||
|                 default_memory_location=0x00, default_memory_address=0x01 | ||||
|             ) | ||||
|         ), | ||||
|         cv.Optional(CONF_FUMES_TEMPERATURE): sensor.sensor_schema( | ||||
|             MicroNovaSensor, | ||||
|             unit_of_measurement=UNIT_CELSIUS, | ||||
|             device_class=DEVICE_CLASS_TEMPERATURE, | ||||
|             state_class=STATE_CLASS_MEASUREMENT, | ||||
|             accuracy_decimals=1, | ||||
|         ).extend( | ||||
|             MICRONOVA_LISTENER_SCHEMA( | ||||
|                 default_memory_location=0x00, default_memory_address=0x5A | ||||
|             ) | ||||
|         ), | ||||
|         cv.Optional(CONF_STOVE_POWER): sensor.sensor_schema( | ||||
|             MicroNovaSensor, | ||||
|             state_class=STATE_CLASS_MEASUREMENT, | ||||
|             accuracy_decimals=0, | ||||
|         ).extend( | ||||
|             MICRONOVA_LISTENER_SCHEMA( | ||||
|                 default_memory_location=0x00, default_memory_address=0x34 | ||||
|             ) | ||||
|         ), | ||||
|         cv.Optional(CONF_FAN_SPEED): sensor.sensor_schema( | ||||
|             MicroNovaSensor, | ||||
|             state_class=STATE_CLASS_MEASUREMENT, | ||||
|             unit_of_measurement=UNIT_REVOLUTIONS_PER_MINUTE, | ||||
|         ) | ||||
|         .extend( | ||||
|             MICRONOVA_LISTENER_SCHEMA( | ||||
|                 default_memory_location=0x00, default_memory_address=0x37 | ||||
|             ) | ||||
|         ) | ||||
|         .extend( | ||||
|             {cv.Optional(CONF_FAN_RPM_OFFSET, default=0): cv.int_range(min=0, max=255)} | ||||
|         ), | ||||
|         cv.Optional(CONF_WATER_TEMPERATURE): sensor.sensor_schema( | ||||
|             MicroNovaSensor, | ||||
|             unit_of_measurement=UNIT_CELSIUS, | ||||
|             device_class=DEVICE_CLASS_TEMPERATURE, | ||||
|             state_class=STATE_CLASS_MEASUREMENT, | ||||
|             accuracy_decimals=1, | ||||
|         ).extend( | ||||
|             MICRONOVA_LISTENER_SCHEMA( | ||||
|                 default_memory_location=0x00, default_memory_address=0x3B | ||||
|             ) | ||||
|         ), | ||||
|         cv.Optional(CONF_WATER_PRESSURE): sensor.sensor_schema( | ||||
|             MicroNovaSensor, | ||||
|             unit_of_measurement=UNIT_BAR, | ||||
|             device_class=DEVICE_CLASS_PRESSURE, | ||||
|             state_class=STATE_CLASS_MEASUREMENT, | ||||
|             accuracy_decimals=1, | ||||
|         ).extend( | ||||
|             MICRONOVA_LISTENER_SCHEMA( | ||||
|                 default_memory_location=0x00, default_memory_address=0x3C | ||||
|             ) | ||||
|         ), | ||||
|         cv.Optional(CONF_MEMORY_ADDRESS_SENSOR): sensor.sensor_schema( | ||||
|             MicroNovaSensor, | ||||
|         ).extend( | ||||
|             MICRONOVA_LISTENER_SCHEMA( | ||||
|                 default_memory_location=0x00, default_memory_address=0x00 | ||||
|             ) | ||||
|         ), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     mv = await cg.get_variable(config[CONF_MICRONOVA_ID]) | ||||
|  | ||||
|     if room_temperature_config := config.get(CONF_ROOM_TEMPERATURE): | ||||
|         sens = await sensor.new_sensor(room_temperature_config, mv) | ||||
|         cg.add(mv.register_micronova_listener(sens)) | ||||
|         cg.add(sens.set_memory_location(room_temperature_config[CONF_MEMORY_LOCATION])) | ||||
|         cg.add(sens.set_memory_address(room_temperature_config[CONF_MEMORY_ADDRESS])) | ||||
|         cg.add(sens.set_function(MicroNovaFunctions.STOVE_FUNCTION_ROOM_TEMPERATURE)) | ||||
|  | ||||
|     if fumes_temperature_config := config.get(CONF_FUMES_TEMPERATURE): | ||||
|         sens = await sensor.new_sensor(fumes_temperature_config, mv) | ||||
|         cg.add(mv.register_micronova_listener(sens)) | ||||
|         cg.add(sens.set_memory_location(fumes_temperature_config[CONF_MEMORY_LOCATION])) | ||||
|         cg.add(sens.set_memory_address(fumes_temperature_config[CONF_MEMORY_ADDRESS])) | ||||
|         cg.add(sens.set_function(MicroNovaFunctions.STOVE_FUNCTION_FUMES_TEMPERATURE)) | ||||
|  | ||||
|     if stove_power_config := config.get(CONF_STOVE_POWER): | ||||
|         sens = await sensor.new_sensor(stove_power_config, mv) | ||||
|         cg.add(mv.register_micronova_listener(sens)) | ||||
|         cg.add(sens.set_memory_location(stove_power_config[CONF_MEMORY_LOCATION])) | ||||
|         cg.add(sens.set_memory_address(stove_power_config[CONF_MEMORY_ADDRESS])) | ||||
|         cg.add(sens.set_function(MicroNovaFunctions.STOVE_FUNCTION_STOVE_POWER)) | ||||
|  | ||||
|     if fan_speed_config := config.get(CONF_FAN_SPEED): | ||||
|         sens = await sensor.new_sensor(fan_speed_config, mv) | ||||
|         cg.add(mv.register_micronova_listener(sens)) | ||||
|         cg.add(sens.set_memory_location(fan_speed_config[CONF_MEMORY_LOCATION])) | ||||
|         cg.add(sens.set_memory_address(fan_speed_config[CONF_MEMORY_ADDRESS])) | ||||
|         cg.add(sens.set_function(MicroNovaFunctions.STOVE_FUNCTION_FAN_SPEED)) | ||||
|         cg.add(sens.set_fan_speed_offset(fan_speed_config[CONF_FAN_RPM_OFFSET])) | ||||
|  | ||||
|     if memory_address_sensor_config := config.get(CONF_MEMORY_ADDRESS_SENSOR): | ||||
|         sens = await sensor.new_sensor(memory_address_sensor_config, mv) | ||||
|         cg.add(mv.register_micronova_listener(sens)) | ||||
|         cg.add( | ||||
|             sens.set_memory_location(memory_address_sensor_config[CONF_MEMORY_LOCATION]) | ||||
|         ) | ||||
|         cg.add( | ||||
|             sens.set_memory_address(memory_address_sensor_config[CONF_MEMORY_ADDRESS]) | ||||
|         ) | ||||
|         cg.add( | ||||
|             sens.set_function(MicroNovaFunctions.STOVE_FUNCTION_MEMORY_ADDRESS_SENSOR) | ||||
|         ) | ||||
|  | ||||
|     if water_temperature_config := config.get(CONF_WATER_TEMPERATURE): | ||||
|         sens = await sensor.new_sensor(water_temperature_config, mv) | ||||
|         cg.add(mv.register_micronova_listener(sens)) | ||||
|         cg.add(sens.set_memory_location(water_temperature_config[CONF_MEMORY_LOCATION])) | ||||
|         cg.add(sens.set_memory_address(water_temperature_config[CONF_MEMORY_ADDRESS])) | ||||
|         cg.add(sens.set_function(MicroNovaFunctions.STOVE_FUNCTION_WATER_TEMPERATURE)) | ||||
|  | ||||
|     if water_pressure_config := config.get(CONF_WATER_PRESSURE): | ||||
|         sens = await sensor.new_sensor(water_pressure_config, mv) | ||||
|         cg.add(mv.register_micronova_listener(sens)) | ||||
|         cg.add(sens.set_memory_location(water_pressure_config[CONF_MEMORY_LOCATION])) | ||||
|         cg.add(sens.set_memory_address(water_pressure_config[CONF_MEMORY_ADDRESS])) | ||||
|         cg.add(sens.set_function(MicroNovaFunctions.STOVE_FUNCTION_WATER_PRESSURE)) | ||||
							
								
								
									
										35
									
								
								esphome/components/micronova/sensor/micronova_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								esphome/components/micronova/sensor/micronova_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| #include "micronova_sensor.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace micronova { | ||||
|  | ||||
| void MicroNovaSensor::process_value_from_stove(int value_from_stove) { | ||||
|   if (value_from_stove == -1) { | ||||
|     this->publish_state(NAN); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   float new_sensor_value = (float) value_from_stove; | ||||
|   switch (this->get_function()) { | ||||
|     case MicroNovaFunctions::STOVE_FUNCTION_ROOM_TEMPERATURE: | ||||
|       new_sensor_value = new_sensor_value / 2; | ||||
|       break; | ||||
|     case MicroNovaFunctions::STOVE_FUNCTION_THERMOSTAT_TEMPERATURE: | ||||
|       break; | ||||
|     case MicroNovaFunctions::STOVE_FUNCTION_FAN_SPEED: | ||||
|       new_sensor_value = new_sensor_value == 0 ? 0 : (new_sensor_value * 10) + this->fan_speed_offset_; | ||||
|       break; | ||||
|     case MicroNovaFunctions::STOVE_FUNCTION_WATER_TEMPERATURE: | ||||
|       new_sensor_value = new_sensor_value / 2; | ||||
|       break; | ||||
|     case MicroNovaFunctions::STOVE_FUNCTION_WATER_PRESSURE: | ||||
|       new_sensor_value = new_sensor_value / 10; | ||||
|       break; | ||||
|     default: | ||||
|       break; | ||||
|   } | ||||
|   this->publish_state(new_sensor_value); | ||||
| } | ||||
|  | ||||
| }  // namespace micronova | ||||
| }  // namespace esphome | ||||
							
								
								
									
										27
									
								
								esphome/components/micronova/sensor/micronova_sensor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								esphome/components/micronova/sensor/micronova_sensor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/micronova/micronova.h" | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace micronova { | ||||
|  | ||||
| class MicroNovaSensor : public sensor::Sensor, public MicroNovaSensorListener { | ||||
|  public: | ||||
|   MicroNovaSensor(MicroNova *m) : MicroNovaSensorListener(m) {} | ||||
|   void dump_config() override { LOG_SENSOR("", "Micronova sensor", this); } | ||||
|  | ||||
|   void request_value_from_stove() override { | ||||
|     this->micronova_->request_address(this->memory_location_, this->memory_address_, this); | ||||
|   } | ||||
|   void process_value_from_stove(int value_from_stove) override; | ||||
|  | ||||
|   void set_fan_speed_offset(uint8_t f) { this->fan_speed_offset_ = f; } | ||||
|   uint8_t get_set_fan_speed_offset() { return this->fan_speed_offset_; } | ||||
|  | ||||
|  protected: | ||||
|   int fan_speed_offset_ = 0; | ||||
| }; | ||||
|  | ||||
| }  // namespace micronova | ||||
| }  // namespace esphome | ||||
							
								
								
									
										56
									
								
								esphome/components/micronova/switch/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								esphome/components/micronova/switch/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import switch | ||||
| from esphome.const import ( | ||||
|     ICON_POWER, | ||||
| ) | ||||
|  | ||||
| from .. import ( | ||||
|     MicroNova, | ||||
|     MicroNovaFunctions, | ||||
|     CONF_MICRONOVA_ID, | ||||
|     CONF_MEMORY_LOCATION, | ||||
|     CONF_MEMORY_ADDRESS, | ||||
|     MICRONOVA_LISTENER_SCHEMA, | ||||
|     micronova_ns, | ||||
| ) | ||||
|  | ||||
| CONF_STOVE = "stove" | ||||
| CONF_MEMORY_DATA_ON = "memory_data_on" | ||||
| CONF_MEMORY_DATA_OFF = "memory_data_off" | ||||
|  | ||||
| MicroNovaSwitch = micronova_ns.class_("MicroNovaSwitch", switch.Switch, cg.Component) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(CONF_MICRONOVA_ID): cv.use_id(MicroNova), | ||||
|         cv.Optional(CONF_STOVE): switch.switch_schema( | ||||
|             MicroNovaSwitch, | ||||
|             icon=ICON_POWER, | ||||
|         ) | ||||
|         .extend( | ||||
|             MICRONOVA_LISTENER_SCHEMA( | ||||
|                 default_memory_location=0x80, default_memory_address=0x21 | ||||
|             ) | ||||
|         ) | ||||
|         .extend( | ||||
|             { | ||||
|                 cv.Optional(CONF_MEMORY_DATA_OFF, default=0x06): cv.hex_int_range(), | ||||
|                 cv.Optional(CONF_MEMORY_DATA_ON, default=0x01): cv.hex_int_range(), | ||||
|             } | ||||
|         ), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     mv = await cg.get_variable(config[CONF_MICRONOVA_ID]) | ||||
|  | ||||
|     if stove_config := config.get(CONF_STOVE): | ||||
|         sw = await switch.new_switch(stove_config, mv) | ||||
|         cg.add(mv.set_stove(sw)) | ||||
|         cg.add(sw.set_memory_location(stove_config[CONF_MEMORY_LOCATION])) | ||||
|         cg.add(sw.set_memory_address(stove_config[CONF_MEMORY_ADDRESS])) | ||||
|         cg.add(sw.set_memory_data_on(stove_config[CONF_MEMORY_DATA_ON])) | ||||
|         cg.add(sw.set_memory_data_off(stove_config[CONF_MEMORY_DATA_OFF])) | ||||
|         cg.add(sw.set_function(MicroNovaFunctions.STOVE_FUNCTION_SWITCH)) | ||||
							
								
								
									
										33
									
								
								esphome/components/micronova/switch/micronova_switch.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								esphome/components/micronova/switch/micronova_switch.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| #include "micronova_switch.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace micronova { | ||||
|  | ||||
| void MicroNovaSwitch::write_state(bool state) { | ||||
|   switch (this->get_function()) { | ||||
|     case MicroNovaFunctions::STOVE_FUNCTION_SWITCH: | ||||
|       if (state) { | ||||
|         // Only send power-on when current state is Off | ||||
|         if (this->micronova_->get_current_stove_state() == 0) { | ||||
|           this->micronova_->write_address(this->memory_location_, this->memory_address_, this->memory_data_on_); | ||||
|           this->publish_state(true); | ||||
|         } else | ||||
|           ESP_LOGW(TAG, "Unable to turn stove on, invalid state: %d", micronova_->get_current_stove_state()); | ||||
|       } else { | ||||
|         // don't send power-off when status is Off or Final cleaning | ||||
|         if (this->micronova_->get_current_stove_state() != 0 && micronova_->get_current_stove_state() != 6) { | ||||
|           this->micronova_->write_address(this->memory_location_, this->memory_address_, this->memory_data_off_); | ||||
|           this->publish_state(false); | ||||
|         } else | ||||
|           ESP_LOGW(TAG, "Unable to turn stove off, invalid state: %d", micronova_->get_current_stove_state()); | ||||
|       } | ||||
|       this->micronova_->update(); | ||||
|       break; | ||||
|  | ||||
|     default: | ||||
|       break; | ||||
|   } | ||||
| } | ||||
|  | ||||
| }  // namespace micronova | ||||
| }  // namespace esphome | ||||
							
								
								
									
										29
									
								
								esphome/components/micronova/switch/micronova_switch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								esphome/components/micronova/switch/micronova_switch.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/micronova/micronova.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/switch/switch.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace micronova { | ||||
|  | ||||
| class MicroNovaSwitch : public Component, public switch_::Switch, public MicroNovaSwitchListener { | ||||
|  public: | ||||
|   MicroNovaSwitch(MicroNova *m) : MicroNovaSwitchListener(m) {} | ||||
|   void dump_config() override { LOG_SWITCH("", "Micronova switch", this); } | ||||
|  | ||||
|   void set_stove_state(bool v) override { this->publish_state(v); } | ||||
|   bool get_stove_state() override { return this->state; } | ||||
|  | ||||
|   void set_memory_data_on(uint8_t f) { this->memory_data_on_ = f; } | ||||
|   uint8_t get_memory_data_on() { return this->memory_data_on_; } | ||||
|  | ||||
|   void set_memory_data_off(uint8_t f) { this->memory_data_off_ = f; } | ||||
|   uint8_t get_memory_data_off() { return this->memory_data_off_; } | ||||
|  | ||||
|  protected: | ||||
|   void write_state(bool state) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace micronova | ||||
| }  // namespace esphome | ||||
							
								
								
									
										43
									
								
								esphome/components/micronova/text_sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								esphome/components/micronova/text_sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import text_sensor | ||||
|  | ||||
| from .. import ( | ||||
|     MicroNova, | ||||
|     MicroNovaFunctions, | ||||
|     CONF_MICRONOVA_ID, | ||||
|     CONF_MEMORY_LOCATION, | ||||
|     CONF_MEMORY_ADDRESS, | ||||
|     MICRONOVA_LISTENER_SCHEMA, | ||||
|     micronova_ns, | ||||
| ) | ||||
|  | ||||
| CONF_STOVE_STATE = "stove_state" | ||||
|  | ||||
| MicroNovaTextSensor = micronova_ns.class_( | ||||
|     "MicroNovaTextSensor", text_sensor.TextSensor, cg.Component | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(CONF_MICRONOVA_ID): cv.use_id(MicroNova), | ||||
|         cv.Optional(CONF_STOVE_STATE): text_sensor.text_sensor_schema( | ||||
|             MicroNovaTextSensor | ||||
|         ).extend( | ||||
|             MICRONOVA_LISTENER_SCHEMA( | ||||
|                 default_memory_location=0x00, default_memory_address=0x21 | ||||
|             ) | ||||
|         ), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     mv = await cg.get_variable(config[CONF_MICRONOVA_ID]) | ||||
|  | ||||
|     if stove_state_config := config.get(CONF_STOVE_STATE): | ||||
|         sens = await text_sensor.new_text_sensor(stove_state_config, mv) | ||||
|         cg.add(mv.register_micronova_listener(sens)) | ||||
|         cg.add(sens.set_memory_location(stove_state_config[CONF_MEMORY_LOCATION])) | ||||
|         cg.add(sens.set_memory_address(stove_state_config[CONF_MEMORY_ADDRESS])) | ||||
|         cg.add(sens.set_function(MicroNovaFunctions.STOVE_FUNCTION_STOVE_STATE)) | ||||
| @@ -0,0 +1,31 @@ | ||||
| #include "micronova_text_sensor.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace micronova { | ||||
|  | ||||
| void MicroNovaTextSensor::process_value_from_stove(int value_from_stove) { | ||||
|   if (value_from_stove == -1) { | ||||
|     this->publish_state("unknown"); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   switch (this->get_function()) { | ||||
|     case MicroNovaFunctions::STOVE_FUNCTION_STOVE_STATE: | ||||
|       this->micronova_->set_current_stove_state(value_from_stove); | ||||
|       this->publish_state(STOVE_STATES[value_from_stove]); | ||||
|       // set the stove switch to on for any value but 0 | ||||
|       if (value_from_stove != 0 && this->micronova_->get_stove_switch() != nullptr && | ||||
|           !this->micronova_->get_stove_switch()->get_stove_state()) { | ||||
|         this->micronova_->get_stove_switch()->set_stove_state(true); | ||||
|       } else if (value_from_stove == 0 && this->micronova_->get_stove_switch() != nullptr && | ||||
|                  this->micronova_->get_stove_switch()->get_stove_state()) { | ||||
|         this->micronova_->get_stove_switch()->set_stove_state(false); | ||||
|       } | ||||
|       break; | ||||
|     default: | ||||
|       break; | ||||
|   } | ||||
| } | ||||
|  | ||||
| }  // namespace micronova | ||||
| }  // namespace esphome | ||||
| @@ -0,0 +1,20 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/micronova/micronova.h" | ||||
| #include "esphome/components/text_sensor/text_sensor.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace micronova { | ||||
|  | ||||
| class MicroNovaTextSensor : public text_sensor::TextSensor, public MicroNovaSensorListener { | ||||
|  public: | ||||
|   MicroNovaTextSensor(MicroNova *m) : MicroNovaSensorListener(m) {} | ||||
|   void dump_config() override { LOG_TEXT_SENSOR("", "Micronova text sensor", this); } | ||||
|   void request_value_from_stove() override { | ||||
|     this->micronova_->request_address(this->memory_location_, this->memory_address_, this); | ||||
|   } | ||||
|   void process_value_from_stove(int value_from_stove) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace micronova | ||||
| }  // namespace esphome | ||||
| @@ -342,6 +342,10 @@ mcp23s17: | ||||
|     cs_pin: GPIO12 | ||||
|     deviceaddress: 1 | ||||
|  | ||||
| micronova: | ||||
|   enable_rx_pin: 4 | ||||
|   uart_id: uart_0 | ||||
|  | ||||
| dfrobot_sen0395: | ||||
|   - id: mmwave | ||||
|     uart_id: dfrobot_mmwave_uart | ||||
| @@ -1518,6 +1522,24 @@ sensor: | ||||
|     field_strength_z: | ||||
|       name: "Magnet Z" | ||||
|       id: magnet_z | ||||
|   - platform: micronova | ||||
|     room_temperature: | ||||
|       name: Room Temperature | ||||
|     fumes_temperature: | ||||
|       name: Fumes Temperature | ||||
|     water_temperature: | ||||
|       name: Water temperature | ||||
|     water_pressure: | ||||
|       name: Water pressure | ||||
|     stove_power: | ||||
|       name: Stove Power | ||||
|     fan_speed: | ||||
|       fan_rpm_offset: 240 | ||||
|       name: Fan RPM | ||||
|     memory_address_sensor: | ||||
|       memory_location: 0x20 | ||||
|       memory_address: 0x7d | ||||
|       name: Adres sensor | ||||
|  | ||||
| esp32_touch: | ||||
|   setup_mode: false | ||||
| @@ -2705,6 +2727,9 @@ switch: | ||||
|       name: "control ld2410 engineering mode" | ||||
|     bluetooth: | ||||
|       name: "control ld2410 bluetooth" | ||||
|   - platform: micronova | ||||
|     stove: | ||||
|       name: Stove on/off | ||||
|  | ||||
| fan: | ||||
|   - platform: binary | ||||
| @@ -3489,6 +3514,12 @@ number: | ||||
|         name: g8 move threshold | ||||
|       still_threshold: | ||||
|         name: g8 still threshold | ||||
|   - platform: micronova | ||||
|     thermostat_temperature: | ||||
|       name: Micronova Thermostaat | ||||
|       step: 1 | ||||
|     power_level: | ||||
|       name: Micronova Power level | ||||
|  | ||||
| select: | ||||
|   - platform: template | ||||
| @@ -3619,6 +3650,12 @@ button: | ||||
|     uart_id: uart_0 | ||||
|     name: UART button | ||||
|     data: "Pressed\r\n" | ||||
|   - platform: micronova | ||||
|     custom_button: | ||||
|       name: Custom Micronova Button | ||||
|       memory_location: 0xA0 | ||||
|       memory_address: 0x7D | ||||
|       memory_data: 0x0F | ||||
|  | ||||
| ld2410: | ||||
|   id: my_ld2410 | ||||
| @@ -3727,3 +3764,4 @@ alarm_control_panel: | ||||
|       then: | ||||
|         - lambda: !lambda |- | ||||
|             ESP_LOGD("TEST", "State change %s", alarm_control_panel_state_to_string(id(alarmcontrolpanel1)->get_state())); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user