Compare commits

..

6 Commits

Author SHA1 Message Date
1f7c437c3e This adds:
- A general folder for documantation configs
- A documantation for general esphome devices with functionallity too small for it's own file.
- Removed the powersave mode NONE on garage doors.
2025-10-08 10:13:10 +02:00
d4edf31a8b I am literally at a point, where I don't know what else to improve here. 2025-10-08 09:09:07 +02:00
d0c2b3993b Made garage1.yaml restart polling after a failed ota update 2025-10-07 14:06:42 +02:00
273cd9e434 Added hoermann_door.stop_polling and .start_polling as automations should that need arise somehow! 2025-10-07 13:15:04 +02:00
3b88f1e988 Made the HCIEmulator Log Level settable inside the .yaml file 2025-10-07 02:01:56 +02:00
9d3930af59 Updated Hörmann_door to completely use namespaces. Makes code a bit cleaner 2025-10-07 00:50:49 +02:00
9 changed files with 250 additions and 59 deletions

50
5gModemPower.yaml Normal file
View File

@@ -0,0 +1,50 @@
esphome:
name: modempower
esp8266:
board: sonoff_basic
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: true
ap:
ssid: modempower_rescue
password: !secret rescue_ap_password
captive_portal:
api:
encryption:
key: !secret k5gModem
ota:
- platform: esphome
password: !secret p5gModem
logger:
binary_sensor:
- platform: gpio
pin:
number: GPIO0
mode:
input: true
pullup: true
inverted: true
name: "5G Modem Power Button"
on_press:
- switch.toggle: relay
switch:
- platform: gpio
name: "5G Modem Power Relay"
pin: GPIO12
restore_mode: ALWAYS_ON
id: relay
status_led:
pin:
number: GPIO13
inverted: yes

49
docs/GENERAL_DEVICE.md Normal file
View File

@@ -0,0 +1,49 @@
# What is this:
This is the general documentation for esphome configs deemed too short to deserve their own documantation. Stuff like the 5gModem, the sonoffStecker or the nbs-light-room are just too short for me to believe it necessary.
So, let's get through the basics of any esphome config developed by me.
# General configs
Before I start, I should mention what I consider to be general configurations: modules like esphome, esp8266 or esp32, as well as wifi, logger, ota are all part of that. Nearly all of them require all of them (with exception to esp8266 and esp32 as they are mutually exclusive).
## esphome
The `esphome` defines general points, and is required by any esphome config to function. Weirdly, in the last few years this has become a mere holder for `name` and maybe `version`.
For `name` I recommand something that actually give the device a name that is clear what it does.
For `version` when used, I recommand using semantic versioning that makes sense. But if you actually use it or not is up to you.
## esp8266/esp32
The `esp8266` or `esp32` has a special point about only being their to define the actually board being used.
Most common boards for esp8266 that we use is the `nodemcuv2`. While for the esp32 it is the `wemos_d1_mini32` also sometimes referenced as `esp32dev`.
A special note about `esp32` is that they have multiple frameworks that are supported. This is defined as `esp32.framework.type`:
- arduino
- esp-idf
## wifi
The wifi part is not confusing at all. Normally we use `ssid`, `password` and `fast_connect`. You will see that there is also a captive_portal defined at times should the esp somehow fail to connect to the wifi at all. This captive portal and it's own wifi that it will open in such an event can be used to rescue the device without having to manually flash it again.
SSID is the wifis name, while password is, well, its password. `fast_connect` just means that the esp will NOT attempt to verify that the network exists by scanning for it. This is important if you need to connect to a hidden wifi.
## logger
We actually keep this empty. Just having it around is enough.
## api
The api just defines the encryption key for the esphome native api used by esphome and home assistant.
## ota
Ahh, the usual over the air update component.
The platform used is the normal esphome platform used by the esphome cli as well as the esphome builder in HomeAssistant from there we define a password. Just to be sure.
## status_led
The status led is specific to each esp device. but I use it usually to detect if a device is connected to the wifi or not.
# More device specific configuration
This is the section for more device specific configuration options that might be important
## 5gModemPower
This is a normal sonoff power relay. It has a pin defined for the relay. A pin defined for the actual button should you need to press it directly on the device and that's about it.
The relay that controls the power is controlled by an gpio pin (Number 12) and starts out as always on.
There is a binary sensor included for the button on the actual device. This is an inverted pullup pin on gpio 0, pressing it toggles the relay.

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome import pins, automation
from esphome.components import uart
from esphome.const import (
CONF_ID
@@ -11,9 +11,26 @@ DEPENDENCIES=["uart"]
hoermann_door_ns = cg.esphome_ns.namespace('hoermann_door')
HoermannDoor = hoermann_door_ns.class_('HoermannMainComponent', cg.Component)
StopPolling = hoermann_door_ns.class_('StopPollingAction', automation.Action)
StartPolling = hoermann_door_ns.class_('StartPollingAction', automation.Action)
CONF_UART_ENTRY = "uart_connection"
CONF_TX_PIN = "tx_pin"
CONF_LOG_LEVEL = "log_level"
#define LL_OFF 0
#define LL_ERROR 1
#define LL_WARN 2
#define LL_INFO 3
#define LL_DEBUG 4
CONF_LOG_LEVELS = {
"NONE": 0,
"ERROR": 1,
"WARN": 2,
"INFO": 3,
"DEBUG": 4,
}
def validate_config(config):
return config
@@ -23,20 +40,34 @@ CONFIG_SCHEMA = cv.All(
cv.GenerateID(): cv.declare_id(HoermannDoor),
cv.Required(CONF_UART_ENTRY): cv.use_id(uart.UARTComponent),
cv.Required(CONF_TX_PIN): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_LOG_LEVEL, default="INFO"): cv.enum(CONF_LOG_LEVELS, upper=True),
}),
validate_config
)
ACTION_SCHEMA = automation.maybe_simple_id({
cv.GenerateID(): cv.use_id(HoermannDoor)
})
@automation.register_action("hoermann_door.stop_polling", StopPolling, ACTION_SCHEMA)
async def stop_polling(config, action_id, template_arg, args):
parent = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, parent)
@automation.register_action("hoermann_door.start_polling", StartPolling, ACTION_SCHEMA)
async def start_polling(config, action_id, template_arg, args):
parent = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, parent)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
#yield mqtt.register_mqtt_component(var, config)
#btnReset = yield cg.gpio_pin_expression(config[CONF_SENSOR_PIN])
#cg.add(var.set_input_pin(btnReset))
code = await cg.get_variable(config[CONF_UART_ENTRY])
cg.add(var.set_seriel_connection(code))
code2 = await cg.gpio_pin_expression(config[CONF_TX_PIN])
cg.add(var.set_tx_on_pin(code2)) # Needs be configured before sensor
cg.add(var.set_tx_on_pin(code2))
log_level = config[CONF_LOG_LEVEL]
cg.add(var.set_log_level(log_level))

View File

@@ -1,8 +1,6 @@
#pragma once
#ifdef USE_COVER
class HoermannDoorx;
//#ifdef USE_COVER
#include "esphome.h"
#include "hoermann.h"
@@ -55,7 +53,7 @@ public:
manual = false;
}
else{
ESP_LOGD("Door controller", "Not yet supported");
ESP_LOGD(COMP_TAG, "Not yet supported");
}
}
@@ -106,4 +104,4 @@ public:
} // namespace hoermann
} // namespace esphome
#endif
//#endif

View File

@@ -1,5 +1,9 @@
#include "hciemulator.h"
namespace esphome {
namespace hoermann_door {
#define CHECKCHANGEDSET(Target, Value, Flag) \
if ((Target) != (Value)) \
{ \
@@ -8,9 +12,9 @@
}
int hciloglevel = LL_DEBUG;
#define SOFTSERIAL 1
//#define SOFTSERIAL 1
#ifdef SOFTSERIAL
//#ifdef SOFTSERIAL
#define Log(Level, Message) LogCore(Level, Message)
#define Log3(Level, Message, Buffer, Len) LogCore(Level, Message, Buffer, Len)
// LOGLEVEL
@@ -23,7 +27,7 @@ void LogCore(int Level, const char *msg, const unsigned char *data = NULL, size_
if (data != NULL && datalen > 0)
{
//std::string newmsg(msg);
char* newmsg = (char*)malloc(strlen(msg) + datalen * 3 + 1);
char* newmsg = (char*)malloc(strlen(msg) + (datalen * 3) + 1);
strncpy(newmsg, msg, strlen(msg) + 1);
newmsg[strlen(msg)] = '\0';
char str[4];
@@ -41,10 +45,10 @@ void LogCore(int Level, const char *msg, const unsigned char *data = NULL, size_
ESP_LOGD(TAG, msg);
}
}
#else
#define Log(Level, Message)
#define Log3(Level, Message, Buffer, Len)
#endif
//#else
//#define Log(Level, Message)
//#define Log3(Level, Message, Buffer, Len)
//#endif
int HCIEmulator::getLogLevel()
{
@@ -119,7 +123,6 @@ HCIEmulator::HCIEmulator(esphome::InternalGPIOPin* pin, esphome::uart::UARTCompo
m_port = uart;
m_pin = pin;
m_statusCallback = NULL;
setLogLevel(DEFAULTLOGLEVEL);
};
#define TX_ON 25
@@ -203,6 +206,7 @@ void HCIEmulator::poll()
void HCIEmulator::processFrame()
{
m_txlen = 0; // clear send buffer
Log3(LL_DEBUG, "Incomming Data: ", m_rxbuffer, m_rxlen);
if (m_rxlen < 5)
{
@@ -509,3 +513,6 @@ void HCIEmulator::onStatusChanged(callback_function_t handler)
{
m_statusCallback = handler;
}
}
}

View File

@@ -1,12 +1,6 @@
#ifndef __hciemulator_h
#define __hciemulator_h
#ifdef USE_ESP_IDF
//#include <cstdint>
//#include <functional>
//#include <algorithm>
#endif
#include "esphome/components/uart/uart.h"
#define LL_OFF 0
@@ -15,8 +9,6 @@
#define LL_INFO 3
#define LL_DEBUG 4
#define DEFAULTLOGLEVEL LL_INFO
#define DEVICEID 0x02
#define BROADCASTID 0x00
#define SIMULATEKEYPRESSDELAYMS 100
@@ -32,6 +24,9 @@
#define T1_5 750
#define T3_5 4800 //
namespace esphome {
namespace hoermann_door {
static const char *const TAG = "HCIEmulator";
enum DoorState : uint8_t
@@ -81,7 +76,7 @@ class HCIEmulator
public:
typedef std::function<void(const SHCIState &)> callback_function_t;
HCIEmulator(esphome::InternalGPIOPin* pin, esphome::uart::UARTComponent* uart);
HCIEmulator(InternalGPIOPin* pin, uart::UARTComponent* uart);
void poll();
@@ -94,7 +89,7 @@ public:
const SHCIState &getState()
{
if (esphome::micros() - m_recvTime > 2000000)
if (micros() - m_recvTime > 2000000)
{
// 2 sec without statusmessage
m_state.valid = false;
@@ -104,7 +99,7 @@ public:
unsigned long getMessageAge()
{
return esphome::micros() - m_recvTime;
return micros() - m_recvTime;
}
int getLogLevel();
@@ -124,8 +119,8 @@ protected:
private:
callback_function_t m_statusCallback;
esphome::InternalGPIOPin* m_pin;
esphome::uart::UARTComponent* m_port;
InternalGPIOPin* m_pin;
uart::UARTComponent* m_port;
SHCIState m_state;
StateMachine m_statemachine;
@@ -147,4 +142,7 @@ private:
bool m_skipFrame;
};
}
}
#endif

View File

@@ -2,14 +2,17 @@
#include <algorithm>
namespace esphome {
namespace hoermann_door {
class HoermannMainComponent;
}
}
#include "hciemulator.h"
#include "esphome/components/uart/uart.h"
namespace esphome {
namespace hoermann_door {
static const char *const TAG = "Hoermann";
static const char *const COMP_TAG = "Hoermann";
void dispatcherFn(void *arg);
@@ -17,6 +20,7 @@ class HoermannMainComponent: public Component{
protected:
HCIEmulator* emulator;
TaskHandle_t modBusTask;
int log_level = 3; // Defaults to INFO
uart::UARTComponent* _uart;
InternalGPIOPin* _tx_on;
@@ -33,6 +37,11 @@ class HoermannMainComponent: public Component{
void set_tx_on_pin(InternalGPIOPin* pin){
this->_tx_on = pin;
}
void set_log_level(int level){
this->log_level = level;
}
HCIEmulator* getEmulator(){
return emulator;
}
@@ -41,19 +50,38 @@ class HoermannMainComponent: public Component{
this->_tx_on->setup();
this->emulator = new HCIEmulator(this->_tx_on, this->_uart);
this->emulator->setLogLevel(this->log_level);
this->start_polling();
}
void stop_polling(){
this->set_continue_ = false;
if(modBusTask != NULL){
// Wait for the task to be deleted
while(eTaskGetState(modBusTask) != eDeleted){
vTaskDelay(1);
}
modBusTask = NULL;
}
this->_tx_on->digital_write(false);
this->set_continue_ = true;
xTaskCreatePinnedToCore(
dispatcherFn, // Function to implement the task
"ModBusTask", // Name of the task
10000, // Stack size in words
this, // Task input parameter
// 1, // Priority of the task
configMAX_PRIORITIES - 1,
&modBusTask, // Task handle.
1 // Core where the task should run
);
}
void start_polling() {
if(modBusTask == NULL){
this->_tx_on->digital_write(false);
this->set_continue_ = true;
xTaskCreatePinnedToCore(
dispatcherFn, // Function to implement the task
"ModBusTask", // Name of the task
10000, // Stack size in words
this, // Task input parameter
// 1, // Priority of the task
configMAX_PRIORITIES - 1,
&modBusTask, // Task handle.
1 // Core where the task should run
);
}
}
void modBusPolling(void *parameter)
@@ -72,9 +100,11 @@ class HoermannMainComponent: public Component{
}
void dump_config() override {
ESP_LOGCONFIG(TAG, "hoermann_door_component:");
ESP_LOGCONFIG(TAG, " UART: %d", this->_uart->get_baud_rate());
//LOG_PIN(TAG, this->_tx_on);
ESP_LOGCONFIG(COMP_TAG, "hoermann_door_component:");
ESP_LOGCONFIG(COMP_TAG, " UART: ");
ESP_LOGCONFIG(COMP_TAG, " UART is configured");
LOG_PIN(" TX_ON_PIN", this->_tx_on);
ESP_LOGCONFIG(COMP_TAG, " Log Level: %d", this->log_level);
}
};
@@ -84,6 +114,25 @@ void dispatcherFn(void *arg)
x->modBusPolling(arg);
}
template<typename... Ts> class StopPollingAction: public Action<Ts...> {
public:
StopPollingAction(HoermannMainComponent *motor) : motor_(motor) {}
void play(Ts... x) override { this->motor_->stop_polling(); }
protected:
HoermannMainComponent *motor_;
};
template<typename... Ts> class StartPollingAction: public Action<Ts...> {
public:
StartPollingAction(HoermannMainComponent *motor) : motor_(motor) {}
void play(Ts... x) override { this->motor_->start_polling(); }
protected:
HoermannMainComponent *motor_;
};
}
}

View File

@@ -13,8 +13,6 @@ class NbsLightManager;
#define TX_ON 25
//#define configMAX_PRIORITIES 25
#define TAG "hoermann_door"
namespace esphome {
namespace hoermann_door {
@@ -61,7 +59,7 @@ class NbsLightOutput: public output::BinaryOutput, public Component{
}
void set_state_callback(light::LightState *callback){
if(callback == nullptr) ESP_LOGW("Hoermann_door(Light)", "Got Nullable callback");
if(callback == nullptr) ESP_LOGW(COMP_TAG, "Got Nullable callback");
this->callback = callback;
}
void set_emulator_component(HoermannMainComponent* component){
@@ -74,14 +72,14 @@ class NbsLightOutput: public output::BinaryOutput, public Component{
//ESP_LOGD("Test", "I have no idea");
lastState = mainComponent->getEmulator()->getState().lampOn;
if(lastState == true){
ESP_LOGD(TAG, "Hoermann_door(Light)", "Light State ON");
ESP_LOGD(COMP_TAG, "Light State ON");
auto call = callback->make_call();
call.set_state(true);
call.perform();
}
else {
ESP_LOGD(TAG, "Hoermann_door(Light)", "Light State OFF");
ESP_LOGD(COMP_TAG, "Light State OFF");
auto call = callback->make_call();
call.set_state(false);
call.perform();

View File

@@ -1,5 +1,6 @@
substitutions:
garageSide: sy # sy, wo
version: "2.1.3"
esphome:
name: garage${garageSide}
@@ -12,6 +13,14 @@ esp32:
ota:
- platform: esphome
password: !secret gd_passwd
on_begin:
then:
- hoermann_door.stop_polling: door_controll_internal
- logger.log: "OTA-Update started, stopped polling"
on_error:
then:
- hoermann_door.start_polling: door_controll_internal
- logger.log: "OTA-Update failed, restarted polling"
api:
encryption:
@@ -20,7 +29,6 @@ api:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
fast_connect: true
logger:
#level: VERY_VERBOSE
@@ -41,15 +49,18 @@ uart:
tx_pin: 17
baud_rate: 57600
id: hm_connection
data_bits: 8 # Standard
parity: EVEN # NON Standard, None would be standard
stop_bits: 1 # Standard
data_bits: 8 # Standard, just defined for clarity
parity: EVEN # The chip uses even parity
stop_bits: 1 # Standard, just defined for clarity
hoermann_door:
id: door_controll_internal
uart_connection: hm_connection
tx_pin: 25
log_level: INFO
tx_pin:
number: 25
inverted: false # Just for clarity, false is standard
light:
- platform: binary