mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-09-12 09:36:59 +00:00
367 lines
14 KiB
Arduino
367 lines
14 KiB
Arduino
#include "_Plugin_Helper.h"
|
|
|
|
#ifdef USES_P043
|
|
|
|
// #######################################################################################################
|
|
// #################################### Plugin 043: Clock Output #########################################
|
|
// #######################################################################################################
|
|
|
|
/** Changelog:
|
|
* 2023-12-28 tonhuisman: Exclude some code that won't work for PLUGIN_BUILD_MINIMAL_OTA builds
|
|
* 2023-12-19 tonhuisman: Fix Value input On/Off only to behave exactly like when using a GPIO, except for changing the GPIO state
|
|
* 2023-12-16 tonhuisman: Add support for _GET_CONFIG_VALUE function, with [Clock#GetTimeX] and [Clock#GetValueX], where X is
|
|
* in range 1..Nr. of Day,Time fields (PLUGIN_EXTRACONFIGVAR_MAX = 16)
|
|
* Renamed setting PLUGIN_043_MAX_SETTINGS to P043_MAX_SETTINGS (to avoid confusion)
|
|
* 2023-12-14 tonhuisman: Fix 'Simplified' mode to behave like GPIO mode, so state Off = 0 and On = 1
|
|
* Add support for config command: config,task,<taskName>,SetTime,<timeIndex>,<timeString>[],<value>]
|
|
* Use convention of accepting '$' for '%' in <timeString> value, to prevent todays values to be used
|
|
* 2023-12-12 tonhuisman: Add option to choose simplified Off/On input instead of full numeric input for non-GPIO configuration
|
|
* 2023-12-11 tonhuisman: Put Value X input on same line as Day,Time X inputs, just like the On/Off combobox.
|
|
* Code optimization, calculating the current time to compare to only once.
|
|
* 2023-12-10 tonhuisman: Change input layout for non-LIMIT_BUILD_SIZE builds, to select a day and a time string in separate
|
|
* inputs.
|
|
* Add setting for number of Day,Time settings (range 1..16), default is 8.
|
|
* 2023-12-07 tonhuisman: Add support for %sunrise[+/-offsetHMS]% and %sunset[+/-offsetHMS]% format in constants
|
|
* also supports long offsets up to 32767 seconds using S suffix in offset
|
|
* 2023-12-06 tonhuisman: Add changelog
|
|
*/
|
|
|
|
/** Supported command:
|
|
* config,task,<taskName>,SetTime,<timeIndex>,<timeString>[],<value>]
|
|
* <timeIndex>: Range 1..number of Day,Time fields
|
|
* <timeString>: As entered in the UI: Mon,12:34, can also be quoted: "Mon,12:34". Day name has to be 3 letters
|
|
* To enter %Sunrise% or %Sunset-1h% etc. use $Sunrise$ or $Sunset-1h$ to prevent todays values to be used
|
|
* ($ for % is a convention introduced in P036)
|
|
* <value>: (Optional) Use 0 or 1 for GPIO configuration or when 'Value input On/Off only' is enabled
|
|
* For non-GPIO and 'Value input On/Off only' is off, then value 0 won't cause an event to be generated!
|
|
*/
|
|
|
|
/** Supported values:
|
|
* GetTimeX: Get the time string for Day,Time line X
|
|
* GetValueX: Get the configured value for Day,Time line X.
|
|
* With a configured GPIO or 'Value input On/Off only' enabled, the value will be converted to Off=0, On=1
|
|
* NB: X is in range 1..Nr. of Day,Time fields
|
|
*/
|
|
|
|
# include "src/Helpers/StringGenerator_Web.h"
|
|
|
|
# define PLUGIN_043
|
|
# define PLUGIN_ID_043 43
|
|
# define PLUGIN_NAME_043 "Output - Clock"
|
|
# define PLUGIN_VALUENAME_043 "Output"
|
|
|
|
# define P043_SIMPLE_VALUE PCONFIG(6)
|
|
# define P043_MAX_SETTINGS PCONFIG(7)
|
|
# define P043_DEFAULT_MAX 8
|
|
# define P043_SENSOR_TYPE_INDEX 2
|
|
# define P043_NR_OUTPUT_VALUES getValueCountFromSensorType(static_cast<Sensor_VType>(PCONFIG(P043_SENSOR_TYPE_INDEX)))
|
|
|
|
|
|
boolean Plugin_043(uint8_t function, struct EventStruct *event, String& string)
|
|
{
|
|
# ifndef LIMIT_BUILD_SIZE
|
|
const String weekDays = F("AllSunMonTueWedThuFriSatWrkWkd");
|
|
# endif // ifndef LIMIT_BUILD_SIZE
|
|
boolean success = false;
|
|
|
|
switch (function)
|
|
{
|
|
case PLUGIN_DEVICE_ADD:
|
|
{
|
|
Device[++deviceCount].Number = PLUGIN_ID_043;
|
|
Device[deviceCount].Type = DEVICE_TYPE_SINGLE;
|
|
Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_SWITCH;
|
|
Device[deviceCount].Ports = 0;
|
|
Device[deviceCount].PullUpOption = false;
|
|
Device[deviceCount].InverseLogicOption = false;
|
|
Device[deviceCount].FormulaOption = false;
|
|
Device[deviceCount].ValueCount = 2;
|
|
Device[deviceCount].SendDataOption = true;
|
|
Device[deviceCount].OutputDataType = Output_Data_type_t::Simple;
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_GET_DEVICENAME:
|
|
{
|
|
string = F(PLUGIN_NAME_043);
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_GET_DEVICEVALUENAMES:
|
|
{
|
|
ExtraTaskSettings.populateDeviceValueNamesSeq(F("Output"), P043_NR_OUTPUT_VALUES, 2, false);
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_SET_DEFAULTS:
|
|
{
|
|
P043_MAX_SETTINGS = P043_DEFAULT_MAX;
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_GET_DEVICEVALUECOUNT:
|
|
{
|
|
event->Par1 = P043_NR_OUTPUT_VALUES;
|
|
success = true;
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_GET_DEVICEVTYPE:
|
|
{
|
|
event->sensorType = static_cast<Sensor_VType>(PCONFIG(P043_SENSOR_TYPE_INDEX));
|
|
event->idx = P043_SENSOR_TYPE_INDEX;
|
|
success = true;
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_GET_DEVICEGPIONAMES:
|
|
{
|
|
event->String1 = formatGpioName_output(F("Clock Event"));
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_WEBFORM_LOAD:
|
|
{
|
|
if (P043_MAX_SETTINGS == 0) { P043_MAX_SETTINGS = P043_DEFAULT_MAX; }
|
|
addFormNumericBox(F("Nr. of Day,Time fields"), F("vcount"), P043_MAX_SETTINGS, 1, PLUGIN_EXTRACONFIGVAR_MAX);
|
|
addUnit(concat(F("1.."), PLUGIN_EXTRACONFIGVAR_MAX));
|
|
|
|
addFormCheckBox(F("Value input On/Off only"), F("simpl"), P043_SIMPLE_VALUE == 1);
|
|
# ifndef LIMIT_BUILD_SIZE
|
|
addFormNote(F("Page will be updated after Submit"));
|
|
# endif // ifndef LIMIT_BUILD_SIZE
|
|
|
|
const __FlashStringHelper *options[] = {
|
|
F(""),
|
|
F("Off"),
|
|
F("On"),
|
|
};
|
|
constexpr int optionsCount = NR_ELEMENTS(options);
|
|
|
|
# ifndef LIMIT_BUILD_SIZE
|
|
const unsigned int daysCount = weekDays.length() / 3;
|
|
String days[daysCount];
|
|
|
|
for (unsigned int n = 0; n < weekDays.length() / 3u; ++n) {
|
|
days[n] = weekDays.substring(n * 3, n * 3 + 3);
|
|
}
|
|
|
|
datalistStart(F("timepatternlist"));
|
|
datalistAddValue(F("00:00"));
|
|
datalistAddValue(F("%sunrise%"));
|
|
datalistAddValue(F("%sunset%"));
|
|
datalistFinish();
|
|
# endif // ifndef LIMIT_BUILD_SIZE
|
|
|
|
for (int x = 0; x < P043_MAX_SETTINGS; x++)
|
|
{
|
|
const String timeStr = timeLong2String(Cache.getTaskDevicePluginConfigLong(event->TaskIndex, x));
|
|
# ifndef LIMIT_BUILD_SIZE
|
|
addRowLabel(concat(F("Day,Time "), x + 1));
|
|
int thisDay = weekDays.indexOf(timeStr.substring(0, 3));
|
|
|
|
if (thisDay > 0) { thisDay /= 3; }
|
|
addSelector(concat(F("day"), x), daysCount, days, nullptr, nullptr, thisDay, false, true, F(""));
|
|
addHtml(',');
|
|
addTextBox(concat(F("clock"), x),
|
|
parseString(timeStr, 2), 32
|
|
, false, false, EMPTY_STRING, F("")
|
|
# if FEATURE_TOOLTIPS
|
|
, EMPTY_STRING
|
|
# endif // if FEATURE_TOOLTIPS
|
|
, F("timepatternlist"));
|
|
# else // ifndef LIMIT_BUILD_SIZE
|
|
addFormTextBox(concat(F("Day,Time "), x + 1),
|
|
concat(F("clock"), x),
|
|
timeStr, 32);
|
|
# endif // ifndef LIMIT_BUILD_SIZE
|
|
|
|
if (validGpio(CONFIG_PIN1) || (P043_SIMPLE_VALUE == 1)) {
|
|
addHtml(' ');
|
|
const uint8_t choice = Cache.getTaskDevicePluginConfig(event->TaskIndex, x);
|
|
addSelector(concat(F("state"), x), optionsCount, options, nullptr, nullptr, choice);
|
|
}
|
|
else {
|
|
addHtml(strformat(F("Value %d:"), x + 1));
|
|
addNumericBox(concat(F("state"), x),
|
|
Cache.getTaskDevicePluginConfig(event->TaskIndex, x), INT_MIN, INT_MAX);
|
|
}
|
|
}
|
|
success = true;
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_WEBFORM_SAVE:
|
|
{
|
|
P043_MAX_SETTINGS = getFormItemInt(F("vcount"));
|
|
P043_SIMPLE_VALUE = isFormItemChecked(F("simpl")) ? 1 : 0;
|
|
|
|
for (int x = 0; x < P043_MAX_SETTINGS; x++)
|
|
{
|
|
String plugin1;
|
|
# ifndef LIMIT_BUILD_SIZE
|
|
const int day = getFormItemInt(concat(F("day"), x));
|
|
plugin1 = strformat(F("%s,%s"), weekDays.substring(day * 3, day * 3 + 3).c_str(), webArg(concat(F("clock"), x)).c_str());
|
|
# else // ifndef LIMIT_BUILD_SIZE
|
|
plugin1 = webArg(concat(F("clock"), x));
|
|
# endif // ifndef LIMIT_BUILD_SIZE
|
|
ExtraTaskSettings.TaskDevicePluginConfigLong[x] = string2TimeLong(plugin1);
|
|
const String plugin2 = webArg(concat(F("state"), x));
|
|
ExtraTaskSettings.TaskDevicePluginConfig[x] = plugin2.toInt();
|
|
}
|
|
success = true;
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_INIT:
|
|
{
|
|
success = true;
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_CLOCK_IN:
|
|
{
|
|
if (P043_MAX_SETTINGS == 0) { P043_MAX_SETTINGS = P043_DEFAULT_MAX; }
|
|
|
|
unsigned long clockEvent = (unsigned long)node_time.minute() % 10
|
|
| (unsigned long)(node_time.minute() / 10) << 4
|
|
| (unsigned long)(node_time.hour() % 10) << 8
|
|
| (unsigned long)(node_time.hour() / 10) << 12
|
|
| (unsigned long)node_time.weekday() << 16;
|
|
|
|
for (uint8_t x = 0; x < P043_MAX_SETTINGS; x++)
|
|
{
|
|
unsigned long clockSet = Cache.getTaskDevicePluginConfigLong(event->TaskIndex, x);
|
|
|
|
# ifndef PLUGIN_BUILD_MINIMAL_OTA
|
|
|
|
if (bitRead(clockSet, 28) || bitRead(clockSet, 29)) { // sunrise or sunset string, apply todays values
|
|
String specialTime = timeLong2String(clockSet);
|
|
|
|
parseSystemVariables(specialTime, false); // Parse systemvariables only, to reduce processing
|
|
clockSet = string2TimeLong(specialTime);
|
|
}
|
|
# endif // ifndef PLUGIN_BUILD_MINIMAL_OTA
|
|
|
|
if (matchClockEvent(clockEvent, clockSet))
|
|
{
|
|
uint8_t state = Cache.getTaskDevicePluginConfig(event->TaskIndex, x);
|
|
|
|
if (state != 0)
|
|
{
|
|
const bool hasGpio = validGpio(CONFIG_PIN1);
|
|
|
|
if (hasGpio || (P043_SIMPLE_VALUE == 1)) { // if GPIO or Yes/No selection is specified, use the old behavior
|
|
state--;
|
|
|
|
if (hasGpio) {
|
|
pinMode(CONFIG_PIN1, OUTPUT);
|
|
digitalWrite(CONFIG_PIN1, state);
|
|
}
|
|
UserVar.setFloat(event->TaskIndex, 0, state);
|
|
}
|
|
else {
|
|
UserVar.setFloat(event->TaskIndex, 0, x + 1);
|
|
UserVar.setFloat(event->TaskIndex, 1, state);
|
|
}
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
|
addLog(LOG_LEVEL_INFO, strformat(F("TCLK : State %d"), state));
|
|
}
|
|
sendData(event);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_SET_CONFIG:
|
|
{
|
|
const String cmd = parseString(string, 1);
|
|
|
|
// command: config,task,<taskname>,SetTime,<timeIndex>,<timeString>,<value>
|
|
if (equals(cmd, F("settime"))) {
|
|
String para = parseString(string, 2);
|
|
int32_t timeIndex = 0;
|
|
|
|
if (validIntFromString(para, timeIndex) && (timeIndex > 0) && (timeIndex <= P043_MAX_SETTINGS)) {
|
|
para = parseString(string, 3);
|
|
String para4 = parseString(string, 4);
|
|
const String para5 = parseString(string, 5);
|
|
int32_t value = INT_MIN;
|
|
|
|
if ((para.length() == 3) && !para4.isEmpty() && !para5.isEmpty()) {
|
|
// handle timeString without quotes: All,12:34,<value> instead of "All,12:34",<value>
|
|
para += ','; // (most compact code...)
|
|
para += para4;
|
|
para4 = para5;
|
|
}
|
|
|
|
# ifndef BUILD_NO_DEBUG
|
|
addLog(LOG_LEVEL_DEBUG, concat(F("P043: Time received "), para));
|
|
# endif // ifndef BUILD_NO_DEBUG
|
|
|
|
para.replace('$', '%'); // Allow %Sunrise%/%Sunset% by using $ instead of %
|
|
|
|
LoadTaskSettings(event->TaskIndex); // Not preloaded
|
|
|
|
ExtraTaskSettings.TaskDevicePluginConfigLong[timeIndex - 1] = string2TimeLong(para);
|
|
|
|
if (validIntFromString(para4, value)) { // Value is optional
|
|
if (validGpio(CONFIG_PIN1) || (P043_SIMPLE_VALUE == 1)) { value++; } // Off is stored as 1, On is stored as 2 for GPIO action
|
|
|
|
ExtraTaskSettings.TaskDevicePluginConfig[timeIndex - 1] = value;
|
|
}
|
|
|
|
Cache.updateExtraTaskSettingsCache();
|
|
SaveTaskSettings(event->TaskIndex); // Unfortunately we have to save the settings here, or they will get lost
|
|
// Using too often will wear out the flash memory quickly!
|
|
|
|
# ifndef BUILD_NO_DEBUG
|
|
addLog(LOG_LEVEL_DEBUG, strformat(F("P043: Time received %s, value %d, stored %s, long: %d"), para.c_str(), value,
|
|
timeLong2String(ExtraTaskSettings.TaskDevicePluginConfigLong[timeIndex - 1]).c_str(),
|
|
string2TimeLong(para)));
|
|
# endif // ifndef BUILD_NO_DEBUG
|
|
success = true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_GET_CONFIG_VALUE:
|
|
{
|
|
# define P043_GETTIME_LENGTH 7u // Length of 'gettime'
|
|
# define P043_GETVALUE_LENGTH 8u // Length of 'getvalue'
|
|
const String cmd = parseString(string, 1);
|
|
unsigned int idx = 0u;
|
|
int32_t timeIndex = -1;
|
|
|
|
if (cmd.startsWith(F("gettime"))) {
|
|
idx = P043_GETTIME_LENGTH;
|
|
} else
|
|
if (cmd.startsWith(F("getvalue"))) {
|
|
idx = P043_GETVALUE_LENGTH;
|
|
}
|
|
|
|
if ((idx > 0) && validIntFromString(cmd.substring(idx), timeIndex) &&
|
|
(timeIndex > 0) && (timeIndex <= P043_MAX_SETTINGS)) {
|
|
LoadTaskSettings(event->TaskIndex); // Not preloaded
|
|
|
|
if (idx == P043_GETTIME_LENGTH) { // gettime
|
|
string = timeLong2String(ExtraTaskSettings.TaskDevicePluginConfigLong[timeIndex - 1]);
|
|
} else { // getvalue
|
|
const int16_t offset = (validGpio(CONFIG_PIN1) || (P043_SIMPLE_VALUE == 1)) ? 1 : 0;
|
|
string = ExtraTaskSettings.TaskDevicePluginConfig[timeIndex - 1] - offset;
|
|
}
|
|
success = true;
|
|
}
|
|
# undef P043_GETTIME_LENGTH // No longer needed
|
|
# undef P043_GETVALUE_LENGTH
|
|
break;
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
#endif // USES_P043
|