Merge branch 'mega' into UweKaditz

This commit is contained in:
TD-er
2020-04-09 23:20:41 +02:00
committed by GitHub
32 changed files with 1163 additions and 60 deletions
+48
View File
@@ -0,0 +1,48 @@
.. include:: ../Plugin/_plugin_substitutions_p09x.repl
.. _P094_page:
|P094_typename|
==================================================
|P094_shortinfo|
Plugin details
--------------
Type: |P094_type|
Name: |P094_name|
Status: |P094_status|
GitHub: |P094_github|_
Maintainer: |P094_maintainer|
Used libraries: |P094_usedlibraries|
Description
-----------
This plugin is written to interact with the `CUL firmware <http://culfw.de/commandref.html>`_
Supported hardware
------------------
|P094_usedby|
Commands available
^^^^^^^^^^^^^^^^^^
.. include:: P094_commands.repl
Change log
----------
.. versionchanged:: 2.0
...
|added| 2020-04-09
+13
View File
@@ -0,0 +1,13 @@
.. csv-table::
:header: "Command", "Extra information"
:widths: 20, 30
"
``culreader_write``
","
Examples:
- ``culreader_write,brt`` Switch CULfw into Wireless M-Bus receive mode 'T'
"
+1
View File
@@ -117,6 +117,7 @@ There's three different released versions of ESP Easy:
":ref:`P091_page`","|P091_status|","P091"
":ref:`P092_page`","|P092_status|","P092"
":ref:`P093_page`","|P093_status|","P093"
":ref:`P094_page`","|P094_status|","P094"
Internal GPIO handling
+1 -1
View File
@@ -1,5 +1,5 @@
.. |Plugin_Analog_input| replace:: :ref:`P002_page`, :ref:`P007_page`, :ref:`P025_page`, :ref:`P060_page`
.. |Plugin_Communication| replace:: :ref:`P016_page`, :ref:`P020_page`, :ref:`P035_page`, :ref:`P044_page`, :ref:`P054_page`, :ref:`P071_page`, :ref:`P087_page`, :ref:`P089_page`
.. |Plugin_Communication| replace:: :ref:`P016_page`, :ref:`P020_page`, :ref:`P035_page`, :ref:`P044_page`, :ref:`P054_page`, :ref:`P071_page`, :ref:`P087_page`, :ref:`P089_page`, :ref:`P094_page`
.. |Plugin_Display| replace:: :ref:`P012_page`, :ref:`P023_page`, :ref:`P036_page`, :ref:`P057_page`, :ref:`P073_page`, :ref:`P075_page`
.. |Plugin_Dust| replace:: :ref:`P018_page`, :ref:`P053_page`, :ref:`P056_page`
.. |Plugin_Energy_AC| replace:: :ref:`P076_page`, :ref:`P077_page`, :ref:`P078_page`
@@ -43,3 +43,15 @@
.. |P093_maintainer| replace:: `crnjan`
.. |P093_compileinfo| replace:: `.`
.. |P093_usedlibraries| replace:: `.`
.. |P094_name| replace:: :cyan:`Communication - CUL Reader`
.. |P094_type| replace:: :cyan:`Communication`
.. |P094_typename| replace:: :cyan:`CUL Reader`
.. |P094_status| replace:: :yellow:`TESTING`
.. |P094_github| replace:: P094_CULReader.ino
.. _P094_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P094_CULReader.ino
.. |P094_usedby| replace:: `.`
.. |P094_shortinfo| replace:: `Interact with the CUL firmware`
.. |P094_maintainer| replace:: `TD-er`
.. |P094_compileinfo| replace:: `.`
.. |P094_usedlibraries| replace:: `.`
+2
View File
@@ -37,6 +37,8 @@ else:
"USES_P085", # AcuDC24x
"USES_P087", # Serial Proxy
"USES_P094", # CUL Reader
"USES_C016", # Cache Controller
"USES_C018", # TTN/RN2483
+1 -1
View File
@@ -116,5 +116,5 @@ void addIPaccessControlSelect(const String& name, int choice)
{
String options[3] = { F("Allow All"), F("Allow Local Subnet"), F("Allow IP range") };
addSelector(name, 3, options, NULL, NULL, choice, false);
addSelector(name, 3, options, NULL, NULL, choice);
}
+5 -5
View File
@@ -226,11 +226,11 @@ void addFormDstSelect(bool isStart, uint16_t choice) {
}
TimeChangeRule rule(isStart ? tmpstart : tmpend, 0);
addRowLabel(weeklabel);
addSelector(weekid, 5, week, weekValues, NULL, rule.week, false);
addSelector(weekid, 5, week, weekValues, NULL, rule.week);
html_BR();
addSelector(dowid, 7, dow, dowValues, NULL, rule.dow, false);
addSelector(dowid, 7, dow, dowValues, NULL, rule.dow);
html_BR();
addSelector(monthid, 12, month, monthValues, NULL, rule.month, false);
addSelector(monthid, 12, month, monthValues, NULL, rule.month);
addFormNumericBox(hourlabel, hourid, rule.hour, 0, 23);
addUnit(isStart ? F("hour &#x21b7;") : F("hour &#x21b6;"));
@@ -253,7 +253,7 @@ void addLogLevelSelect(const String& name, int choice)
for (int i = 0; i < LOG_LEVEL_NRELEMENTS; ++i) {
options[i + 1] = getLogLevelDisplayStringFromIndex(i, optionValues[i + 1]);
}
addSelector(name, LOG_LEVEL_NRELEMENTS + 1, options, optionValues, NULL, choice, false);
addSelector(name, LOG_LEVEL_NRELEMENTS + 1, options, optionValues, NULL, choice);
}
void addFormLogFacilitySelect(const String& label, const String& id, int choice)
@@ -269,7 +269,7 @@ void addLogFacilitySelect(const String& name, int choice)
F("Local2"), F("Local3"), F("Local4"), F("Local5"), F("Local6"), F("Local7") };
int optionValues[12] = { 0, 1, 3, 5, 16, 17, 18, 19, 20, 21, 22, 23 };
addSelector(name, 12, options, optionValues, NULL, choice, false);
addSelector(name, 12, options, optionValues, NULL, choice);
}
#endif // ifdef WEBSERVER_ADVANCED
+1 -1
View File
@@ -233,7 +233,7 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex
addFormHeader(F("Controller Settings"));
addRowLabel(F("Protocol"));
byte choice = Settings.Protocol[controllerindex];
addSelector_Head(F("protocol"), true);
addSelector_Head_reloadOnChange(F("protocol"));
addSelector_Item(F("- Standalone -"), 0, false, false, "");
for (byte x = 0; x <= protocolCount; x++)
+1 -1
View File
@@ -54,7 +54,7 @@ boolean handle_custom(String path) {
html_add_form();
// create unit selector dropdown
addSelector_Head(F("unit"), true);
addSelector_Head_reloadOnChange(F("unit"));
byte choice = Settings.Unit;
for (NodesMap::iterator it = Nodes.begin(); it != Nodes.end(); ++it)
+7 -2
View File
@@ -170,7 +170,7 @@ void addDeviceSelect(const String& name, int choice)
{
String deviceName;
addSelector_Head(name, true);
addSelector_Head_reloadOnChange(name);
addSelector_Item(F("- None -"), 0, false, false, "");
for (byte x = 0; x <= deviceCount; x++)
@@ -431,6 +431,11 @@ void handle_devicess_ShowAllTasksTable(byte page)
case DEVICE_TYPE_I2C:
addHtml(F("I2C"));
break;
case DEVICE_TYPE_SERIAL:
case DEVICE_TYPE_SERIAL_PLUS1:
addHtml(serialHelper_getSerialTypeLabel(&TempEvent));
break;
default:
// Plugin has no custom port formatting, show default one.
@@ -514,7 +519,7 @@ void handle_devicess_ShowAllTasksTable(byte page)
// fallthrough
case DEVICE_TYPE_SERIAL:
{
addHtml(serialHelper_getGpioDescription(Settings.TaskDevicePin1[x], Settings.TaskDevicePin2[x]));
addHtml(serialHelper_getGpioDescription(Settings.TaskDevicePin1[x], Settings.TaskDevicePin2[x], F("<BR>")));
if (showpin3) {
html_BR();
}
+1 -1
View File
@@ -90,7 +90,7 @@ void handle_factoryreset() {
void addPreDefinedConfigSelector() {
DeviceModel active_model = ResetFactoryDefaultPreference.getDeviceModel();
addSelector_Head("fdm", true);
addSelector_Head_reloadOnChange("fdm");
for (byte x = 0; x < DeviceModel_MAX; ++x) {
DeviceModel model = static_cast<DeviceModel>(x);
+28 -14
View File
@@ -2,6 +2,16 @@
// Add Selector
// ********************************************************************************
void addSelector(const String& id,
int optionCount,
const String options[],
const int indices[],
const String attr[],
int selectedIndex)
{
addSelector(id, optionCount, options, indices, attr, selectedIndex, false, true, F("wide"));
}
void addSelector(const String& id,
int optionCount,
const String options[],
@@ -25,7 +35,12 @@ void addSelector(const String& id,
const String& classname)
{
// FIXME TD-er Change boolean to disabled
addSelector_Head(id, classname, reloadonchange, !enabled);
if (reloadonchange)
{
addSelector_Head_reloadOnChange(id, classname, !enabled);
} else {
do_addSelector_Head(id, classname, "", !enabled);
}
addSelector_options(optionCount, options, indices, attr, selectedIndex);
addSelector_Foot();
}
@@ -63,24 +78,23 @@ void addSelector_options(int optionCount, const String options[], const int indi
}
}
void addSelector_Head(const String& id, bool reloadonchange) {
addSelector_Head(id, F("wide"), reloadonchange, false);
void addSelector_Head(const String& id) {
do_addSelector_Head(id, F("wide"), "", false);
}
void addSelector_Head_reloadOnChange(const String& id, const String& classname) {
addSelector_Head(id, classname, true, false);
void addSelector_Head_reloadOnChange(const String& id) {
addSelector_Head_reloadOnChange(id, F("wide"), false);
}
void addSelector_Head(const String& id, const String& classname, bool reloadonchange, bool disabled)
{
if (reloadonchange) {
do_addSelector_Head(id, classname, F("return dept_onchange(frmselect)"), disabled);
} else {
do_addSelector_Head(id, classname, "", disabled);
}
void addSelector_Head_reloadOnChange(const String& id, const String& classname, bool disabled) {
do_addSelector_Head(id, classname, F("return dept_onchange(frmselect)"), disabled);
}
void do_addSelector_Head(const String& id, const String& classname, const String& onChangeCall, bool disabled)
void addSelector_Head_reloadOnChange(const String& id, const String& classname, const String& onChangeCall, bool disabled) {
do_addSelector_Head(id, classname, onChangeCall, disabled);
}
void do_addSelector_Head(const String& id, const String& classname, const String& onChangeCall, const bool& disabled)
{
{
String html;
@@ -537,7 +551,7 @@ void addPinSelect(boolean forI2C, String id, int choice)
// Helper function actually rendering dropdown list for addPinSelect()
// ********************************************************************************
void renderHTMLForPinSelect(String options[], int optionValues[], boolean forI2C, const String& id, int choice, int count) {
addSelector_Head(id, false);
addSelector_Head(id);
for (byte x = 0; x < count; x++)
{
+2 -2
View File
@@ -222,7 +222,7 @@ void addFormSelector(const String& label,
boolean reloadonchange)
{
addRowLabel_tr_id(label, id);
addSelector(id, optionCount, options, indices, attr, selectedIndex, reloadonchange);
addSelector(id, optionCount, options, indices, attr, selectedIndex, reloadonchange, true);
}
void addFormSelector_script(const String& label,
@@ -235,7 +235,7 @@ void addFormSelector_script(const String& label,
const String& onChangeCall)
{
addRowLabel_tr_id(label, id);
addSelector_Head(id, "", onChangeCall, false);
do_addSelector_Head(id, "", onChangeCall, false);
addSelector_options(optionCount, options, indices, attr, selectedIndex);
addSelector_Foot();
}
+1 -1
View File
@@ -134,7 +134,7 @@ void handle_notifications() {
addFormHeader(F("Notification Settings"));
addRowLabel(F("Notification"));
byte choice = Settings.Notification[notificationindex];
addSelector_Head(F("notification"), true);
addSelector_Head_reloadOnChange(F("notification"));
addSelector_Item(F("- None -"), 0, false, false, "");
for (byte x = 0; x <= notificationCount; x++)
+1 -1
View File
@@ -96,7 +96,7 @@ void handle_rules() {
html_TR_TD();
addHtml(F("<form name = 'frmselect'>"));
addSelector(F("set"), RULESETS_MAX, options, optionValues, NULL, choice, true);
addSelector(F("set"), RULESETS_MAX, options, optionValues, NULL, choice, true, true);
addHelpButton(F("Tutorial_Rules"));
Rule_showRuleTextArea(fileName);
+1 -1
View File
@@ -42,7 +42,7 @@ void handle_sysvars() {
#if defined(ESP8266)
addSysVar_enum_html(SystemVariables::MAC_INT);
#endif // if defined(ESP8266)
addSysVar_enum_html(SystemVariables::IP4);
addSysVar_enum_html(SystemVariables::IP);
addSysVar_enum_html(SystemVariables::IP4);
addSysVar_enum_html(SystemVariables::RSSI);
addSysVar_enum_html(SystemVariables::SSID);
+2 -2
View File
@@ -110,7 +110,7 @@ void addSelector(const String& id,
const int indices[],
const String attr[],
int selectedIndex,
boolean reloadonchange = false,
bool enabled = true);
boolean reloadonchange,
bool enabled);
#endif // WEBSERVER_FWD_DECL_H
+1 -1
View File
@@ -101,7 +101,7 @@ void sensorTypeHelper_webformLoad(struct EventStruct *event, byte pconfigIndex,
PCONFIG(pconfigIndex) = choice;
}
addRowLabel(F("Output Data Type"));
addSelector_Head(PCONFIG_LABEL(pconfigIndex), false);
addSelector_Head(PCONFIG_LABEL(pconfigIndex));
for (byte x = 0; x < optionCount; x++)
{
+1 -1
View File
@@ -86,7 +86,7 @@ boolean Plugin_004(byte function, struct EventStruct *event, String& string)
// find all suitable devices
addRowLabel(F("Device Address"));
addSelector_Head(F("p004_dev"), false);
addSelector_Head(F("p004_dev"));
addSelector_Item("", -1, false, false, "");
uint8_t tmpAddress[8];
byte count = 0;
+1 -1
View File
@@ -70,7 +70,7 @@ boolean Plugin_043(byte function, struct EventStruct *event, String& string)
addHtml(" ");
byte choice = ExtraTaskSettings.TaskDevicePluginConfig[x];
addSelector(String(F("p043_state")) + (x), 3, options, NULL, NULL, choice, false);
addSelector(String(F("p043_state")) + (x), 3, options, NULL, NULL, choice);
}
success = true;
break;
+1 -1
View File
@@ -72,7 +72,7 @@ boolean Plugin_080(byte function, struct EventStruct * event, String& string)
// find all suitable devices
addRowLabel(F("Device Address"));
addSelector_Head(F("p080_dev"), false);
addSelector_Head(F("p080_dev"));
addSelector_Item("", -1, false, false, "");
uint8_t tmpAddress[8];
byte count = 0;
+1 -8
View File
@@ -397,14 +397,7 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
if (P082_data->init(serial_rx, serial_tx)) {
success = true;
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("GPS : Init OK ESP GPIO-pin RX:");
log += serial_rx;
log += F(" TX:");
log += serial_tx;
addLog(LOG_LEVEL_DEBUG, log);
}
serialHelper_log_GpioDescription(serial_rx, serial_tx);
if (pps_pin != -1) {
// pinMode(pps_pin, INPUT_PULLUP);
+1
View File
@@ -334,6 +334,7 @@ boolean Plugin_085(byte function, struct EventStruct *event, String& string) {
if (P085_data->init(serial_rx, serial_tx, P085_DEPIN,
p085_storageValueToBaudrate(P085_BAUDRATE),
P085_DEV_ID)) {
serialHelper_log_GpioDescription(serial_rx, serial_tx);
success = true;
} else {
clearPluginTaskData(event->TaskIndex);
+1 -8
View File
@@ -177,14 +177,7 @@ boolean Plugin_087(byte function, struct EventStruct *event, String& string) {
LoadCustomTaskSettings(event->TaskIndex, P087_data->_lines, P87_Nlines, 0);
P087_data->post_init();
success = true;
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("Serial : Init OK ESP GPIO-pin RX:");
log += serial_rx;
log += F(" TX:");
log += serial_tx;
addLog(LOG_LEVEL_DEBUG, log);
}
serialHelper_log_GpioDescription(serial_rx, serial_tx);
} else {
clearPluginTaskData(event->TaskIndex);
}
+1 -1
View File
@@ -698,7 +698,7 @@ boolean Plugin_093(byte function, struct EventStruct *event, String& string) {
case PLUGIN_DEVICE_ADD: {
Device[++deviceCount].Number = PLUGIN_ID_093;
Device[deviceCount].Type = DEVICE_TYPE_DUAL;
Device[deviceCount].Type = DEVICE_TYPE_SERIAL;
Device[deviceCount].VType = SENSOR_TYPE_STRING;
Device[deviceCount].ValueCount = 1;
Device[deviceCount].SendDataOption = true;
+425
View File
@@ -0,0 +1,425 @@
#ifdef USES_P094
// #######################################################################################################
// #################### Plugin 094 Brick4U CUL Reader ####################################################
// #######################################################################################################
//
// Interact with Brick4U CUL receiver
// Allows to control the mode of the CUL receiver
//
#include "_Plugin_Helper.h"
#include "src/PluginStructs/P094_data_struct.h"
#include <Regexp.h>
#define PLUGIN_094
#define PLUGIN_ID_094 94
#define PLUGIN_NAME_094 "Communication - CUL Reader"
#define P094_BAUDRATE PCONFIG_LONG(0)
#define P094_BAUDRATE_LABEL PCONFIG_LABEL(0)
#define P094_QUERY_VALUE 0 // Temp placement holder until we know what selectors are needed.
#define P094_NR_OUTPUT_OPTIONS 1
#define P094_NR_OUTPUT_VALUES 1
#define P094_QUERY1_CONFIG_POS 3
#define P094_DEFAULT_BAUDRATE 38400
// Plugin settings:
// Validate:
// - [0..9]
// - "+", "-", "."
// - [A..Z]
// - [a..z]
// - ASCII 32 - 217
// Sentence start: char
// Sentence end: CR/CRLF/LF/char
// Max length sentence: 1k max
// Interpret as:
// - Float
// - int
// - String
// Init string (incl parsing CRLF like characters)
// Timeout between sentences.
boolean Plugin_094(byte function, struct EventStruct *event, String& string) {
boolean success = false;
switch (function) {
case PLUGIN_DEVICE_ADD: {
Device[++deviceCount].Number = PLUGIN_ID_094;
Device[deviceCount].Type = DEVICE_TYPE_SERIAL;
Device[deviceCount].VType = SENSOR_TYPE_STRING;
Device[deviceCount].Ports = 0;
Device[deviceCount].PullUpOption = false;
Device[deviceCount].InverseLogicOption = false;
Device[deviceCount].FormulaOption = false;
Device[deviceCount].ValueCount = 1;
Device[deviceCount].SendDataOption = true;
Device[deviceCount].TimerOption = true;
Device[deviceCount].GlobalSyncOption = false;
break;
}
case PLUGIN_GET_DEVICENAME: {
string = F(PLUGIN_NAME_094);
break;
}
case PLUGIN_GET_DEVICEVALUENAMES: {
for (byte i = 0; i < VARS_PER_TASK; ++i) {
if (i < P094_NR_OUTPUT_VALUES) {
const byte pconfigIndex = i + P094_QUERY1_CONFIG_POS;
byte choice = PCONFIG(pconfigIndex);
safe_strncpy(
ExtraTaskSettings.TaskDeviceValueNames[i],
Plugin_094_valuename(choice, false),
sizeof(ExtraTaskSettings.TaskDeviceValueNames[i]));
} else {
ZERO_FILL(ExtraTaskSettings.TaskDeviceValueNames[i]);
}
}
break;
}
case PLUGIN_GET_DEVICEGPIONAMES: {
serialHelper_getGpioNames(event, false, true); // TX optional
break;
}
case PLUGIN_WEBFORM_SHOW_VALUES:
{
P094_data_struct *P094_data =
static_cast<P094_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P094_data) && P094_data->isInitialized()) {
uint32_t success, error, length_last;
P094_data->getSentencesReceived(success, error, length_last);
byte varNr = VARS_PER_TASK;
addHtml(pluginWebformShowValue(event->TaskIndex, varNr++, F("Success"), String(success)));
addHtml(pluginWebformShowValue(event->TaskIndex, varNr++, F("Error"), String(error)));
addHtml(pluginWebformShowValue(event->TaskIndex, varNr++, F("Length Last"), String(length_last), true));
// success = true;
}
break;
}
case PLUGIN_SET_DEFAULTS:
{
P094_BAUDRATE = P094_DEFAULT_BAUDRATE;
success = true;
break;
}
case PLUGIN_WEBFORM_SHOW_CONFIG:
{
string += serialHelper_getSerialTypeLabel(event);
success = true;
break;
}
case PLUGIN_WEBFORM_LOAD: {
serialHelper_webformLoad(event);
addFormNumericBox(F("Baudrate"), P094_BAUDRATE_LABEL, P094_BAUDRATE, 2400, 115200);
addUnit(F("baud"));
addFormSubHeader(F("Filtering"));
P094_html_show_matchForms(event);
addFormSubHeader(F("Statistics"));
P094_html_show_stats(event);
success = true;
break;
}
case PLUGIN_WEBFORM_SAVE: {
serialHelper_webformSave(event);
P094_BAUDRATE = getFormItemInt(P094_BAUDRATE_LABEL);
P094_data_struct *P094_data =
static_cast<P094_data_struct *>(getPluginTaskData(event->TaskIndex));
if (nullptr != P094_data) {
for (byte varNr = 0; varNr < P94_Nlines; varNr++)
{
P094_data->setLine(varNr, web_server.arg(getPluginCustomArgName(varNr)));
}
addHtmlError(SaveCustomTaskSettings(event->TaskIndex, P094_data->_lines, P94_Nlines, 0));
success = true;
}
break;
}
case PLUGIN_INIT: {
const int16_t serial_rx = CONFIG_PIN1;
const int16_t serial_tx = CONFIG_PIN2;
initPluginTaskData(event->TaskIndex, new P094_data_struct());
P094_data_struct *P094_data =
static_cast<P094_data_struct *>(getPluginTaskData(event->TaskIndex));
if (nullptr == P094_data) {
return success;
}
if (P094_data->init(serial_rx, serial_tx, P094_BAUDRATE)) {
LoadCustomTaskSettings(event->TaskIndex, P094_data->_lines, P94_Nlines, 0);
P094_data->post_init();
success = true;
serialHelper_log_GpioDescription(serial_rx, serial_tx);
} else {
clearPluginTaskData(event->TaskIndex);
}
break;
}
case PLUGIN_EXIT: {
clearPluginTaskData(event->TaskIndex);
success = true;
break;
}
case PLUGIN_FIFTY_PER_SECOND: {
if (Settings.TaskDeviceEnabled[event->TaskIndex]) {
P094_data_struct *P094_data =
static_cast<P094_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P094_data) && P094_data->loop()) {
// schedule_task_device_timer(event->TaskIndex, millis() + 10);
delay(0); // Processing a full sentence may take a while, run some
// background tasks.
P094_data->getSentence(event->String2);
if (event->String2.length() > 0) {
if (Plugin_094_match_all(event->TaskIndex, event->String2)) {
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log = F("CUL Reader: Sending: ");
log += event->String2;
addLog(LOG_LEVEL_INFO, log);
}
sendData(event);
}
}
}
success = true;
}
break;
}
case PLUGIN_READ: {
P094_data_struct *P094_data =
static_cast<P094_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P094_data)) {}
break;
}
case PLUGIN_WRITE: {
String cmd = parseString(string, 1);
if (cmd.startsWith(F("culreader"))) {
if (cmd.equals(F("culreader_write"))) {
P094_data_struct *P094_data =
static_cast<P094_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P094_data)) {
String param1 = parseStringKeepCase(string, 2);
parseSystemVariables(param1, false);
P094_data->sendString(param1);
addLog(LOG_LEVEL_INFO, param1);
success = true;
}
}
}
break;
}
}
return success;
}
bool Plugin_094_match_all(taskIndex_t taskIndex, String& received)
{
P094_data_struct *P094_data =
static_cast<P094_data_struct *>(getPluginTaskData(taskIndex));
if ((nullptr == P094_data)) {
return false;
}
if (P094_data->disableFilterWindowActive()) {
addLog(LOG_LEVEL_INFO, F("CUL Reader: Disable Filter Window active"));
return true;
}
bool res = P094_data->parsePacket(received);
if (P094_data->invertMatch()) {
addLog(LOG_LEVEL_INFO, F("CUL Reader: invert filter"));
return !res;
}
return res;
}
String Plugin_094_valuename(byte value_nr, bool displayString) {
switch (value_nr) {
case P094_QUERY_VALUE: return displayString ? F("Value") : F("v");
}
return "";
}
void P094_html_show_matchForms(struct EventStruct *event) {
P094_data_struct *P094_data =
static_cast<P094_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P094_data)) {
addFormNumericBox(F("Filter Off Window after send"),
getPluginCustomArgName(P094_FILTER_OFF_WINDOW_POS),
P094_data->getFilterOffWindowTime(),
0,
60000);
addUnit(F("msec"));
addFormNote(F("0 = Do not turn off filter after sending to the connected device."));
{
String options[P094_Match_Type_NR_ELEMENTS];
int optionValues[P094_Match_Type_NR_ELEMENTS];
for (int i = 0; i < P094_Match_Type_NR_ELEMENTS; ++i) {
P094_Match_Type matchType = static_cast<P094_Match_Type>(i);
options[i] = P094_data_struct::MatchType_toString(matchType);
optionValues[i] = matchType;
}
P094_Match_Type choice = P094_data->getMatchType();
addFormSelector(F("Filter Mode"),
getPluginCustomArgName(P094_MATCH_TYPE_POS),
P094_Match_Type_NR_ELEMENTS,
options,
optionValues,
choice,
false);
}
byte filterSet = 0;
uint32_t optional = 0;
P094_Filter_Value_Type capture = P094_Filter_Value_Type::P094_packet_length;
P094_Filter_Comp comparator = P094_Filter_Comp::P094_Equal_OR;
String filter;
for (byte filterLine = 0; filterLine < P094_NR_FILTERS; ++filterLine)
{
// Filter parameter number on a filter line.
bool newLine = (filterLine % P094_AND_FILTER_BLOCK) == 0;
for (byte filterLinePar = 0; filterLinePar < P094_ITEMS_PER_FILTER; ++filterLinePar)
{
String id = getPluginCustomArgName(P094_data_struct::P094_Get_filter_base_index(filterLine) + filterLinePar);
switch (filterLinePar) {
case 0:
{
filter = P094_data->getFilter(filterLine, capture, optional, comparator);
if (newLine) {
// Label + first parameter
++filterSet;
String label;
label = F("Filter ");
label += String(filterSet);
addRowLabel_tr_id(label, id);
} else {
addHtml(F("<B>AND</>"));
html_BR();
}
// Combo box with filter types
{
String options[P094_FILTER_VALUE_Type_NR_ELEMENTS];
int optionValues[P094_FILTER_VALUE_Type_NR_ELEMENTS];
for (int i = 0; i < P094_FILTER_VALUE_Type_NR_ELEMENTS; ++i) {
P094_Filter_Value_Type filterValueType = static_cast<P094_Filter_Value_Type>(i);
options[i] = P094_data_struct::P094_FilterValueType_toString(filterValueType);
optionValues[i] = filterValueType;
}
addSelector(id, P094_FILTER_VALUE_Type_NR_ELEMENTS, options, optionValues, NULL, capture, false, true, "");
}
break;
}
case 1:
{
// Optional numerical value
addNumericBox(id, optional, 0, 1024);
break;
}
case 2:
{
// Comparator
String options[P094_FILTER_COMP_NR_ELEMENTS];
int optionValues[P094_FILTER_COMP_NR_ELEMENTS];
for (int i = 0; i < P094_FILTER_COMP_NR_ELEMENTS; ++i) {
P094_Filter_Comp enumValue = static_cast<P094_Filter_Comp>(i);
options[i] = P094_data_struct::P094_FilterComp_toString(enumValue);
optionValues[i] = enumValue;
}
addSelector(id, P094_FILTER_COMP_NR_ELEMENTS, options, optionValues, NULL, comparator, false, true, "");
break;
}
case 3:
{
// Compare with
addTextBox(id, filter, 8, false, false, "", "");
break;
}
}
}
}
}
}
void P094_html_show_stats(struct EventStruct *event) {
P094_data_struct *P094_data =
static_cast<P094_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr == P094_data) || !P094_data->isInitialized()) {
return;
}
{
addRowLabel(F("Current Sentence"));
String sentencePart;
P094_data->getSentence(sentencePart);
addHtml(sentencePart);
}
{
addRowLabel(F("Sentences (pass/fail)"));
String chksumStats;
uint32_t success, error, length_last;
P094_data->getSentencesReceived(success, error, length_last);
chksumStats = success;
chksumStats += '/';
chksumStats += error;
addHtml(chksumStats);
addRowLabel(F("Length Last Sentence"));
addHtml(String(length_last));
}
}
#endif // USES_P094
+1 -1
View File
@@ -63,7 +63,7 @@ bool pluginTaskData_initialized(taskIndex_t taskIndex) {
}
String getPluginCustomArgName(int varNr) {
String argName = F("plugin_custom_arg");
String argName = F("pc_arg");
argName += varNr + 1;
return argName;
}
+12 -4
View File
@@ -19,14 +19,22 @@ static String serialHelper_getSerialTypeLabel(ESPeasySerialType::serialtype serT
return label;
}
static String serialHelper_getGpioDescription(int config_pin1, int config_pin2) {
void serialHelper_log_GpioDescription(int config_pin1, int config_pin2) {
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("Serial : ");
log += serialHelper_getGpioDescription(config_pin1, config_pin2, " ");
addLog(LOG_LEVEL_DEBUG, log);
}
}
static String serialHelper_getGpioDescription(int config_pin1, int config_pin2, const String& newline) {
String result;
result.reserve(20);
switch (ESPeasySerialType::getSerialType(config_pin1, config_pin2)) {
case ESPeasySerialType::serialtype::sc16is752:
{
result += formatToHex(config_pin1);
result += F("<BR>");
result += newline;
result += F(" ch: ");
result += config_pin2 == 0 ? F("A") : F("B");
return result;
@@ -39,7 +47,7 @@ static String serialHelper_getGpioDescription(int config_pin1, int config_pin2)
{
result += F("RX: ");
result += config_pin1;
result += F("<BR>");
result += newline;
result += F("TX: ");
result += config_pin2;
break;
@@ -98,7 +106,7 @@ void serialHelper_addI2CuartSelectors(int address, int channel) {
if (!addr_in_range) {
address = SC16IS752_I2C_BASE_ADDR;
}
addFormSelector(F("I2C Address"), F("i2cuart_addr"), SC16IS752_I2C_ADDRESSES, options, addresses, NULL, address, false);
addFormSelector(F("I2C Address"), F("i2cuart_addr"), SC16IS752_I2C_ADDRESSES, options, addresses, address);
}
{
if (channel != SC16IS752_CHANNEL_A && channel != SC16IS752_CHANNEL_B) {
+2 -1
View File
@@ -832,6 +832,7 @@ To create/register a plugin, you have to :
#define USES_P090 // CCS811 TVOC/eCO2 Sensor
#define USES_P091 // SerSwitch
#define USES_P093 // Mitsubishi Heat Pump
#define USES_P094 // CUL Reader
#endif
@@ -971,7 +972,7 @@ To create/register a plugin, you have to :
/******************************************************************************\
* Libraries dependencies *****************************************************
\******************************************************************************/
#if defined(USES_P049) || defined(USES_P052) || defined(USES_P053) || defined(USES_P056) || defined(USES_P071) || defined(USES_P075) || defined(USES_P082) || defined(USES_P087)
#if defined(USES_P049) || defined(USES_P052) || defined(USES_P053) || defined(USES_P056) || defined(USES_P071) || defined(USES_P075) || defined(USES_P082) || defined(USES_P087) || defined(USES_P094)
// At least one plugin uses serial.
#else
// No plugin uses serial, so make sure software serial is not included.
+449
View File
@@ -0,0 +1,449 @@
#include "P094_data_struct.h"
#ifdef USES_P094
P094_data_struct::P094_data_struct() : easySerial(nullptr) {}
P094_data_struct::~P094_data_struct() {
reset();
}
void P094_data_struct::reset() {
if (easySerial != nullptr) {
delete easySerial;
easySerial = nullptr;
}
}
bool P094_data_struct::init(const int16_t serial_rx, const int16_t serial_tx, unsigned long baudrate) {
if ((serial_rx < 0) && (serial_tx < 0)) {
return false;
}
reset();
easySerial = new ESPeasySerial(serial_rx, serial_tx);
if (isInitialized()) {
easySerial->begin(baudrate);
return true;
}
return false;
}
void P094_data_struct::post_init() {
for (uint8_t i = 0; i < P094_FILTER_VALUE_Type_NR_ELEMENTS; ++i) {
valueType_used[i] = false;
}
for (uint8_t i = 0; i < P094_NR_FILTERS; ++i) {
size_t lines_baseindex = P094_Get_filter_base_index(i);
int index = _lines[lines_baseindex].toInt();
int tmp_filter_comp = _lines[lines_baseindex + 2].toInt();
const bool filter_string_notempty = _lines[lines_baseindex + 3].length() > 0;
const bool valid_index = index >= 0 && index < P094_FILTER_VALUE_Type_NR_ELEMENTS;
const bool valid_filter_comp = tmp_filter_comp >= 0 && tmp_filter_comp < P094_FILTER_COMP_NR_ELEMENTS;
valueType_index[i] = P094_not_used;
if (valid_index && valid_filter_comp && filter_string_notempty) {
valueType_used[index] = true;
valueType_index[i] = static_cast<P094_Filter_Value_Type>(index);
filter_comp[i] = static_cast<P094_Filter_Comp>(tmp_filter_comp);
}
}
}
bool P094_data_struct::isInitialized() const {
return easySerial != nullptr;
}
void P094_data_struct::sendString(const String& data) {
if (isInitialized()) {
if (data.length() > 0) {
setDisableFilterWindowTimer();
easySerial->write(data.c_str());
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log = F("Proxy: Sending: ");
log += data;
addLog(LOG_LEVEL_INFO, log);
}
}
}
}
bool P094_data_struct::loop() {
if (!isInitialized()) {
return false;
}
bool fullSentenceReceived = false;
if (easySerial != nullptr) {
int available = easySerial->available();
unsigned long timeout = millis() + 10;
while (available > 0 && !fullSentenceReceived) {
// Look for end marker
char c = easySerial->read();
--available;
if (available == 0) {
if (!timeOutReached(timeout)) {
available = easySerial->available();
}
delay(0);
}
switch (c) {
case 13:
{
const size_t length = sentence_part.length();
bool valid = length > 0;
for (size_t i = 0; i < length && valid; ++i) {
if ((sentence_part[i] > 127) || (sentence_part[i] < 32)) {
sentence_part = "";
++sentences_received_error;
valid = false;
}
}
if (valid) {
fullSentenceReceived = true;
}
break;
}
case 10:
// Ignore LF
break;
default:
sentence_part += c;
break;
}
if (max_length_reached()) { fullSentenceReceived = true; }
}
}
if (fullSentenceReceived) {
++sentences_received;
length_last_received = sentence_part.length();
}
return fullSentenceReceived;
}
void P094_data_struct::getSentence(String& string) {
string = sentence_part;
sentence_part = "";
}
void P094_data_struct::getSentencesReceived(uint32_t& succes, uint32_t& error, uint32_t& length_last) const {
succes = sentences_received;
error = sentences_received_error;
length_last = length_last_received;
}
void P094_data_struct::setMaxLength(uint16_t maxlenght) {
max_length = maxlenght;
}
void P094_data_struct::setLine(byte varNr, const String& line) {
if (varNr < P94_Nlines) {
_lines[varNr] = line;
}
}
uint32_t P094_data_struct::getFilterOffWindowTime() const {
return _lines[P094_FILTER_OFF_WINDOW_POS].toInt();
}
P094_Match_Type P094_data_struct::getMatchType() const {
return static_cast<P094_Match_Type>(_lines[P094_MATCH_TYPE_POS].toInt());
}
bool P094_data_struct::invertMatch() const {
switch (getMatchType()) {
case P094_Regular_Match:
break;
case P094_Regular_Match_inverted:
return true;
case P094_Filter_Disabled:
break;
}
return false;
}
bool P094_data_struct::filterUsed(uint8_t lineNr) const
{
if (valueType_index[lineNr] == P094_Filter_Value_Type::P094_not_used) { return false; }
uint8_t varNr = P094_Get_filter_base_index(lineNr);
return _lines[varNr + 3].length() > 0;
}
String P094_data_struct::getFilter(uint8_t lineNr, P094_Filter_Value_Type& filterValueType, uint32_t& optional,
P094_Filter_Comp& comparator) const
{
uint8_t varNr = P094_Get_filter_base_index(lineNr);
filterValueType = P094_Filter_Value_Type::P094_not_used;
if (varNr >= P94_Nlines) { return ""; }
optional = _lines[varNr + 1].toInt();
filterValueType = valueType_index[lineNr];
comparator = filter_comp[lineNr];
// filterValueType = static_cast<P094_Filter_Value_Type>(_lines[varNr].toInt());
// comparator = static_cast<P094_Filter_Comp>(_lines[varNr + 2].toInt());
return _lines[varNr + 3];
}
void P094_data_struct::setDisableFilterWindowTimer() {
if (getFilterOffWindowTime() == 0) {
disable_filter_window = 0;
}
else {
disable_filter_window = millis() + getFilterOffWindowTime();
}
}
bool P094_data_struct::disableFilterWindowActive() const {
if (disable_filter_window != 0) {
if (!timeOutReached(disable_filter_window)) {
// We're still in the window where filtering is disabled
return true;
}
}
return false;
}
bool P094_data_struct::parsePacket(String& received) const {
size_t strlength = received.length();
if (strlength == 0) {
return false;
}
if (getMatchType() == P094_Filter_Disabled) {
return true;
}
bool match_result = false;
// FIXME TD-er: For now added '$' to test with GPS.
if ((received[0] == 'b') || (received[0] == '$')) {
// Received a data packet in CUL format.
if (strlength < 21) {
return false;
}
// Decoded packet
unsigned long packet_header[P094_FILTER_VALUE_Type_NR_ELEMENTS];
packet_header[P094_packet_length] = hexToUL(received, 1, 2);
packet_header[P094_unknown1] = hexToUL(received, 3, 2);
packet_header[P094_manufacturer] = hexToUL(received, 5, 4);
packet_header[P094_serial_number] = hexToUL(received, 9, 8);
packet_header[P094_unknown2] = hexToUL(received, 17, 2);
packet_header[P094_meter_type] = hexToUL(received, 19, 2);
// FIXME TD-er: Is this also correct?
packet_header[P094_rssi] = hexToUL(received, strlength - 4, 4);
// FIXME TD-er: Is this correct?
// match_result = packet_length == (strlength - 21) / 2;
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log;
log.reserve(128);
log = F("CUL Reader: ");
log += F(" length: ");
log += packet_header[P094_packet_length];
log += F(" (header: ");
log += strlength - (packet_header[P094_packet_length] * 2);
log += F(") manu: ");
log += formatToHex_decimal(packet_header[P094_manufacturer]);
log += F(" serial: ");
log += formatToHex_decimal(packet_header[P094_serial_number]);
log += F(" mType: ");
log += formatToHex_decimal(packet_header[P094_meter_type]);
log += F(" RSSI: ");
log += formatToHex_decimal(packet_header[P094_rssi]);
addLog(LOG_LEVEL_INFO, log);
}
bool filter_matches[P094_NR_FILTERS];
for (unsigned int f = 0; f < P094_NR_FILTERS; ++f) {
filter_matches[f] = false;
}
// Do not check for "not used" (0)
for (unsigned int i = 1; i < P094_FILTER_VALUE_Type_NR_ELEMENTS; ++i) {
if (valueType_used[i]) {
for (unsigned int f = 0; f < P094_NR_FILTERS; ++f) {
if (valueType_index[f] == i) {
// Have a matching filter
uint32_t optional;
P094_Filter_Value_Type filterValueType;
P094_Filter_Comp comparator;
bool match = false;
String inputString;
String valueString;
if (i == P094_Filter_Value_Type::P094_position) {
valueString = getFilter(f, filterValueType, optional, comparator);
if (received.length() >= (optional + valueString.length())) {
// received string is long enough to fit the expression.
inputString = received.substring(optional, optional + valueString.length());
match = inputString.equalsIgnoreCase(valueString);
}
} else {
unsigned long value = hexToUL(getFilter(f, filterValueType, optional, comparator));
match = (value == packet_header[i]);
inputString = formatToHex_decimal(packet_header[i]);
valueString = formatToHex_decimal(value);
}
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log;
log.reserve(64);
log = F("CUL Reader: ");
log += P094_FilterValueType_toString(valueType_index[f]);
log += F(": in:");
log += inputString;
log += ' ';
log += P094_FilterComp_toString(comparator);
log += ' ';
log += valueString;
switch (comparator) {
case P094_Filter_Comp::P094_Equal_OR:
case P094_Filter_Comp::P094_Equal_MUST:
if (match) { log += F(" expected MATCH"); }
break;
case P094_Filter_Comp::P094_NotEqual_OR:
case P094_Filter_Comp::P094_NotEqual_MUST:
if (!match) { log += F(" expected NO MATCH"); }
break;
}
addLog(LOG_LEVEL_INFO, log);
}
switch (comparator) {
case P094_Filter_Comp::P094_Equal_OR:
if (match) { filter_matches[f] = true; }
break;
case P094_Filter_Comp::P094_NotEqual_OR:
if (!match) { filter_matches[f] = true; }
break;
case P094_Filter_Comp::P094_Equal_MUST:
if (!match) { return false; }
case P094_Filter_Comp::P094_NotEqual_MUST:
if (match) { return false; }
}
}
}
}
}
// Now we have to check if all rows per filter line in filter_matches[f] are true or not used.
int nrMatches = 0;
int nrNotUsed = 0;
for (unsigned int f = 0; !match_result && f < P094_NR_FILTERS; ++f) {
if (f % P094_AND_FILTER_BLOCK == 0) {
if ((nrMatches > 0) && ((nrMatches + nrNotUsed) == P094_AND_FILTER_BLOCK)) {
match_result = true;
}
nrMatches = 0;
nrNotUsed = 0;
}
if (filter_matches[f]) {
++nrMatches;
} else {
if (!filterUsed(f)) {
++nrNotUsed;
}
}
}
} else {
switch (received[0]) {
case 'C': // CMODE
case 'S': // SMODE
case 'T': // TMODE
case 'O': // OFF
case 'V': // Version info
// FIXME TD-er: Must test the result of the other possible answers.
match_result = true;
break;
}
}
return match_result;
}
String P094_data_struct::MatchType_toString(P094_Match_Type matchType) {
switch (matchType)
{
case P094_Match_Type::P094_Regular_Match: return F("Regular Match");
case P094_Match_Type::P094_Regular_Match_inverted: return F("Regular Match inverted");
case P094_Match_Type::P094_Filter_Disabled: return F("Filter Disabled");
}
return "";
}
String P094_data_struct::P094_FilterValueType_toString(P094_Filter_Value_Type valueType)
{
switch (valueType) {
case P094_Filter_Value_Type::P094_not_used: return F("---");
case P094_Filter_Value_Type::P094_packet_length: return F("Packet Length");
case P094_Filter_Value_Type::P094_unknown1: return F("unknown1");
case P094_Filter_Value_Type::P094_manufacturer: return F("Manufacturer");
case P094_Filter_Value_Type::P094_serial_number: return F("Serial Number");
case P094_Filter_Value_Type::P094_unknown2: return F("unknown2");
case P094_Filter_Value_Type::P094_meter_type: return F("Meter Type");
case P094_Filter_Value_Type::P094_rssi: return F("RSSI");
case P094_Filter_Value_Type::P094_position: return F("Position");
// default: break;
}
return F("unknown");
}
String P094_data_struct::P094_FilterComp_toString(P094_Filter_Comp comparator)
{
switch (comparator) {
case P094_Filter_Comp::P094_Equal_OR: return F("==");
case P094_Filter_Comp::P094_NotEqual_OR: return F("!=");
case P094_Filter_Comp::P094_Equal_MUST: return F("== (must)");
case P094_Filter_Comp::P094_NotEqual_MUST: return F("!= (must)");
}
return "";
}
bool P094_data_struct::max_length_reached() const {
if (max_length == 0) { return false; }
return sentence_part.length() >= max_length;
}
size_t P094_data_struct::P094_Get_filter_base_index(size_t filterLine) {
return filterLine * P094_ITEMS_PER_FILTER + P094_FIRST_FILTER_POS;
}
#endif // USES_P094
+138
View File
@@ -0,0 +1,138 @@
#ifndef PLUGINSTRUCTS_P094_DATA_STRUCT_H
#define PLUGINSTRUCTS_P094_DATA_STRUCT_H
#include <ESPeasySerial.h>
#include "../../_Plugin_Helper.h"
#ifdef USES_P094
# include <Regexp.h>
# define P094_REGEX_POS 0
# define P094_NR_CHAR_USE_POS 1
# define P094_FILTER_OFF_WINDOW_POS 2
# define P094_MATCH_TYPE_POS 3
# define P094_FIRST_FILTER_POS 10
# define P094_ITEMS_PER_FILTER 4
# define P094_AND_FILTER_BLOCK 3
# define P094_NR_FILTERS (7 * P094_AND_FILTER_BLOCK)
# define P94_Nlines (P094_FIRST_FILTER_POS + (P094_ITEMS_PER_FILTER * (P094_NR_FILTERS)))
# define P94_Nchars 128
# define P94_MAX_CAPTURE_INDEX 32
enum P094_Match_Type {
P094_Regular_Match = 0,
P094_Regular_Match_inverted = 1,
P094_Filter_Disabled = 2
};
# define P094_Match_Type_NR_ELEMENTS 3
enum P094_Filter_Value_Type {
P094_not_used = 0,
P094_packet_length = 1,
P094_unknown1 = 2,
P094_manufacturer = 3,
P094_serial_number = 4,
P094_unknown2 = 5,
P094_meter_type = 6,
P094_rssi = 7,
P094_position = 8
};
# define P094_FILTER_VALUE_Type_NR_ELEMENTS 9
enum P094_Filter_Comp {
P094_Equal_OR = 0,
P094_NotEqual_OR = 1,
P094_Equal_MUST = 2,
P094_NotEqual_MUST = 3
};
# define P094_FILTER_COMP_NR_ELEMENTS 4
struct P094_data_struct : public PluginTaskData_base {
public:
P094_data_struct();
~P094_data_struct();
void reset();
bool init(const int16_t serial_rx,
const int16_t serial_tx,
unsigned long baudrate);
void post_init();
bool isInitialized() const;
void sendString(const String& data);
bool loop();
void getSentence(String& string);
void getSentencesReceived(uint32_t& succes,
uint32_t& error,
uint32_t& length_last) const;
void setMaxLength(uint16_t maxlenght);
void setLine(byte varNr,
const String& line);
uint32_t getFilterOffWindowTime() const;
P094_Match_Type getMatchType() const;
bool invertMatch() const;
bool filterUsed(uint8_t lineNr) const;
String getFilter(uint8_t lineNr,
P094_Filter_Value_Type& capture,
uint32_t & optional,
P094_Filter_Comp & comparator) const;
void setDisableFilterWindowTimer();
bool disableFilterWindowActive() const;
bool parsePacket(String& received) const;
static String MatchType_toString(P094_Match_Type matchType);
static String P094_FilterValueType_toString(P094_Filter_Value_Type valueType);
static String P094_FilterComp_toString(P094_Filter_Comp comparator);
// Made public so we don't have to copy the values when loading/saving.
String _lines[P94_Nlines];
static size_t P094_Get_filter_base_index(size_t filterLine);
private:
bool max_length_reached() const;
ESPeasySerial *easySerial = nullptr;
String sentence_part;
uint16_t max_length = 550;
uint32_t sentences_received = 0;
uint32_t sentences_received_error = 0;
uint32_t length_last_received = 0;
unsigned long disable_filter_window = 0;
bool valueType_used[P094_FILTER_VALUE_Type_NR_ELEMENTS];
P094_Filter_Value_Type valueType_index[P094_NR_FILTERS];
P094_Filter_Comp filter_comp[P094_NR_FILTERS];
};
#endif // USES_P094
#endif // PLUGINSTRUCTS_P094_DATA_STRUCT_H