mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
Merge branch 'mega' into UweKaditz
This commit is contained in:
@@ -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
|
||||
@@ -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'
|
||||
|
||||
"
|
||||
|
||||
@@ -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,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:: `.`
|
||||
@@ -37,6 +37,8 @@ else:
|
||||
"USES_P085", # AcuDC24x
|
||||
"USES_P087", # Serial Proxy
|
||||
|
||||
"USES_P094", # CUL Reader
|
||||
|
||||
"USES_C016", # Cache Controller
|
||||
"USES_C018", # TTN/RN2483
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 ↷") : F("hour ↶"));
|
||||
@@ -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
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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++)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user