Browse Source

system: use direct status updates instead of broker

Get rid of status & config brokers, register status callbacks directly with the module.
dev
Maxim Prokhorov 3 years ago
parent
commit
78b4007f01
15 changed files with 259 additions and 252 deletions
  1. +111
    -69
      code/espurna/alexa.cpp
  2. +12
    -29
      code/espurna/domoticz.cpp
  3. +6
    -4
      code/espurna/influxdb.cpp
  4. +5
    -11
      code/espurna/led.cpp
  5. +4
    -25
      code/espurna/light.cpp
  6. +50
    -55
      code/espurna/relay.cpp
  7. +2
    -2
      code/espurna/relay.h
  8. +0
    -2
      code/espurna/rpc.cpp
  9. +0
    -4
      code/espurna/rpc.h
  10. +25
    -17
      code/espurna/rpnrules.cpp
  11. +0
    -2
      code/espurna/settings.cpp
  12. +0
    -3
      code/espurna/settings.h
  13. +5
    -11
      code/espurna/thingspeak.cpp
  14. +38
    -17
      code/espurna/utils.cpp
  15. +1
    -1
      code/espurna/utils.h

+ 111
- 69
code/espurna/alexa.cpp View File

@ -13,7 +13,6 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include <queue> #include <queue>
#include "api.h" #include "api.h"
#include "broker.h"
#include "light.h" #include "light.h"
#include "mqtt.h" #include "mqtt.h"
#include "relay.h" #include "relay.h"
@ -24,16 +23,40 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include <fauxmoESP.h> #include <fauxmoESP.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
struct alexa_queue_element_t {
unsigned char device_id;
bool state;
unsigned char value;
namespace {
struct AlexaEvent {
AlexaEvent() = delete;
AlexaEvent(unsigned char id, bool state, unsigned char value) :
_id(id),
_state(state),
_value(value)
{}
unsigned char id() const {
return _id;
}
unsigned char value() const {
return _value;
}
bool state() const {
return _state;
}
private:
unsigned char _id;
bool _state;
unsigned char _value;
}; };
static std::queue<alexa_queue_element_t> _alexa_queue;
std::queue<AlexaEvent> _alexa_events;
fauxmoESP _alexa; fauxmoESP _alexa;
} // namespace
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// ALEXA // ALEXA
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -62,25 +85,27 @@ void _alexaConfigure() {
} }
#endif #endif
void _alexaBrokerCallback(const String& topic, unsigned char id, unsigned int value) {
// Only process status messages for switches and channels
if (!topic.equals(MQTT_TOPIC_CHANNEL)
&& !topic.equals(MQTT_TOPIC_RELAY)) {
return;
}
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
if (topic.equals(MQTT_TOPIC_CHANNEL)) {
_alexa.setState(id + 1, value > 0, value);
}
void _alexaUpdateLights() {
_alexa.setState(static_cast<unsigned char>(0u), lightState(), lightState() ? 255u : 0u);
if (topic.equals(MQTT_TOPIC_RELAY)) {
if (id > 0) return;
_alexa.setState(id, value, value > 0 ? 255 : 0);
auto channels = lightChannels();
for (decltype(channels) channel = 0; channel < channels; ++channel) {
auto value = lightChannel(channel);
_alexa.setState(channel + 1, value > 0, value);
} }
}
#endif
#if RELAY_SUPPORT
void _alexaUpdateRelay(size_t id, bool status) {
_alexa.setState(id, status, status ? 255 : 0);
} }
#endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool alexaEnabled() { bool alexaEnabled() {
@ -91,67 +116,83 @@ void alexaLoop() {
_alexa.handle(); _alexa.handle();
while (!_alexa_queue.empty()) {
while (!_alexa_events.empty()) {
auto& event = _alexa_events.front();
DEBUG_MSG_P(PSTR("[ALEXA] Device #%hhu state=#%s value=%hhu\n"),
event.id(), event.state() ? 't' : 'f', event.value());
alexa_queue_element_t element = _alexa_queue.front();
DEBUG_MSG_P(PSTR("[ALEXA] Device #%u state: %s value: %d\n"), element.device_id, element.state ? "ON" : "OFF", element.value);
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
if (0 == element.device_id) {
lightState(element.state);
} else {
lightState(element.device_id - 1, element.state);
lightChannel(element.device_id - 1, element.value);
lightUpdate();
}
#else
relayStatus(element.device_id, element.state);
#endif
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
if (0 == event.id()) {
lightState(event.state());
} else {
lightState(event.id() - 1, event.state());
lightChannel(event.id() - 1, event.value());
lightUpdate();
}
#else
relayStatus(event.id(), event.state());
#endif
_alexa_queue.pop();
_alexa_events.pop();
} }
} }
void alexaSetup() {
constexpr bool _alexaCreateServer() {
return !WEB_SUPPORT;
}
constexpr const char* _alexaHostname() {
return ALEXA_HOSTNAME;
}
void _alexaSettingsMigrate(int version) {
if (version && (version < 3)) {
moveSetting("fauxmoEnabled", "alexaEnabled");
}
}
void alexaSetup() {
// Backwards compatibility // Backwards compatibility
moveSetting("fauxmoEnabled", "alexaEnabled");
_alexaSettingsMigrate(migrateVersion());
// Basic fauxmoESP configuration // Basic fauxmoESP configuration
_alexa.createServer(!WEB_SUPPORT);
_alexa.createServer(_alexaCreateServer());
_alexa.setPort(80); _alexa.setPort(80);
// Use custom alexa hostname if defined, device hostname otherwise // Use custom alexa hostname if defined, device hostname otherwise
String hostname = getSetting("alexaName", ALEXA_HOSTNAME);
if (hostname.length() == 0) {
hostname = getSetting("hostname");
String hostname = getSetting("alexaName", _alexaHostname());
if (!hostname.length()) {
hostname = getSetting("hostname", getIdentifier());
} }
// Lights
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
// Global switch
_alexa.addDevice(hostname.c_str());
// For each channel
for (unsigned char i = 1; i <= lightChannels(); i++) {
_alexa.addDevice((hostname + " " + i).c_str());
}
// Relays
#else
auto deviceName = [&](size_t index) {
auto name = hostname;
name += ' ';
name += index;
return name;
};
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
// 1st is the global state, the rest are mapped to channel values
_alexa.addDevice(hostname.c_str());
for (size_t channel = 1; channel <= lightChannels(); ++channel) {
_alexa.addDevice(deviceName(channel).c_str());
}
unsigned int relays = relayCount();
if (relays == 1) {
_alexa.addDevice(hostname.c_str());
} else {
for (unsigned int i=1; i<=relays; i++) {
_alexa.addDevice((hostname + " " + i).c_str());
}
// Relays are mapped 1-to-1
#elif RELAY_SUPPORT
auto relays = relayCount();
if (relays > 1) {
for (decltype(relays) id = 1; id <= relays; ++id) {
_alexa.addDevice(deviceName(id).c_str());
} }
} else {
_alexa.addDevice(hostname.c_str());
}
#endif
#endif
// Load & cache settings // Load & cache settings
_alexaConfigure(); _alexaConfigure();
@ -174,16 +215,17 @@ void alexaSetup() {
}); });
// Callback // Callback
_alexa.onSetState([&](unsigned char device_id, const char * name, bool state, unsigned char value) {
alexa_queue_element_t element;
element.device_id = device_id;
element.state = state;
element.value = value;
_alexa_queue.push(element);
_alexa.onSetState([&](unsigned char device_id, const char*, bool state, unsigned char value) {
_alexa_events.emplace(device_id, state, value);
}); });
// Register main callbacks // Register main callbacks
StatusBroker::Register(_alexaBrokerCallback);
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
lightSetReportListener(_alexaUpdateLights);
#else
relaySetStatusChange(_alexaUpdateRelay);
#endif
espurnaRegisterReload(_alexaConfigure); espurnaRegisterReload(_alexaConfigure);
espurnaRegisterLoop(alexaLoop); espurnaRegisterLoop(alexaLoop);


+ 12
- 29
code/espurna/domoticz.cpp View File

@ -10,7 +10,6 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#if DOMOTICZ_SUPPORT #if DOMOTICZ_SUPPORT
#include "broker.h"
#include "light.h" #include "light.h"
#include "mqtt.h" #include "mqtt.h"
#include "relay.h" #include "relay.h"
@ -173,43 +172,26 @@ void _domoticzMqtt(unsigned int type, const char * topic, char * payload) {
} }
};
void _domoticzRelayConfigure(size_t size) {
for (size_t n = 0; n < size; ++n) {
_dcz_relay_state[n] = relayStatus(n);
}
} }
void _domoticzConfigure() { void _domoticzConfigure() {
const bool enabled = getSetting("dczEnabled", 1 == DOMOTICZ_ENABLED); const bool enabled = getSetting("dczEnabled", 1 == DOMOTICZ_ENABLED);
if (enabled != _dcz_enabled) _domoticzMqttSubscribe(enabled); if (enabled != _dcz_enabled) _domoticzMqttSubscribe(enabled);
#if RELAY_SUPPORT
_domoticzRelayConfigure(relayCount());
#endif
#if RELAY_SUPPORT
for (size_t id = 0; id < relayCount(); ++id) {
_dcz_relay_state[id] = relayStatus(id);
}
#endif
_dcz_enabled = enabled; _dcz_enabled = enabled;
} }
void _domoticzConfigCallback(const String& key, const String& value) {
if (key.equals("relayDummy")) {
_domoticzRelayConfigure(value.toInt());
return;
}
}
void _domoticzBrokerCallback(const String& topic, unsigned char id, unsigned int value) {
// Only process status messages for switches
if (!topic.equals(MQTT_TOPIC_RELAY)) {
return;
void _domoticzRelayCallback(size_t id, bool status) {
if (_domoticzStatus(id) != status) {
_dcz_relay_state[id] = status;
domoticzSendRelay(id, status);
} }
if (_domoticzStatus(id) == value) return;
_dcz_relay_state[id] = value;
domoticzSendRelay(id, value);
} }
#if SENSOR_SUPPORT #if SENSOR_SUPPORT
@ -330,8 +312,9 @@ void domoticzSetup() {
.onKeyCheck(_domoticzWebSocketOnKeyCheck); .onKeyCheck(_domoticzWebSocketOnKeyCheck);
#endif #endif
StatusBroker::Register(_domoticzBrokerCallback);
ConfigBroker::Register(_domoticzConfigCallback);
#if RELAY_SUPPORT
relaySetStatusChange(_domoticzRelayCallback);
#endif
// Callbacks // Callbacks
mqttRegister(_domoticzMqtt); mqttRegister(_domoticzMqtt);


+ 6
- 4
code/espurna/influxdb.cpp View File

@ -161,8 +161,8 @@ void _idbBrokerSensor(const String& topic, unsigned char id, double, const char*
idbSend(topic.c_str(), id, value); idbSend(topic.c_str(), id, value);
} }
void _idbBrokerStatus(const String& topic, unsigned char id, unsigned int value) {
idbSend(topic.c_str(), id, String(int(value)).c_str());
void _idbSendStatus(size_t id, bool status) {
idbSend(MQTT_TOPIC_RELAY, id, status ? "1" : "0"); // "status" ?
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -219,7 +219,7 @@ void _idbFlush() {
_idb_client->payload = ""; _idb_client->payload = "";
for (auto& pair : _idb_client->values) { for (auto& pair : _idb_client->values) {
if (!isNumber(pair.second.c_str())) {
if (!isNumber(pair.second)) {
String quoted; String quoted;
quoted.reserve(pair.second.length() + 2); quoted.reserve(pair.second.length() + 2);
quoted += '"'; quoted += '"';
@ -292,7 +292,9 @@ void idbSetup() {
.onKeyCheck(_idbWebSocketOnKeyCheck); .onKeyCheck(_idbWebSocketOnKeyCheck);
#endif #endif
StatusBroker::Register(_idbBrokerStatus);
#if RELAY_SUPPORT
relaySetStatusChange(_idbSendStatus);
#endif
#if SENSOR_SUPPORT #if SENSOR_SUPPORT
SensorReportBroker::Register(_idbBrokerSensor); SensorReportBroker::Register(_idbBrokerSensor);


+ 5
- 11
code/espurna/led.cpp View File

@ -12,7 +12,6 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include <algorithm> #include <algorithm>
#include "broker.h"
#include "mqtt.h" #include "mqtt.h"
#include "relay.h" #include "relay.h"
#include "rpc.h" #include "rpc.h"
@ -232,15 +231,6 @@ void _ledWebSocketOnConnected(JsonObject& root) {
#endif #endif
void _ledBrokerCallback(const String& topic, unsigned char, unsigned int) {
// Only process status messages for switches
if (topic.equals(MQTT_TOPIC_RELAY)) {
ledUpdate(true);
}
}
#if MQTT_SUPPORT #if MQTT_SUPPORT
void _ledMQTTCallback(unsigned int type, const char * topic, const char * payload) { void _ledMQTTCallback(unsigned int type, const char * topic, const char * payload) {
@ -497,7 +487,11 @@ void ledSetup() {
.onKeyCheck(_ledWebSocketOnKeyCheck); .onKeyCheck(_ledWebSocketOnKeyCheck);
#endif #endif
StatusBroker::Register(_ledBrokerCallback);
#if RELAY_SUPPORT
relaySetStatusNotify([](size_t, bool) {
ledUpdate(true);
});
#endif
DEBUG_MSG_P(PSTR("[LED] Number of leds: %d\n"), _leds.size()); DEBUG_MSG_P(PSTR("[LED] Number of leds: %d\n"), _leds.size());


+ 4
- 25
code/espurna/light.cpp View File

@ -12,7 +12,6 @@ Copyright (C) 2019-2021 by Maxim Prokhorov <prokhorov dot max at outlook dot com
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
#include "api.h" #include "api.h"
#include "broker.h"
#include "mqtt.h" #include "mqtt.h"
#include "relay.h" #include "relay.h"
#include "rpc.h" #include "rpc.h"
@ -286,7 +285,7 @@ Ticker _light_save_ticker;
unsigned long _light_report_delay = LIGHT_REPORT_DELAY; unsigned long _light_report_delay = LIGHT_REPORT_DELAY;
Ticker _light_report_ticker; Ticker _light_report_ticker;
LightReportListener _light_report;
std::forward_list<LightReportListener> _light_report;
bool _light_has_controls = false; bool _light_has_controls = false;
bool _light_has_color = false; bool _light_has_color = false;
@ -1553,20 +1552,6 @@ void lightMQTTGroup() {
#endif #endif
// -----------------------------------------------------------------------------
// Broker
// -----------------------------------------------------------------------------
#if BROKER_SUPPORT
void lightBroker() {
for (unsigned int id = 0; id < _light_channels.size(); ++id) {
StatusBroker::Publish(MQTT_TOPIC_CHANNEL, id, _light_channels[id].value);
}
}
#endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// API // API
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -2037,7 +2022,7 @@ void lightHs(long hue, long saturation) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void lightSetReportListener(LightReportListener func) { void lightSetReportListener(LightReportListener func) {
_light_report = func;
_light_report.push_front(func);
} }
void _lightReport(int report) { void _lightReport(int report) {
@ -2057,14 +2042,8 @@ void _lightReport(int report) {
} }
#endif #endif
#if BROKER_SUPPORT
if (report & Light::Report::Broker) {
lightBroker();
}
#endif
if (_light_report) {
_light_report();
for (auto& report : _light_report) {
report();
} }
} }


+ 50
- 55
code/espurna/relay.cpp View File

@ -12,16 +12,17 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include <Ticker.h> #include <Ticker.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <vector>
#include <functional>
#include <bitset> #include <bitset>
#include <cstring>
#include <functional>
#include <vector>
#include "api.h" #include "api.h"
#include "broker.h"
#include "mqtt.h" #include "mqtt.h"
#include "rpc.h" #include "rpc.h"
#include "rtcmem.h" #include "rtcmem.h"
#include "settings.h" #include "settings.h"
#include "terminal.h"
#include "storage_eeprom.h" #include "storage_eeprom.h"
#include "utils.h" #include "utils.h"
#include "ws.h" #include "ws.h"
@ -286,8 +287,8 @@ bool _relay_sync_locked = false;
Ticker _relay_save_timer; Ticker _relay_save_timer;
Ticker _relay_sync_timer; Ticker _relay_sync_timer;
RelayStatusCallback _relay_status_notify { nullptr };
RelayStatusCallback _relay_status_change { nullptr };
std::forward_list<RelayStatusCallback> _relay_status_notify;
std::forward_list<RelayStatusCallback> _relay_status_change;
#if WEB_SUPPORT #if WEB_SUPPORT
@ -332,11 +333,11 @@ void RelayProviderBase::notify(bool) {
// Direct status notifications // Direct status notifications
void relaySetStatusNotify(RelayStatusCallback callback) { void relaySetStatusNotify(RelayStatusCallback callback) {
_relay_status_notify = callback;
_relay_status_notify.push_front(callback);
} }
void relaySetStatusChange(RelayStatusCallback callback) { void relaySetStatusChange(RelayStatusCallback callback) {
_relay_status_change = callback;
_relay_status_change.push_front(callback);
} }
// No-op provider, available for purely virtual relays that are controlled only via API // No-op provider, available for purely virtual relays that are controlled only via API
@ -418,7 +419,7 @@ struct GpioProvider : public RelayProviderBase {
} }
private: private:
unsigned char _id { RELAY_NONE };
unsigned char _id { RelaysMax };
RelayType _type { RelayType::Normal }; RelayType _type { RelayType::Normal };
std::unique_ptr<BasePin> _pin; std::unique_ptr<BasePin> _pin;
std::unique_ptr<BasePin> _reset_pin; std::unique_ptr<BasePin> _reset_pin;
@ -620,16 +621,16 @@ bool _relayTryParseIdFromPath(const String& endpoint, unsigned char& relayID) {
return _relayTryParseId(p, relayID); return _relayTryParseId(p, relayID);
} }
void _relayHandleStatus(unsigned char relayID, PayloadStatus status) {
void _relayHandleStatus(unsigned char id, PayloadStatus status) {
switch (status) { switch (status) {
case PayloadStatus::Off: case PayloadStatus::Off:
relayStatus(relayID, false);
relayStatus(id, false);
break; break;
case PayloadStatus::On: case PayloadStatus::On:
relayStatus(relayID, true);
relayStatus(id, true);
break; break;
case PayloadStatus::Toggle: case PayloadStatus::Toggle:
relayToggle(relayID);
relayToggle(id);
break; break;
case PayloadStatus::Unknown: case PayloadStatus::Unknown:
break; break;
@ -832,7 +833,7 @@ void relayPulse(unsigned char id) {
} }
if ((mode == RelayPulse::On) != relay.current_status) { if ((mode == RelayPulse::On) != relay.current_status) {
DEBUG_MSG_P(PSTR("[RELAY] Scheduling relay #%d back in %lums (pulse)\n"), id, ms);
DEBUG_MSG_P(PSTR("[RELAY] Scheduling relay #%u back in %lums (pulse)\n"), id, ms);
relay.pulseTicker->once_ms(ms, relayToggle, id); relay.pulseTicker->once_ms(ms, relayToggle, id);
// Reconfigure after dynamic pulse // Reconfigure after dynamic pulse
relay.pulse = getSetting({"relayPulse", id}, _relayPulseMode(id)); relay.pulse = getSetting({"relayPulse", id}, _relayPulseMode(id));
@ -845,11 +846,12 @@ void relayPulse(unsigned char id) {
bool relayStatus(unsigned char id, bool status, bool report, bool group_report) { bool relayStatus(unsigned char id, bool status, bool report, bool group_report) {
if (id == RELAY_NONE) return false;
if (id >= _relays.size()) return false;
if ((id >= RelaysMax) || (id >= _relays.size())) {
return false;
}
if (!_relayStatusLock(id, status)) { if (!_relayStatusLock(id, status)) {
DEBUG_MSG_P(PSTR("[RELAY] #%d is locked to %s\n"), id, _relays[id].current_status ? "ON" : "OFF");
DEBUG_MSG_P(PSTR("[RELAY] #%u is locked to %s\n"), id, _relays[id].current_status ? "ON" : "OFF");
_relays[id].report = true; _relays[id].report = true;
_relays[id].group_report = true; _relays[id].group_report = true;
return false; return false;
@ -860,7 +862,7 @@ bool relayStatus(unsigned char id, bool status, bool report, bool group_report)
if (_relays[id].current_status == status) { if (_relays[id].current_status == status) {
if (_relays[id].target_status != status) { if (_relays[id].target_status != status) {
DEBUG_MSG_P(PSTR("[RELAY] #%d scheduled change cancelled\n"), id);
DEBUG_MSG_P(PSTR("[RELAY] #%u scheduled change cancelled\n"), id);
_relays[id].target_status = status; _relays[id].target_status = status;
_relays[id].report = false; _relays[id].report = false;
_relays[id].group_report = false; _relays[id].group_report = false;
@ -869,8 +871,8 @@ bool relayStatus(unsigned char id, bool status, bool report, bool group_report)
} }
_relays[id].provider->notify(status); _relays[id].provider->notify(status);
if (_relay_status_notify) {
_relay_status_notify(id, status);
for (auto& notify : _relay_status_notify) {
notify(id, status);
} }
// Update the pulse counter if the relay is already in the non-normal state (#454) // Update the pulse counter if the relay is already in the non-normal state (#454)
@ -911,7 +913,7 @@ bool relayStatus(unsigned char id, bool status, bool report, bool group_report)
relaySync(id); relaySync(id);
DEBUG_MSG_P(PSTR("[RELAY] #%d scheduled %s in %u ms\n"),
DEBUG_MSG_P(PSTR("[RELAY] #%u scheduled %s in %u ms\n"),
id, status ? "ON" : "OFF", _relays[id].change_delay id, status ? "ON" : "OFF", _relays[id].change_delay
); );
@ -1407,16 +1409,17 @@ const String& relayPayloadToggle() {
const char* relayPayload(PayloadStatus status) { const char* relayPayload(PayloadStatus status) {
switch (status) { switch (status) {
case PayloadStatus::Off:
return _relay_rpc_payload_off.c_str();
case PayloadStatus::On:
return _relay_rpc_payload_on.c_str();
case PayloadStatus::Toggle:
return _relay_rpc_payload_toggle.c_str();
case PayloadStatus::Unknown:
default:
return "";
case PayloadStatus::Off:
return _relay_rpc_payload_off.c_str();
case PayloadStatus::On:
return _relay_rpc_payload_on.c_str();
case PayloadStatus::Toggle:
return _relay_rpc_payload_toggle.c_str();
case PayloadStatus::Unknown:
break;
} }
return "";
} }
#endif // MQTT_SUPPORT || API_SUPPORT #endif // MQTT_SUPPORT || API_SUPPORT
@ -1480,6 +1483,7 @@ private:
String _value; String _value;
RelayMqttTopicMode _mode; RelayMqttTopicMode _mode;
}; };
struct RelayCustomTopic { struct RelayCustomTopic {
RelayCustomTopic() = delete; RelayCustomTopic() = delete;
RelayCustomTopic(const RelayCustomTopic&) = delete; RelayCustomTopic(const RelayCustomTopic&) = delete;
@ -1836,9 +1840,9 @@ void _relayInitCommands() {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void _relayReport(unsigned char id [[gnu::unused]], bool status [[gnu::unused]]) { void _relayReport(unsigned char id [[gnu::unused]], bool status [[gnu::unused]]) {
#if BROKER_SUPPORT
StatusBroker::Publish(MQTT_TOPIC_RELAY, id, status);
#endif
for (auto& change : _relay_status_change) {
change(id, status);
}
#if MQTT_SUPPORT #if MQTT_SUPPORT
_relayMqttReport(id); _relayMqttReport(id);
#endif #endif
@ -1873,15 +1877,11 @@ void _relayProcess(bool mode) {
_relays[id].change_delay = 0; _relays[id].change_delay = 0;
changed = true; changed = true;
DEBUG_MSG_P(PSTR("[RELAY] #%d set to %s\n"), id, target ? "ON" : "OFF");
DEBUG_MSG_P(PSTR("[RELAY] #%u set to %s\n"), id, target ? "ON" : "OFF");
// Call the provider to perform the action // Call the provider to perform the action
_relays[id].current_status = target; _relays[id].current_status = target;
_relays[id].provider->change(target); _relays[id].provider->change(target);
if (_relay_status_change) {
_relay_status_change(id, target);
}
_relayReport(id, target); _relayReport(id, target);
if (!_relayRecursive) { if (!_relayRecursive) {
@ -1926,11 +1926,14 @@ void _relayLoop() {
// Dummy relays for virtual light switches (hardware-less), Sonoff Dual, Sonoff RF Bridge and Tuya // Dummy relays for virtual light switches (hardware-less), Sonoff Dual, Sonoff RF Bridge and Tuya
void relaySetupDummy(size_t size, bool reconfigure) { void relaySetupDummy(size_t size, bool reconfigure) {
if (size == _relayDummy) return;
if (size == _relayDummy) {
return;
}
const size_t new_size = ((_relays.size() - _relayDummy) + size); const size_t new_size = ((_relays.size() - _relayDummy) + size);
if (new_size > RelaysMax) return;
if (new_size > RelaysMax) {
return;
}
_relayDummy = size; _relayDummy = size;
_relays.resize(new_size); _relays.resize(new_size);
@ -1938,11 +1941,6 @@ void relaySetupDummy(size_t size, bool reconfigure) {
if (reconfigure) { if (reconfigure) {
_relayConfigure(); _relayConfigure();
} }
#if BROKER_SUPPORT
ConfigBroker::Publish("relayDummy", String(int(size)));
#endif
} }
constexpr size_t _relayAdhocPins() { constexpr size_t _relayAdhocPins() {
@ -1976,8 +1974,8 @@ constexpr size_t _relayAdhocPins() {
struct RelayGpioProviderCfg { struct RelayGpioProviderCfg {
GpioBase* base; GpioBase* base;
unsigned char main;
unsigned char reset;
uint8_t main;
uint8_t reset;
}; };
RelayGpioProviderCfg _relayGpioProviderCfg(unsigned char index) { RelayGpioProviderCfg _relayGpioProviderCfg(unsigned char index) {
@ -1987,8 +1985,6 @@ RelayGpioProviderCfg _relayGpioProviderCfg(unsigned char index) {
getSetting({"relayResetGPIO", index}, _relayResetPin(index))}; getSetting({"relayResetGPIO", index}, _relayResetPin(index))};
} }
using GpioCheck = bool(*)(unsigned char);
std::unique_ptr<GpioProvider> _relayGpioProvider(unsigned char index, RelayType type) { std::unique_ptr<GpioProvider> _relayGpioProvider(unsigned char index, RelayType type) {
auto cfg = _relayGpioProviderCfg(index); auto cfg = _relayGpioProviderCfg(index);
if (!cfg.base) { if (!cfg.base) {
@ -1996,14 +1992,13 @@ std::unique_ptr<GpioProvider> _relayGpioProvider(unsigned char index, RelayType
} }
auto main = gpioRegister(*cfg.base, cfg.main); auto main = gpioRegister(*cfg.base, cfg.main);
if (!main) {
return nullptr;
if (main) {
auto reset = gpioRegister(*cfg.base, cfg.reset);
return std::make_unique<GpioProvider>(
index, type, std::move(main), std::move(reset));
} }
auto reset = gpioRegister(*cfg.base, cfg.reset);
return std::make_unique<GpioProvider>(
index, type, std::move(main), std::move(reset)
);
return nullptr;
} }
RelayProviderBasePtr _relaySetupProvider(unsigned char index) { RelayProviderBasePtr _relaySetupProvider(unsigned char index) {


+ 2
- 2
code/espurna/relay.h View File

@ -11,7 +11,7 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include "espurna.h" #include "espurna.h"
#include "rpc.h" #include "rpc.h"
constexpr size_t RelaysMax = 32;
constexpr size_t RelaysMax { 32ul };
enum class RelayPulse : uint8_t { enum class RelayPulse : uint8_t {
None, None,
@ -94,7 +94,7 @@ void relayPulse(unsigned char id);
void relaySync(unsigned char id); void relaySync(unsigned char id);
void relaySave(bool persist); void relaySave(bool persist);
using RelayStatusCallback = void(*)(unsigned char id, bool status);
using RelayStatusCallback = void(*)(size_t id, bool status);
using RelayProviderBasePtr = std::unique_ptr<RelayProviderBase>; using RelayProviderBasePtr = std::unique_ptr<RelayProviderBase>;
bool relayAdd(RelayProviderBasePtr&& provider); bool relayAdd(RelayProviderBasePtr&& provider);


+ 0
- 2
code/espurna/rpc.cpp View File

@ -14,8 +14,6 @@ Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
#include "system.h" #include "system.h"
#include "utils.h" #include "utils.h"
BrokerBind(StatusBroker);
bool rpcHandleAction(const String& action) { bool rpcHandleAction(const String& action) {
bool result = false; bool result = false;
if (action.equals("reboot")) { if (action.equals("reboot")) {


+ 0
- 4
code/espurna/rpc.h View File

@ -8,10 +8,6 @@ Part of MQTT and API modules
#include "espurna.h" #include "espurna.h"
#include "broker.h"
BrokerDeclare(StatusBroker, void(const String& topic, unsigned char id, unsigned int status));
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
enum class PayloadStatus { enum class PayloadStatus {


+ 25
- 17
code/espurna/rpnrules.cpp View File

@ -12,7 +12,6 @@ Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
#include <rpnlib.h> #include <rpnlib.h>
#include "broker.h"
#include "light.h" #include "light.h"
#include "mqtt.h" #include "mqtt.h"
#include "ntp.h" #include "ntp.h"
@ -147,29 +146,32 @@ void _rpnMQTTCallback(unsigned int type, const char * topic, const char * payloa
#endif // MQTT_SUPPORT #endif // MQTT_SUPPORT
void _rpnConfigure() { void _rpnConfigure() {
#if MQTT_SUPPORT
if (mqttConnected()) _rpnMQTTSubscribe();
#endif
#if MQTT_SUPPORT
if (mqttConnected()) {
_rpnMQTTSubscribe();
}
#endif
_rpn_delay = getSetting("rpnDelay", RPN_DELAY); _rpn_delay = getSetting("rpnDelay", RPN_DELAY);
} }
void _rpnBrokerCallback(const String& topic, unsigned char id, double value, const char*) {
void _rpnRelayStatus(size_t id, bool status) {
char name[32] = {0}; char name[32] = {0};
snprintf(name, sizeof(name), "%s%u", topic.c_str(), id);
if (topic == MQTT_TOPIC_RELAY) {
rpn_variable_set(_rpn_ctxt, name, rpn_value(static_cast<bool>(value)));
} else {
rpn_variable_set(_rpn_ctxt, name, rpn_value(value));
}
snprintf(name, sizeof(name), "relay%u", id);
rpn_variable_set(_rpn_ctxt, name, rpn_value(std::forward<T>(value)));
_rpn_run = true; _rpn_run = true;
} }
void _rpnBrokerStatus(const String& topic, unsigned char id, unsigned int value) {
_rpnBrokerCallback(topic, id, double(value), nullptr);
void _rpnLightStatus() {
auto channels = lightChannels();
char name[32] = {0};
for (decltype(channels) channel = 0; channel < channels; ++channel) {
snprintf(name, sizeof(name), "channel%u", channel);
rpn_variable_set(_rpn_ctxt, name, rpn_value(lightChannel(channel)));
}
_rpn_run = true;
} }
#if NTP_SUPPORT #if NTP_SUPPORT
@ -1019,7 +1021,13 @@ void rpnSetup() {
}); });
#endif #endif
StatusBroker::Register(_rpnBrokerStatus);
#if RELAY_SUPPORT
relaySetStatusChange(_rpnRelayStatus);
#endif
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
lightSetReportListener(_rpnLightStatus);
#endif
#if RFB_SUPPORT #if RFB_SUPPORT
_rpnRfbSetup(); _rpnRfbSetup();


+ 0
- 2
code/espurna/settings.cpp View File

@ -18,8 +18,6 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include <ArduinoJson.h> #include <ArduinoJson.h>
BrokerBind(ConfigBroker);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
namespace settings { namespace settings {


+ 0
- 3
code/espurna/settings.h View File

@ -17,12 +17,9 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include "broker.h"
#include "storage_eeprom.h" #include "storage_eeprom.h"
#include "settings_embedis.h" #include "settings_embedis.h"
BrokerDeclare(ConfigBroker, void(const String& key, const String& value));
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
void resetSettings(); void resetSettings();


+ 5
- 11
code/espurna/thingspeak.cpp View File

@ -12,7 +12,6 @@ Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
#include <memory> #include <memory>
#include "broker.h"
#include "mqtt.h" #include "mqtt.h"
#include "relay.h" #include "relay.h"
#include "rpc.h" #include "rpc.h"
@ -87,16 +86,9 @@ AsyncClientState _tspk_state = AsyncClientState::Disconnected;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void _tspkBrokerCallback(const String& topic, unsigned char id, unsigned int value) {
// Only process status messages for switches
if (!topic.equals(MQTT_TOPIC_RELAY)) {
return;
}
tspkEnqueueRelay(id, value > 0);
void _tspkRelayStatus(size_t id, bool status) {
tspkEnqueueRelay(id, status);
tspkFlush(); tspkFlush();
} }
#if WEB_SUPPORT #if WEB_SUPPORT
@ -464,7 +456,9 @@ void tspkSetup() {
.onKeyCheck(_tspkWebSocketOnKeyCheck); .onKeyCheck(_tspkWebSocketOnKeyCheck);
#endif #endif
StatusBroker::Register(_tspkBrokerCallback);
#if RELAY_SUPPORT
relaySetStatusChange(_tspkRelayStatus);
#endif
DEBUG_MSG_P(PSTR("[THINGSPEAK] Async %s, SSL %s\n"), DEBUG_MSG_P(PSTR("[THINGSPEAK] Async %s, SSL %s\n"),
THINGSPEAK_USE_ASYNC ? "ENABLED" : "DISABLED", THINGSPEAK_USE_ASYNC ? "ENABLED" : "DISABLED",


+ 38
- 17
code/espurna/utils.cpp View File

@ -404,25 +404,46 @@ void nice_delay(unsigned long ms) {
while (millis() - start < ms) delay(1); while (millis() - start < ms) delay(1);
} }
bool isNumber(const char * s) {
unsigned char len = strlen(s);
if (0 == len) return false;
bool decimal = false;
bool digit = false;
for (unsigned char i=0; i<len; i++) {
if (('-' == s[i]) || ('+' == s[i])) {
if (i>0) return false;
} else if (s[i] == '.') {
if (!digit) return false;
if (decimal) return false;
decimal = true;
} else if (!isdigit(s[i])) {
return false;
} else {
digit = true;
bool isNumber(const String& value) {
if (value.length()) {
const char* begin { value.c_str() };
const char* end { value.c_str() + value.length() };
bool dot { false };
bool digit { false };
const char* ptr { begin };
while (ptr != end) {
switch (*ptr) {
case '\0':
break;
case '-':
case '+':
if (ptr != begin) {
return false;
}
break;
case '.':
if (dot) {
return false;
}
dot = true;
break;
case '0' ... '9':
digit = true;
break;
case 'a' ... 'z':
case 'A' ... 'Z':
return false;
}
++ptr;
} }
return digit;
} }
return digit;
return false;
} }
// ref: lwip2 lwip_strnstr with strnlen // ref: lwip2 lwip_strnstr with strnlen


+ 1
- 1
code/espurna/utils.h View File

@ -44,7 +44,7 @@ bool sslFingerPrintChar(const char * fingerprint, char * destination);
char* ltrim(char* s); char* ltrim(char* s);
char* strnstr(const char* buffer, const char* token, size_t n); char* strnstr(const char* buffer, const char* token, size_t n);
bool isNumber(const char* s);
bool isNumber(const String&);
void nice_delay(unsigned long ms); void nice_delay(unsigned long ms);


Loading…
Cancel
Save