Added garage1.yaml, including the implementation for the hoermann external components

This commit is contained in:
Nicolas Bachschwell 2024-04-26 21:02:48 +02:00
parent ac345026e7
commit 9719833b47
Signed by: NBSgamesAT
GPG Key ID: 2D73288FF7AEED2F
8 changed files with 924 additions and 1 deletions

View File

@ -77,4 +77,13 @@ There are a few light effects configured so please play around with them for a l
There is one thing I should mention tho: There is one thing I should mention tho:
At line 34 in the ledstrip_simple.yaml there is a number called num_leds which dictates how many leds there are on the ledstrip. Please change that according to your ledstrip. At line 34 in the ledstrip_simple.yaml there is a number called num_leds which dictates how many leds there are on the ledstrip. Please change that according to your ledstrip.
More infos are coming soon. # garage1.yaml (Hörmann Garage Doors)
This is the config for the Hörmann Garage Doors and it is absolutely snuffing stupid.
First of all: It uses a ~~custom component~~... They are called external components now.
Which starts the trouble. You currently cannot use it without using BOTH the door and the output since they are on the same file and I put no safeguards in yet. Maybe later.
Second of all, if you want to change the door, just change the `garageSide` under `substitutions`. It's either `sy` or `wo`.
Then let the custom components do their job I suppose. They are under [external_components/hoermann_door](external_components/hoermann_door). The [__init__.py](external_components/hoermann_door/__init__.py) needs to be there while the [cover.py](external_components/hoermann_door/cover.py) and [output.py](external_components/hoermann_door/output.py) make the platforms in `cover` and `output` available.
Otherwise. they works but there is still room for improvements

View File

@ -0,0 +1,18 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.components import cover
from esphome.const import CONF_ID
hoermann_cover_ns = cg.esphome_ns.namespace('hoermann_door')
HoermannDoor = hoermann_cover_ns.class_('HoermanDoor', cover.Cover, cg.Component)
CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(HoermannDoor)
}).extend(cv.COMPONENT_SCHEMA)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield cover.register_cover(var, config)

View File

@ -0,0 +1,192 @@
#pragma once
#include "esphome.h"
#include "Arduino.h"
#include "hciemulator.h"
//#include "cover.h"
#define RS485 Serial2
#define TX_ON 25
//#define configMAX_PRIORITIES 25
#define TAG "hoermann_door"
namespace esphome {
namespace hoermann_door {
HCIEmulator emulator;
TaskHandle_t modBusTask;
void modBusPolling(void *parameter);
class HoermanDoor : public Component, public cover::Cover
{
public:
HoermanDoor()
{
emulator;
}
cover::CoverTraits get_traits() override {
auto traits = cover::CoverTraits();
traits.set_is_assumed_state(false);
traits.set_supports_position(true);
traits.set_supports_tilt(false);
traits.set_supports_stop(true);
traits.set_supports_toggle(true);
return traits;
}
//u_int8_t stopAt == 0;
bool manual = false;
void control(const cover::CoverCall &call) override {
ESP_LOGW(TAG, "In func control door");
// This will be called every time the user requests a state change.
if (call.get_position().has_value()) {
float pos = *call.get_position();
//emulator(&RS485);
// Write pos (range 0-1) to cover
// ...
if(pos == 1.0){
emulator.openDoor();
manual = false;
}
else if(pos == 0.0){
emulator.closeDoor();
manual = false;
}
else{
ESP_LOGD("Door controller", "Not yet supported");
}
}
if (call.get_stop()) {
emulator.stopDoor();
}
//if(call.get_close()) {
//emulator.closeDoor();
//}
}
/*
void modBusPolling(void *parameter)
{
while (true)
{
if (lastCall > 0)
{
maxPeriod = _max(micros() - lastCall, maxPeriod);
}
lastCall = micros();
emulator.poll();
vTaskDelay(1);
}
vTaskDelete(NULL);
}
*/
void setup() override
{
// setup modbus
RS485.begin(57600, SERIAL_8E1, 16, 17);
pinMode(TX_ON, OUTPUT);
digitalWrite(TX_ON, LOW);
xTaskCreatePinnedToCore(
modBusPolling, // Function to implement the task
"ModBusTask", // Name of the task
10000, // Stack size in words
NULL, // Task input parameter
// 1, // Priority of the task
configMAX_PRIORITIES - 1,
&modBusTask, // Task handle.
1 // Core where the task should run
);
}
void open(){
//emulator.openDoor();
}
void close(){
//emulator.closeDoor();
}
void stop(){
//emulator.stopDoor();
}
int count = 0;
unsigned long lastStatus = 0;
u_int8_t pos = 255;
void loop() override
{
u_int8_t position = emulator.getState().doorCurrentPosition;
if(pos != position){
this->position = (float) position / 200.0;
this->publish_state();
pos = position;
}
}
};
// Custom binary output, for exposing binary states
class NbsLightOutput: public output::BinaryOutput, public Component {
public:
void setup() override {
}
void write_state(bool state) override {
}
bool lastState = false;
bool firstState = false;
void loop() override {
if(!emulator.getState().valid) false;
if(!firstState || emulator.getState().lampOn != lastState){
//ESP_LOGD("Test", "I have no idea");
lastState = emulator.getState().lampOn;
this->set_state(lastState);
firstState = true;
}
}
void turn_on() override {
if(!lastState) emulator.toggleLamp();
}
void turn_off() override {
if(lastState) emulator.toggleLamp();
}
};
volatile unsigned long lastCall = 0;
volatile unsigned long maxPeriod = 0;
void modBusPolling(void *parameter)
{
while (true)
{
if (lastCall > 0)
{
maxPeriod = _max(micros() - lastCall, maxPeriod);
}
lastCall = micros();
emulator.poll();
vTaskDelay(1);
}
vTaskDelete(NULL);
}
} // namespace hoermann
} // namespace esphome

View File

@ -0,0 +1,492 @@
#include "hciemulator.h"
#include "Arduino.h"
#define CHECKCHANGEDSET(Target, Value, Flag) \
if ((Target) != (Value)) \
{ \
Target = Value; \
Flag = true; \
}
int hciloglevel = LL_DEBUG;
#define SOFTSERIAL 1
#ifdef SOFTSERIAL
#define Log(Level, Message) LogCore(Level, Message)
#define Log3(Level, Message, Buffer, Len) LogCore(Level, Message, Buffer, Len)
// LOGLEVEL
void LogCore(int Level, const char *msg, const unsigned char *data = NULL, size_t datalen = 0)
{
if (Level > hciloglevel)
{
return;
}
if (data != NULL && datalen > 0)
{
String newmsg(msg);
char str[4];
for (size_t i = 0; i < datalen; i++)
{
snprintf(str, sizeof(str), "%02x ", data[i]);
newmsg += str;
}
Serial.println(newmsg);
}
else
{
Serial.println(msg);
}
}
#else
#define Log(Level, Message)
#define Log3(Level, Message, Buffer, Len)
#endif
int HCIEmulator::getLogLevel()
{
return hciloglevel;
}
void HCIEmulator::setLogLevel(int level)
{
hciloglevel = level;
}
// modbus crc calculation borrowed from:
// https://github.com/yaacov/ArduinoModbusSlave
#define MODBUS_CRC_LENGTH 2
#define readCRC(arr, length) word(arr[(length - MODBUS_CRC_LENGTH) + 1], arr[length - MODBUS_CRC_LENGTH])
#define readUInt16(arr, index) word(arr[index], arr[index + 1])
/**
* Calculate the CRC of the passed byte array from zero up to the passed length.
*
* @param buffer The byte array containing the data.
* @param length The length of the byte array.
*
* @return The calculated CRC as an unsigned 16 bit integer.
*
* Calculate and add the CRC.
* uint16_t crc = Modbus::calculateCRC(_responseBuffer, _responseBufferLength - MODBUS_CRC_LENGTH);
* _responseBuffer[_responseBufferLength - MODBUS_CRC_LENGTH] = crc & 0xFF;
* _responseBuffer[(_responseBufferLength - MODBUS_CRC_LENGTH) + 1] = crc >> 8;
*
*
* #define MODBUS_FRAME_SIZE 4
* #define MODBUS_CRC_LENGTH 2
* uint16_t crc = readCRC(_requestBuffer, _requestBufferLength);
* #define readUInt16(arr, index) word(arr[index], arr[index + 1])
* #define readCRC(arr, length) word(arr[(length - MODBUS_CRC_LENGTH) + 1], arr[length - MODBUS_CRC_LENGTH])
*/
uint16_t calculateCRC(uint8_t *buffer, int length)
{
int i, j;
uint16_t crc = 0xFFFF;
uint16_t tmp;
// Calculate the CRC.
for (i = 0; i < length; i++)
{
crc = crc ^ buffer[i];
for (j = 0; j < 8; j++)
{
tmp = crc & 0x0001;
crc = crc >> 1;
if (tmp)
{
crc = crc ^ 0xA001;
}
}
}
return crc;
}
HCIEmulator::HCIEmulator()
{
m_state.valid = false;
m_statemachine = WAITING;
m_rxlen = m_txlen = 0;
m_recvTime = m_lastStateTime = 0;
m_skipFrame = false;
m_port = &Serial2;
m_statusCallback = NULL;
setLogLevel(DEFAULTLOGLEVEL);
};
#define TX_ON 25
void HCIEmulator::poll()
{
if (m_port == NULL)
return;
// receive Data
if (m_port->available() > 0)
{
// Serial.println("got data");
m_rxlen += m_port->readBytes((char *)(m_rxbuffer + m_rxlen), _min((int)(255 - m_rxlen), m_port->available()));
if (m_rxlen > 254)
{
Log(LL_ERROR, "RX Bufferoverflow, skip next Frame");
Log3(LL_DEBUG, "Buffer Data: ", m_rxbuffer, m_rxlen);
m_rxlen = 0;
m_skipFrame = true;
}
m_recvTime = micros();
}
// Serial.printf("Data % x\n", m_txbuffer);
// check frame, process frame
if (m_rxlen > 0 && (micros() - m_recvTime > T3_5))
{
// Serial.printf("Act on it % x\n", m_txbuffer);
// check last action timeout -> reset > then 2sec
if (m_statemachine != WAITING && m_lastStateTime + 2000 < millis())
{
m_statemachine = WAITING;
}
if (!m_skipFrame)
{
processFrame();
// send response
if (m_txlen > 0)
{
// fix crc
uint16_t crc = calculateCRC(m_txbuffer, m_txlen - MODBUS_CRC_LENGTH);
m_txbuffer[m_txlen - MODBUS_CRC_LENGTH] = crc & 0xFF;
m_txbuffer[(m_txlen - MODBUS_CRC_LENGTH) + 1] = crc >> 8;
// send data
m_lastSendTime = micros() - m_recvTime;
// Log(LL_DEBUG, ("ST:"+String(m_lastSendTime)).c_str());
digitalWrite(TX_ON, HIGH);
// Log3(LL_DEBUG, "write data: ");
m_port->write(m_txbuffer, m_txlen);
Log3(LL_DEBUG, "Response: ", m_txbuffer, m_txlen);
delayMicroseconds(m_txlen * 9 * 22); // 8 bits + par * Bittime 18 micros on 57600 bauds
digitalWrite(TX_ON, LOW);
m_txlen = 0;
}
}
else
{
Serial.println("skipped frame");
}
m_skipFrame = false;
m_rxlen = 0;
}
}
void HCIEmulator::processFrame()
{
m_txlen = 0; // clear send buffer
if (m_rxlen < 5)
{
Log(LL_ERROR, "Frame skipped, invalid frame len");
Log3(LL_ERROR, "Data:", m_rxbuffer, m_rxlen);
return;
}
// check device id, pass only device id 2 and 0 (broadcast)
if (m_rxbuffer[0] != BROADCASTID && m_rxbuffer[0] != DEVICEID)
{
Log(LL_DEBUG, "Frame skipped, unsupported device id");
Log3(LL_DEBUG, "Data:", m_rxbuffer, m_rxlen);
return;
}
// check crc
uint16_t crc = readCRC(m_rxbuffer, m_rxlen);
if (crc != calculateCRC(m_rxbuffer, m_rxlen - MODBUS_CRC_LENGTH))
{
Log3(LL_ERROR, "Frame skipped, wrong crc", m_rxbuffer, m_rxlen);
return;
}
Log3(LL_DEBUG, "Incomming Data: ", m_rxbuffer, m_rxlen);
// dispatch modbus function
switch (m_rxbuffer[1])
{
case 0x10:
{ // Write Multiple registers
if (m_rxlen == 0x1b && m_rxbuffer[0] == BROADCASTID)
{
processBroadcastStatusFrame();
return;
}
break;
}
case 0x17:
{ // Read/Write Multiple registers
if (m_rxbuffer[0] == DEVICEID)
{
switch (m_rxlen)
{
case 0x11:
{
processDeviceStatusFrame();
return;
}
case 0x13:
processDeviceBusScanFrame();
return;
;
}
}
break;
}
}
Log3(LL_ERROR, "Frame skipped, unexpected data: ", m_rxbuffer, m_rxlen);
}
const unsigned char ResponseTemplate_Fcn17_Cmd03_L08[] = {0x02, 0x17, 0x10, 0x3E, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x1B};
const unsigned char ResponseTemplate_Fcn17_Cmd04_L02[] = {0x02, 0x17, 0x04, 0x0F, 0x00, 0x04, 0xFD, 0x0A, 0x72};
void HCIEmulator::processDeviceStatusFrame()
{
if (m_rxlen == 0x11)
{
unsigned char counter = m_rxbuffer[11];
unsigned char cmd = m_rxbuffer[12];
if (m_rxbuffer[5] == 0x08)
{
// expose internal state
// 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
// 0011: 02 17 9C B9 00 08 9C 41 00 02 04 3E 03 00 00 EB CC
// res=> 02 17 10 3E 00 03 01 00 00 00 00 00 00 00 00 00 00 00 00 74 1B
memcpy(m_txbuffer, ResponseTemplate_Fcn17_Cmd03_L08, sizeof(ResponseTemplate_Fcn17_Cmd03_L08));
m_txbuffer[0] = m_rxbuffer[0];
m_txbuffer[3] = counter;
m_txbuffer[5] = cmd;
m_txlen = sizeof(ResponseTemplate_Fcn17_Cmd03_L08);
switch (m_statemachine)
{
// open Door
case STARTOPENDOOR:
m_txbuffer[7] = 0x02;
m_txbuffer[8] = 0x10;
m_statemachine = STARTOPENDOOR_RELEASE;
m_lastStateTime = millis();
break;
case STARTOPENDOOR_RELEASE:
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
{
m_txbuffer[7] = 0x01;
m_txbuffer[8] = 0x10;
m_statemachine = WAITING;
}
break;
// close Door
case STARTCLOSEDOOR:
m_txbuffer[7] = 0x02;
m_txbuffer[8] = 0x20;
m_statemachine = STARTCLOSEDOOR_RELEASE;
m_lastStateTime = millis();
break;
case STARTCLOSEDOOR_RELEASE:
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
{
m_txbuffer[7] = 0x01;
m_txbuffer[8] = 0x20;
m_statemachine = WAITING;
}
break;
// stop Door
case STARTSTOPDOOR:
m_txbuffer[7] = 0x02;
m_txbuffer[8] = 0x40;
m_statemachine = STARTSTOPDOOR_RELEASE;
m_lastStateTime = millis();
break;
case STARTSTOPDOOR_RELEASE:
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
{
m_txbuffer[7] = 0x01;
m_txbuffer[8] = 0x40;
m_statemachine = WAITING;
}
break;
// Ventilation
case STARTVENTPOSITION:
m_txbuffer[7] = 0x02;
m_txbuffer[9] = 0x40;
m_statemachine = STARTVENTPOSITION_RELEASE;
m_lastStateTime = millis();
break;
case STARTVENTPOSITION_RELEASE:
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
{
m_txbuffer[7] = 0x01;
m_txbuffer[9] = 0x40;
m_statemachine = WAITING;
}
break;
// Half Position
case STARTOPENDOORHALF:
m_txbuffer[7] = 0x02;
m_txbuffer[9] = 0x04;
m_statemachine = STARTOPENDOORHALF_RELEASE;
m_lastStateTime = millis();
break;
case STARTOPENDOORHALF_RELEASE:
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
{
m_txbuffer[7] = 0x01;
m_txbuffer[9] = 0x04;
m_statemachine = WAITING;
}
break;
// Toggle Lamp
case STARTTOGGLELAMP:
m_txbuffer[7] = 0x10;
m_txbuffer[9] = 0x02;
m_statemachine = STARTTOGGLELAMP_RELEASE;
m_lastStateTime = millis();
break;
case STARTTOGGLELAMP_RELEASE:
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
{
m_txbuffer[7] = 0x08;
m_txbuffer[9] = 0x02;
m_statemachine = WAITING;
}
break;
case WAITING:
break;
}
return;
}
else if (m_rxbuffer[5] == 0x02)
{
// 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
// 0011: 02 17 9C B9 00 02 9C 41 00 02 04 0F 04 17 00 7B 21
// res=> 02 17 04 0F 00 04 FD 0A 72
memcpy(m_txbuffer, ResponseTemplate_Fcn17_Cmd04_L02, sizeof(ResponseTemplate_Fcn17_Cmd04_L02));
m_txbuffer[0] = m_rxbuffer[0];
m_txbuffer[3] = counter;
m_txbuffer[5] = cmd;
m_txlen = sizeof(ResponseTemplate_Fcn17_Cmd04_L02);
return;
}
}
Log3(LL_ERROR, "Frame skipped, unexpected data: ", m_rxbuffer, m_rxlen);
}
const unsigned char ResponseTemplate_Fcn17_Cmd02_L05[] = {0x02, 0x17, 0x0a, 0x00, 0x00, 0x02, 0x05, 0x04, 0x30, 0x10, 0xff, 0xa8, 0x45, 0x0e, 0xdf};
void HCIEmulator::processDeviceBusScanFrame()
{
// 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
// 0013: 02 17 9C B9 00 05 9C 41 00 03 06 00 02 00 00 01 02 f8 35
// res=> 02 17 0a 00 00 02 05 04 30 10 ff a8 45 0e df
unsigned char counter = m_rxbuffer[11];
unsigned char cmd = m_rxbuffer[12];
memcpy(m_txbuffer, ResponseTemplate_Fcn17_Cmd02_L05, sizeof(ResponseTemplate_Fcn17_Cmd02_L05));
m_txbuffer[0] = m_rxbuffer[0];
m_txbuffer[3] = counter;
m_txbuffer[5] = cmd;
m_txlen = sizeof(ResponseTemplate_Fcn17_Cmd02_L05);
Log(LL_INFO, "Busscan received");
}
void HCIEmulator::processBroadcastStatusFrame()
{
// 001B: 00 10 9D 31 00 09 12 64 00 00 00 40 60 00 00 00 00 00 00 00 00 00 01 00 00 CA 22
bool hasChanged = false;
CHECKCHANGEDSET(m_state.lampOn, m_rxbuffer[20] == 0x14, hasChanged);
CHECKCHANGEDSET(m_state.doorCurrentPosition, m_rxbuffer[10], hasChanged);
CHECKCHANGEDSET(m_state.doorTargetPosition, m_rxbuffer[9], hasChanged);
CHECKCHANGEDSET(m_state.doorState, m_rxbuffer[11], hasChanged);
CHECKCHANGEDSET(m_state.reserved, m_rxbuffer[17], hasChanged);
CHECKCHANGEDSET(m_state.valid, true, hasChanged);
if (hasChanged)
{
Log3(LL_INFO, "New State: ", m_rxbuffer, m_rxlen);
if (m_statusCallback != NULL)
{
m_statusCallback(m_state);
}
}
}
void HCIEmulator::openDoor()
{
if (m_statemachine != WAITING)
{
return;
}
m_lastStateTime = millis();
m_statemachine = STARTOPENDOOR;
}
void HCIEmulator::openDoorHalf()
{
if (m_statemachine != WAITING)
{
return;
}
m_lastStateTime = millis();
m_statemachine = STARTOPENDOORHALF;
}
void HCIEmulator::closeDoor()
{
if (m_statemachine != WAITING)
{
return;
}
m_lastStateTime = millis();
m_statemachine = STARTCLOSEDOOR;
}
void HCIEmulator::stopDoor()
{
if (m_statemachine != WAITING)
{
return;
}
m_lastStateTime = millis();
m_statemachine = STARTSTOPDOOR;
}
void HCIEmulator::toggleLamp()
{
if (m_statemachine != WAITING)
{
return;
}
m_lastStateTime = millis();
m_statemachine = STARTTOGGLELAMP;
}
void HCIEmulator::ventilationPosition()
{
if (m_statemachine != WAITING)
{
return;
}
m_lastStateTime = millis();
m_statemachine = STARTVENTPOSITION;
}
void HCIEmulator::onStatusChanged(callback_function_t handler)
{
m_statusCallback = handler;
}

View File

@ -0,0 +1,138 @@
#ifndef __hciemulator_h
#define __hciemulator_h
#include <Arduino.h>
#include <Stream.h>
#include <SoftwareSerial.h>
#define LL_OFF 0
#define LL_ERROR 1
#define LL_WARN 2
#define LL_INFO 3
#define LL_DEBUG 4
#define DEFAULTLOGLEVEL LL_INFO
#define DEVICEID 0x02
#define BROADCASTID 0x00
#define SIMULATEKEYPRESSDELAYMS 100
// Modbus states that a baud rate higher than 19200 must use a fixed 750 us
// for inter character time out and 1.75 ms for a frame delay.
// For baud rates below 19200 the timeing is more critical and has to be calculated.
// E.g. 9600 baud in a 10 bit packet is 960 characters per second
// In milliseconds this will be 960characters per 1000ms. So for 1 character
// 1000ms/960characters is 1.04167ms per character and finaly modbus states an
// intercharacter must be 1.5T or 1.5 times longer than a normal character and thus
// 1.5T = 1.04167ms * 1.5 = 1.5625ms. A frame delay is 3.5T.
#define T1_5 750
#define T3_5 4800 // 1750
enum DoorState : uint8_t
{
DOOR_OPEN_POSITION = 0x20,
DOOR_CLOSE_POSITION = 0x40,
DOOR_HALF_POSITION = 0x80,
DOOR_MOVE_CLOSEPOSITION = 0x02,
DOOR_MOVE_OPENPOSITION = 0x01,
};
struct SHCIState
{
bool valid;
bool lampOn;
uint8_t doorState; // see DoorState
uint8_t doorCurrentPosition;
uint8_t doorTargetPosition;
uint8_t reserved;
};
enum StateMachine : uint8_t
{
WAITING,
STARTOPENDOOR,
STARTOPENDOOR_RELEASE,
STARTOPENDOORHALF,
STARTOPENDOORHALF_RELEASE,
STARTCLOSEDOOR,
STARTCLOSEDOOR_RELEASE,
STARTSTOPDOOR,
STARTSTOPDOOR_RELEASE,
STARTTOGGLELAMP,
STARTTOGGLELAMP_RELEASE,
STARTVENTPOSITION,
STARTVENTPOSITION_RELEASE
};
class HCIEmulator
{
public:
typedef std::function<void(const SHCIState &)> callback_function_t;
HCIEmulator();
void poll();
void openDoor();
void openDoorHalf();
void closeDoor();
void stopDoor();
void toggleLamp();
void ventilationPosition();
const SHCIState &getState()
{
if (micros() - m_recvTime > 2000000)
{
// 2 sec without statusmessage
m_state.valid = false;
}
return m_state;
};
unsigned long getMessageAge()
{
return micros() - m_recvTime;
}
int getLogLevel();
void setLogLevel(int level);
void onStatusChanged(callback_function_t handler);
protected:
void processFrame();
void processDeviceStatusFrame();
void processDeviceBusScanFrame();
void processBroadcastStatusFrame();
private:
callback_function_t m_statusCallback;
Stream *m_port;
SHCIState m_state;
StateMachine m_statemachine;
unsigned long m_recvTime;
unsigned long m_lastStateTime;
unsigned long m_lastSendTime;
size_t m_rxlen;
size_t m_txlen;
unsigned char m_rxbuffer[255] = {
0,
};
unsigned char m_txbuffer[255] = {
0,
};
bool m_skipFrame;
};
#endif

View File

@ -0,0 +1,18 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.components import output
from esphome.const import CONF_ID
hoermann_cover_ns = cg.esphome_ns.namespace('hoermann_door')
HoermannCoverLight = hoermann_cover_ns.class_('NbsLightOutput', output.BinaryOutput, cg.Component)
CONFIG_SCHEMA = output.BINARY_OUTPUT_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(HoermannCoverLight)
}).extend(cv.COMPONENT_SCHEMA)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield output.register_output(var, config)

56
garage1.yaml Normal file
View File

@ -0,0 +1,56 @@
substitutions:
garageSide: sy # sy, wo
esphome:
name: garage${garageSide}
platform: ESP32
board: esp32dev
libraries:
- plerup/EspSoftwareSerial
# custom_component:
# - lambda: |-
# auto door = new HoermanDoor();
# App.register_component(door);
# return {door};
# components:
# - id: door_${garageSide}
# - lambda: |-
# auto light = new NbsLightOutput();
# App.register_component(light);
# return {light};
# components:
# - id: door_${garageSide}_lamp
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: true
logger:
#level: VERY_VERBOSE
external_components:
- source:
type: local
path: external_components/
components: [ hoermann_door ]
cover:
- platform: hoermann_door
name: door_${garageSide}
light:
- platform: binary
name: door_${garageSide}_lamp
output: light_out
output:
- id: light_out
platform: hoermann_door
ota:
password: !secret gd_passwd
api:
encryption:
key: !secret gd_key