mirror of
https://github.com/xoseperez/espurna.git
synced 2026-03-05 07:54:18 +01:00
providers: relays, lights and buttons refactoring (#2414)
- gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
This commit is contained in:
@@ -123,6 +123,19 @@ String serialize(const debounce_event::types::PinMode& mode) {
|
||||
return result;
|
||||
}
|
||||
|
||||
template <>
|
||||
ButtonProvider convert(const String& value) {
|
||||
auto type = static_cast<ButtonProvider>(value.toInt());
|
||||
switch (type) {
|
||||
case ButtonProvider::None:
|
||||
case ButtonProvider::Gpio:
|
||||
case ButtonProvider::Analog:
|
||||
return type;
|
||||
}
|
||||
|
||||
return ButtonProvider::None;
|
||||
}
|
||||
|
||||
} // namespace settings::internal
|
||||
} // namespace settings
|
||||
|
||||
@@ -209,18 +222,15 @@ button_event_delays_t::button_event_delays_t(unsigned long debounce, unsigned lo
|
||||
lnglngclick(lnglngclick)
|
||||
{}
|
||||
|
||||
button_t::button_t(unsigned char relayID, const button_actions_t& actions, const button_event_delays_t& delays) :
|
||||
event_emitter(nullptr),
|
||||
event_delays(delays),
|
||||
actions(actions),
|
||||
relayID(relayID)
|
||||
button_t::button_t(button_actions_t&& actions_, button_event_delays_t&& delays_) :
|
||||
actions(std::move(actions_)),
|
||||
event_delays(std::move(delays_))
|
||||
{}
|
||||
|
||||
button_t::button_t(std::shared_ptr<BasePin> pin, const debounce_event::types::Config& config, unsigned char relayID, const button_actions_t& actions, const button_event_delays_t& delays) :
|
||||
event_emitter(std::make_unique<debounce_event::EventEmitter>(pin, config, delays.debounce, delays.repeat)),
|
||||
event_delays(delays),
|
||||
actions(actions),
|
||||
relayID(relayID)
|
||||
button_t::button_t(BasePinPtr&& pin, const debounce_event::types::Config& config, button_actions_t&& actions_, button_event_delays_t&& delays_) :
|
||||
event_emitter(std::make_unique<debounce_event::EventEmitter>(std::move(pin), config, delays_.debounce, delays_.repeat)),
|
||||
actions(std::move(actions_)),
|
||||
event_delays(std::move(delays_))
|
||||
{}
|
||||
|
||||
bool button_t::state() {
|
||||
@@ -327,7 +337,7 @@ void _buttonWebSocketOnConnected(JsonObject& root) {
|
||||
|
||||
// TODO: configure PIN object instead of button specifically, link PIN<->BUTTON
|
||||
button.add(getSetting({"btnProv", index}, _buttonProvider(index)));
|
||||
if (_buttons[i].getPin()) {
|
||||
if (_buttons[i].pin()) {
|
||||
button.add(getSetting({"btnGPIO", index}, _buttonPin(index)));
|
||||
const auto config = _buttonRuntimeConfig(index);
|
||||
button.add(static_cast<int>(config.mode));
|
||||
@@ -417,6 +427,41 @@ String _buttonEventString(button_event_t event) {
|
||||
return String(ptr);
|
||||
}
|
||||
|
||||
#if RELAY_SUPPORT
|
||||
|
||||
unsigned char _buttonRelaySetting(unsigned char id) {
|
||||
static std::vector<uint8_t> relays;
|
||||
|
||||
if (!relays.size()) {
|
||||
relays.reserve(_buttons.size());
|
||||
for (unsigned char button = 0; button < _buttons.size(); ++button) {
|
||||
relays.push_back(getSetting({"btnRelay", button}, _buttonRelay(button)));
|
||||
}
|
||||
}
|
||||
|
||||
return relays[id];
|
||||
}
|
||||
|
||||
void _buttonRelayAction(unsigned char id, button_action_t action) {
|
||||
auto relayId = _buttonRelaySetting(id);
|
||||
|
||||
switch (action) {
|
||||
case BUTTON_ACTION_TOGGLE:
|
||||
relayToggle(relayId);
|
||||
break;
|
||||
|
||||
case BUTTON_ACTION_ON:
|
||||
relayStatus(relayId, true);
|
||||
break;
|
||||
|
||||
case BUTTON_ACTION_OFF:
|
||||
relayStatus(relayId, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // RELAY_SUPPORT
|
||||
|
||||
void buttonEvent(unsigned char id, button_event_t event) {
|
||||
|
||||
DEBUG_MSG_P(PSTR("[BUTTON] Button #%u event %d (%s)\n"),
|
||||
@@ -441,17 +486,11 @@ void buttonEvent(unsigned char id, button_event_t event) {
|
||||
|
||||
#if RELAY_SUPPORT
|
||||
case BUTTON_ACTION_TOGGLE:
|
||||
relayToggle(button.relayID);
|
||||
break;
|
||||
|
||||
case BUTTON_ACTION_ON:
|
||||
relayStatus(button.relayID, true);
|
||||
break;
|
||||
|
||||
case BUTTON_ACTION_OFF:
|
||||
relayStatus(button.relayID, false);
|
||||
_buttonRelayAction(id, action);
|
||||
break;
|
||||
#endif // RELAY_SUPPORT == 1
|
||||
#endif
|
||||
|
||||
case BUTTON_ACTION_AP:
|
||||
if (wifiState() & WIFI_STATE_AP) {
|
||||
@@ -486,12 +525,12 @@ void buttonEvent(unsigned char id, button_event_t event) {
|
||||
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
|
||||
case BUTTON_ACTION_DIM_UP:
|
||||
lightBrightnessStep(1);
|
||||
lightUpdate(true, true);
|
||||
lightUpdate();
|
||||
break;
|
||||
|
||||
case BUTTON_ACTION_DIM_DOWN:
|
||||
lightBrightnessStep(-1);
|
||||
lightUpdate(true, true);
|
||||
lightUpdate();
|
||||
break;
|
||||
#endif // LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
|
||||
|
||||
@@ -520,69 +559,7 @@ unsigned long _buttonGetSetting(const char* key, unsigned char index, T default_
|
||||
return getSetting({key, index}, getSetting(key, default_value));
|
||||
}
|
||||
|
||||
// Sonoff Dual does not do real GPIO readings and we
|
||||
// depend on the external MCU to send us relay / button events
|
||||
// Lightfox uses the same protocol as Dual, but has slightly different actions
|
||||
// TODO: move this to a separate 'hardware' setup file?
|
||||
|
||||
void _buttonLoopSonoffDual() {
|
||||
|
||||
if (Serial.available() < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char bytes[4] = {0};
|
||||
Serial.readBytes(bytes, 4);
|
||||
if ((bytes[0] != 0xA0) && (bytes[1] != 0x04) && (bytes[3] != 0xA1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned char value [[gnu::unused]] = bytes[2];
|
||||
|
||||
#if BUTTON_PROVIDER_ITEAD_SONOFF_DUAL_SUPPORT
|
||||
|
||||
// RELAYs and BUTTONs are synchonized in the SIL F330
|
||||
// The on-board BUTTON2 should toggle RELAY0 value
|
||||
// Since we are not passing back RELAY2 value
|
||||
// (in the relayStatus method) it will only be present
|
||||
// here if it has actually been pressed
|
||||
if ((value & 4) == 4) {
|
||||
buttonEvent(2, button_event_t::Click);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise check if any of the other two BUTTONs
|
||||
// (in the header) has been pressed, but we should
|
||||
// ensure that we only toggle one of them to avoid
|
||||
// the synchronization going mad
|
||||
// This loop is generic for any PSB-04 module
|
||||
for (unsigned int i=0; i<relayCount(); i++) {
|
||||
|
||||
const bool status = (value & (1 << i)) > 0;
|
||||
|
||||
// Check if the status for that relay has changed
|
||||
if (relayStatus(i) != status) {
|
||||
buttonEvent(i, button_event_t::Click);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#elif BUTTON_PROVIDER_FOXEL_LIGHTFOX_DUAL_SUPPORT
|
||||
|
||||
DEBUG_MSG_P(PSTR("[BUTTON] [LIGHTFOX] Received buttons mask: %u\n"), value);
|
||||
|
||||
for (unsigned int i=0; i<_buttons.size(); i++) {
|
||||
if ((value & (1 << i)) > 0) {
|
||||
buttonEvent(i, button_event_t::Click);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BUTTON_PROVIDER_ITEAD_SONOFF_DUAL
|
||||
|
||||
}
|
||||
|
||||
void _buttonLoopGeneric() {
|
||||
void buttonLoop() {
|
||||
for (size_t id = 0; id < _buttons.size(); ++id) {
|
||||
auto event = _buttons[id].loop();
|
||||
if (event != button_event_t::None) {
|
||||
@@ -591,17 +568,6 @@ void _buttonLoopGeneric() {
|
||||
}
|
||||
}
|
||||
|
||||
void buttonLoop() {
|
||||
|
||||
_buttonLoopGeneric();
|
||||
|
||||
// Unconditionally call these. By default, generic loop will discard everything without the configured events emmiter
|
||||
#if BUTTON_PROVIDER_ITEAD_SONOFF_DUAL_SUPPORT || BUTTON_PROVIDER_FOXEL_LIGHTFOX_DUAL
|
||||
_buttonLoopSonoffDual();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// Resistor ladder buttons. Inspired by:
|
||||
// - https://gitter.im/tinkerman-cat/espurna?at=5f5d44c8df4af236f902e25d
|
||||
// - https://github.com/bxparks/AceButton/tree/develop/docs/resistor_ladder (especially thx @bxparks for the great documentation!)
|
||||
@@ -611,18 +577,14 @@ void buttonLoop() {
|
||||
#if BUTTON_PROVIDER_ANALOG_SUPPORT
|
||||
|
||||
class AnalogPin final : public BasePin {
|
||||
|
||||
public:
|
||||
|
||||
public:
|
||||
static constexpr int RangeFrom { 0 };
|
||||
static constexpr int RangeTo { 1023 };
|
||||
|
||||
AnalogPin() = delete;
|
||||
AnalogPin(unsigned char) = delete;
|
||||
|
||||
AnalogPin(unsigned char pin_, int expected_) :
|
||||
BasePin(pin_),
|
||||
_expected(expected_)
|
||||
explicit AnalogPin(unsigned char pin, int expected) :
|
||||
_pin(pin),
|
||||
_expected(expected)
|
||||
{
|
||||
pins.reserve(ButtonsPresetMax);
|
||||
pins.push_back(this);
|
||||
@@ -634,17 +596,26 @@ class AnalogPin final : public BasePin {
|
||||
adjustPinRanges();
|
||||
}
|
||||
|
||||
String description() const override {
|
||||
char buffer[64];
|
||||
snprintf_P(buffer, sizeof(buffer),
|
||||
PSTR("%s @ level %d (%d...%d)\n"),
|
||||
id(), _expected, _from, _to);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Notice that 'static' method vars are shared between instances
|
||||
// This way we will throttle every invocation (which should be safe to do, since we only read things through the button loop)
|
||||
int analogRead() {
|
||||
static unsigned long ts { ESP.getCycleCount() };
|
||||
static int last { ::analogRead(pin) };
|
||||
static int last { ::analogRead(_pin) };
|
||||
|
||||
// Cannot hammer analogRead() all the time:
|
||||
// https://github.com/esp8266/Arduino/issues/1634
|
||||
if (ESP.getCycleCount() - ts >= _read_interval) {
|
||||
ts = ESP.getCycleCount();
|
||||
last = ::analogRead(pin);
|
||||
last = ::analogRead(_pin);
|
||||
}
|
||||
|
||||
return last;
|
||||
@@ -665,14 +636,12 @@ class AnalogPin final : public BasePin {
|
||||
return true;
|
||||
}
|
||||
|
||||
String description() const override {
|
||||
char buffer[64] {0};
|
||||
snprintf_P(buffer, sizeof(buffer),
|
||||
PSTR("AnalogPin @ GPIO%u, expected %d (%d, %d)"),
|
||||
pin, _expected, _from, _to
|
||||
);
|
||||
unsigned char pin() const override {
|
||||
return _pin;
|
||||
}
|
||||
|
||||
return String(buffer);
|
||||
const char* id() const override {
|
||||
return "AnalogPin";
|
||||
}
|
||||
|
||||
// Simulate LOW level when the range matches and HIGH when it does not
|
||||
@@ -687,8 +656,7 @@ class AnalogPin final : public BasePin {
|
||||
void digitalWrite(int8_t val) override {
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
private:
|
||||
// ref. https://github.com/bxparks/AceButton/tree/develop/docs/resistor_ladder#level-matching-tolerance-range
|
||||
// fuzzy matching instead of directly comparing with the `_expected` level and / or specifying tolerance manually
|
||||
// for example, for pins with expected values 0, 327, 512 and 844 we match analogRead() when:
|
||||
@@ -701,6 +669,8 @@ class AnalogPin final : public BasePin {
|
||||
|
||||
unsigned long _read_interval { microsecondsToClockCycles(200u) };
|
||||
|
||||
unsigned char _pin { A0 };
|
||||
|
||||
int _expected { 0u };
|
||||
int _from { RangeFrom };
|
||||
int _to { RangeTo };
|
||||
@@ -731,174 +701,154 @@ std::vector<AnalogPin*> AnalogPin::pins;
|
||||
|
||||
#endif // BUTTON_PROVIDER_ANALOG_SUPPORT
|
||||
|
||||
std::shared_ptr<BasePin> _buttonFromProvider([[gnu::unused]] unsigned char index, int provider, unsigned char pin) {
|
||||
BasePinPtr _buttonGpioPin(unsigned char index, ButtonProvider provider) {
|
||||
BasePinPtr result;
|
||||
|
||||
auto pin = getSetting({"btnGPIO", index}, _buttonPin(index));
|
||||
|
||||
switch (provider) {
|
||||
|
||||
case BUTTON_PROVIDER_GENERIC:
|
||||
if (!gpioValid(pin)) {
|
||||
case ButtonProvider::Gpio: {
|
||||
#if BUTTON_PROVIDER_GPIO_SUPPORT
|
||||
auto* base = gpioBase(getSetting({"btnGPIOType", index}, _buttonPinType(index)));
|
||||
if (!base) {
|
||||
break;
|
||||
}
|
||||
return std::shared_ptr<BasePin>(new GpioPin(pin));
|
||||
|
||||
#if BUTTON_PROVIDER_MCP23S08_SUPPORT
|
||||
case BUTTON_PROVIDER_MCP23S08:
|
||||
if (!mcpGpioValid(pin)) {
|
||||
if (!gpioLock(*base, pin)) {
|
||||
break;
|
||||
}
|
||||
return std::shared_ptr<BasePin>(new McpGpioPin(pin));
|
||||
|
||||
result = std::move(base->pin(pin));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case ButtonProvider::Analog: {
|
||||
#if BUTTON_PROVIDER_ANALOG_SUPPORT
|
||||
case BUTTON_PROVIDER_ANALOG: {
|
||||
if (A0 != pin) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto level = getSetting({"btnLevel", index}, _buttonAnalogLevel(index));
|
||||
auto level = getSetting({"btnLevel", index}, _buttonAnalogLevel(index));
|
||||
if (!AnalogPin::checkExpectedLevel(level)) {
|
||||
break;
|
||||
}
|
||||
|
||||
return std::shared_ptr<BasePin>(new AnalogPin(pin, level));
|
||||
}
|
||||
result.reset(new AnalogPin(pin, level));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
return result;
|
||||
}
|
||||
|
||||
inline button_actions_t _buttonActions(unsigned char index) {
|
||||
button_actions_t actions {
|
||||
getSetting({"btnPress", index}, _buttonPress(index)),
|
||||
getSetting({"btnRlse", index}, _buttonRelease(index)),
|
||||
getSetting({"btnClick", index}, _buttonClick(index)),
|
||||
getSetting({"btnDclk", index}, _buttonDoubleClick(index)),
|
||||
getSetting({"btnLclk", index}, _buttonLongClick(index)),
|
||||
getSetting({"btnLLclk", index}, _buttonLongLongClick(index)),
|
||||
getSetting({"btnTclk", index}, _buttonTripleClick(index))
|
||||
};
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
// Note that we use settings without indexes as default values
|
||||
button_event_delays_t _buttonDelays(unsigned char index) {
|
||||
button_event_delays_t delays {
|
||||
_buttonGetSetting("btnDebDel", index, _buttonDebounceDelay(index)),
|
||||
_buttonGetSetting("btnRepDel", index, _buttonRepeatDelay(index)),
|
||||
_buttonGetSetting("btnLclkDel", index, _buttonLongClickDelay(index)),
|
||||
_buttonGetSetting("btnLLclkDel", index, _buttonLongLongClickDelay(index)),
|
||||
};
|
||||
|
||||
return delays;
|
||||
}
|
||||
|
||||
bool _buttonSetupProvider(unsigned char index, ButtonProvider provider) {
|
||||
bool result { false };
|
||||
|
||||
switch (provider) {
|
||||
|
||||
case ButtonProvider::Analog:
|
||||
case ButtonProvider::Gpio: {
|
||||
#if BUTTON_PROVIDER_GPIO_SUPPORT || BUTTON_PROVIDER_ANALOG_SUPPORT
|
||||
auto pin = _buttonGpioPin(index, provider);
|
||||
if (!pin) {
|
||||
break;
|
||||
}
|
||||
|
||||
_buttons.emplace_back(
|
||||
std::move(pin),
|
||||
_buttonRuntimeConfig(index),
|
||||
_buttonActions(index),
|
||||
_buttonDelays(index));
|
||||
result = true;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case ButtonProvider::None:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void buttonSetup() {
|
||||
|
||||
// Backwards compatibility
|
||||
moveSetting("btnDelay", "btnRepDel");
|
||||
|
||||
// Special hardware cases
|
||||
#if BUTTON_PROVIDER_ITEAD_SONOFF_DUAL_SUPPORT || BUTTON_PROVIDER_FOXEL_LIGHTFOX_DUAL
|
||||
{
|
||||
size_t buttons = 0;
|
||||
#if BUTTON1_RELAY != RELAY_NONE
|
||||
++buttons;
|
||||
#endif
|
||||
#if BUTTON2_RELAY != RELAY_NONE
|
||||
++buttons;
|
||||
#endif
|
||||
#if BUTTON3_RELAY != RELAY_NONE
|
||||
++buttons;
|
||||
#endif
|
||||
#if BUTTON4_RELAY != RELAY_NONE
|
||||
++buttons;
|
||||
#endif
|
||||
|
||||
_buttons.reserve(buttons);
|
||||
|
||||
// Ignore real button delays since we don't use them here
|
||||
const auto delays = button_event_delays_t();
|
||||
|
||||
for (unsigned char index = 0; index < buttons; ++index) {
|
||||
const button_actions_t actions {
|
||||
BUTTON_ACTION_NONE,
|
||||
BUTTON_ACTION_NONE,
|
||||
// The only generated event is ::Click
|
||||
getSetting({"btnClick", index}, _buttonClick(index)),
|
||||
BUTTON_ACTION_NONE,
|
||||
BUTTON_ACTION_NONE,
|
||||
BUTTON_ACTION_NONE,
|
||||
BUTTON_ACTION_NONE
|
||||
};
|
||||
_buttons.emplace_back(
|
||||
getSetting({"btnRelay", index}, _buttonRelay(index)),
|
||||
actions,
|
||||
delays
|
||||
);
|
||||
for (unsigned char index = 0; index < ButtonsMax; ++index) {
|
||||
auto provider = getSetting({"btnProv", index}, _buttonProvider(index));
|
||||
if (!_buttonSetupProvider(index, provider)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // BUTTON_PROVIDER_ITEAD_SONOFF_DUAL_SUPPORT || BUTTON_PROVIDER_FOXEL_LIGHTFOX_DUAL
|
||||
|
||||
#if BUTTON_PROVIDER_GENERIC_SUPPORT
|
||||
|
||||
// Generic GPIO input handlers
|
||||
{
|
||||
_buttons.reserve(_buttonPreconfiguredPins());
|
||||
|
||||
for (unsigned char index = _buttons.size(); index < ButtonsMax; ++index) {
|
||||
const auto provider = getSetting({"btnProv", index}, _buttonProvider(index));
|
||||
const auto pin = getSetting({"btnGPIO", index}, _buttonPin(index));
|
||||
|
||||
auto managed_pin = _buttonFromProvider(index, provider, pin);
|
||||
if (!managed_pin) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto relayID = getSetting({"btnRelay", index}, _buttonRelay(index));
|
||||
|
||||
// TODO: compatibility proxy, fetch global key before indexed
|
||||
const button_event_delays_t delays {
|
||||
_buttonGetSetting("btnDebDel", index, _buttonDebounceDelay(index)),
|
||||
_buttonGetSetting("btnRepDel", index, _buttonRepeatDelay(index)),
|
||||
_buttonGetSetting("btnLclkDel", index, _buttonLongClickDelay(index)),
|
||||
_buttonGetSetting("btnLLclkDel", index, _buttonLongLongClickDelay(index)),
|
||||
};
|
||||
|
||||
const button_actions_t actions {
|
||||
getSetting({"btnPress", index}, _buttonPress(index)),
|
||||
getSetting({"btnRlse", index}, _buttonRelease(index)),
|
||||
getSetting({"btnClick", index}, _buttonClick(index)),
|
||||
getSetting({"btnDclk", index}, _buttonDoubleClick(index)),
|
||||
getSetting({"btnLclk", index}, _buttonLongClick(index)),
|
||||
getSetting({"btnLLclk", index}, _buttonLongLongClick(index)),
|
||||
getSetting({"btnTclk", index}, _buttonTripleClick(index))
|
||||
};
|
||||
|
||||
const auto config = _buttonRuntimeConfig(index);
|
||||
|
||||
_buttons.emplace_back(
|
||||
managed_pin, config,
|
||||
relayID, actions, delays
|
||||
);
|
||||
}
|
||||
|
||||
auto count = _buttons.size();
|
||||
DEBUG_MSG_P(PSTR("[BUTTON] Number of buttons: %u\n"), count);
|
||||
if (!count) {
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if TERMINAL_SUPPORT
|
||||
if (_buttons.size()) {
|
||||
terminalRegisterCommand(F("BUTTON"), [](const terminal::CommandContext& ctx) {
|
||||
unsigned index { 0u };
|
||||
for (auto& button : _buttons) {
|
||||
ctx.output.printf("%u - ", index++);
|
||||
if (button.event_emitter) {
|
||||
auto pin = button.event_emitter->getPin();
|
||||
ctx.output.println(pin->description());
|
||||
} else {
|
||||
ctx.output.println(F("Virtual"));
|
||||
}
|
||||
terminalRegisterCommand(F("BUTTON"), [](const terminal::CommandContext& ctx) {
|
||||
unsigned index { 0u };
|
||||
for (auto& button : _buttons) {
|
||||
ctx.output.printf("%u - ", index++);
|
||||
if (button.event_emitter) {
|
||||
auto& pin = button.event_emitter->pin();
|
||||
ctx.output.println(pin->description());
|
||||
} else {
|
||||
ctx.output.println(F("Virtual"));
|
||||
}
|
||||
}
|
||||
|
||||
terminalOK(ctx);
|
||||
});
|
||||
}
|
||||
terminalOK(ctx);
|
||||
});
|
||||
#endif
|
||||
|
||||
_buttonConfigure();
|
||||
|
||||
DEBUG_MSG_P(PSTR("[BUTTON] Number of buttons: %u\n"), _buttons.size());
|
||||
|
||||
// Websocket Callbacks
|
||||
#if WEB_SUPPORT
|
||||
wsRegister()
|
||||
.onConnected(_buttonWebSocketOnVisible)
|
||||
.onVisible(_buttonWebSocketOnVisible)
|
||||
.onConnected(_buttonWebSocketOnConnected)
|
||||
.onKeyCheck(_buttonWebSocketOnKeyCheck);
|
||||
#endif
|
||||
|
||||
// Register system callbacks
|
||||
espurnaRegisterLoop(buttonLoop);
|
||||
espurnaRegisterReload(_buttonConfigure);
|
||||
|
||||
}
|
||||
|
||||
#endif // BUTTON_SUPPORT
|
||||
|
||||
Reference in New Issue
Block a user