Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

844 lines
24 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. /*
  2. RELAY MODULE
  3. Copyright (C) 2016-2018 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #include <EEPROM.h>
  6. #include <Ticker.h>
  7. #include <ArduinoJson.h>
  8. #include <vector>
  9. #include <functional>
  10. typedef struct {
  11. // Configuration variables
  12. unsigned char pin; // GPIO pin for the relay
  13. unsigned char type; // RELAY_TYPE_NORMAL, RELAY_TYPE_INVERSE or RELAY_TYPE_LATCHED
  14. unsigned char reset_pin; // GPIO to reset the relay if RELAY_TYPE_LATCHED
  15. unsigned long delay_on; // Delay to turn relay ON
  16. unsigned long delay_off; // Delay to turn relay OFF
  17. unsigned char pulse; // RELAY_PULSE_NONE, RELAY_PULSE_OFF or RELAY_PULSE_ON
  18. unsigned long pulse_ms; // Pulse length in millis
  19. // Status variables
  20. bool current_status; // Holds the current (physical) status of the relay
  21. bool target_status; // Holds the target status
  22. unsigned long fw_start; // Flood window start time
  23. unsigned char fw_count; // Number of changes within the current flood window
  24. unsigned long change_time; // Scheduled time to change
  25. bool report; // Whether to report to own topic
  26. bool group_report; // Whether to report to group topic
  27. // Helping objects
  28. Ticker pulseTicker; // Holds the pulse back timer
  29. } relay_t;
  30. std::vector<relay_t> _relays;
  31. bool _relayRecursive = false;
  32. Ticker _relaySaveTicker;
  33. // -----------------------------------------------------------------------------
  34. // RELAY PROVIDERS
  35. // -----------------------------------------------------------------------------
  36. void _relayProviderStatus(unsigned char id, bool status) {
  37. // Check relay ID
  38. if (id >= _relays.size()) return;
  39. // Store new current status
  40. _relays[id].current_status = status;
  41. #if RELAY_PROVIDER == RELAY_PROVIDER_RFBRIDGE
  42. rfbStatus(id, status);
  43. #endif
  44. #if RELAY_PROVIDER == RELAY_PROVIDER_DUAL
  45. // Calculate mask
  46. unsigned char mask=0;
  47. for (unsigned char i=0; i<_relays.size(); i++) {
  48. if (_relays[i].current_status) mask = mask + (1 << i);
  49. }
  50. // Send it to F330
  51. Serial.flush();
  52. Serial.write(0xA0);
  53. Serial.write(0x04);
  54. Serial.write(mask);
  55. Serial.write(0xA1);
  56. Serial.flush();
  57. #endif
  58. #if RELAY_PROVIDER == RELAY_PROVIDER_LIGHT
  59. // If the number of relays matches the number of light channels
  60. // assume each relay controls one channel.
  61. // If the number of relays is the number of channels plus 1
  62. // assume the first one controls all the channels and
  63. // the rest one channel each.
  64. // Otherwise every relay controls all channels.
  65. // TODO: this won't work with a mixed of dummy and real relays
  66. // but this option is not allowed atm (YANGNI)
  67. if (_relays.size() == lightChannels()) {
  68. lightState(id, status);
  69. lightState(true);
  70. } else if (_relays.size() == lightChannels() + 1) {
  71. if (id == 0) {
  72. lightState(status);
  73. } else {
  74. lightState(id-1, status);
  75. }
  76. } else {
  77. lightState(status);
  78. }
  79. lightUpdate(true, true);
  80. #endif
  81. #if RELAY_PROVIDER == RELAY_PROVIDER_RELAY
  82. if (_relays[id].type == RELAY_TYPE_NORMAL) {
  83. digitalWrite(_relays[id].pin, status);
  84. } else if (_relays[id].type == RELAY_TYPE_INVERSE) {
  85. digitalWrite(_relays[id].pin, !status);
  86. } else if (_relays[id].type == RELAY_TYPE_LATCHED) {
  87. digitalWrite(_relays[id].pin, LOW);
  88. digitalWrite(_relays[id].reset_pin, LOW);
  89. if (status) {
  90. digitalWrite(_relays[id].pin, HIGH);
  91. } else {
  92. digitalWrite(_relays[id].reset_pin, HIGH);
  93. }
  94. delay(RELAY_LATCHING_PULSE);
  95. digitalWrite(_relays[id].pin, LOW);
  96. digitalWrite(_relays[id].reset_pin, LOW);
  97. }
  98. #endif
  99. }
  100. /**
  101. * Walks the relay vector processing only those relays
  102. * that have to change to the requested mode
  103. * @bool mode Requested mode
  104. */
  105. void _relayProcess(bool mode) {
  106. unsigned int current_time = millis();
  107. for (unsigned char id = 0; id < _relays.size(); id++) {
  108. bool target = _relays[id].target_status;
  109. // Only process the relays we have to change
  110. if (target == _relays[id].current_status) continue;
  111. // Only process the relays we have change to the requested mode
  112. if (target != mode) continue;
  113. // Only process if the change_time has arrived
  114. if (current_time < _relays[id].change_time) continue;
  115. DEBUG_MSG_P(PSTR("[RELAY] #%d set to %s\n"), id, target ? "ON" : "OFF");
  116. // Call the provider to perform the action
  117. _relayProviderStatus(id, target);
  118. // Send to Broker
  119. #if BROKER_SUPPORT
  120. brokerPublish(MQTT_TOPIC_RELAY, id, target ? "1" : "0");
  121. #endif
  122. // Send MQTT
  123. #if MQTT_SUPPORT
  124. relayMQTT(id);
  125. #endif
  126. if (!_relayRecursive) {
  127. relayPulse(id);
  128. _relaySaveTicker.once_ms(RELAY_SAVE_DELAY, relaySave);
  129. #if WEB_SUPPORT
  130. wsSend(_relayWebSocketUpdate);
  131. #endif
  132. }
  133. #if DOMOTICZ_SUPPORT
  134. domoticzSendRelay(id);
  135. #endif
  136. #if INFLUXDB_SUPPORT
  137. relayInfluxDB(id);
  138. #endif
  139. #if THINGSPEAK_SUPPORT
  140. tspkEnqueueRelay(id, target);
  141. tspkFlush();
  142. #endif
  143. // Flag relay-based LEDs to update status
  144. ledUpdate(true);
  145. _relays[id].report = false;
  146. _relays[id].group_report = false;
  147. }
  148. }
  149. // -----------------------------------------------------------------------------
  150. // RELAY
  151. // -----------------------------------------------------------------------------
  152. void relayPulse(unsigned char id) {
  153. byte mode = _relays[id].pulse;
  154. if (mode == RELAY_PULSE_NONE) return;
  155. unsigned long ms = _relays[id].pulse_ms;
  156. if (ms == 0) return;
  157. bool status = relayStatus(id);
  158. bool pulseStatus = (mode == RELAY_PULSE_ON);
  159. if (pulseStatus == status) {
  160. _relays[id].pulseTicker.detach();
  161. } else {
  162. _relays[id].pulseTicker.once_ms(ms, relayToggle, id);
  163. }
  164. }
  165. bool relayStatus(unsigned char id, bool status, bool report, bool group_report) {
  166. if (id >= _relays.size()) return false;
  167. bool changed = false;
  168. if (_relays[id].current_status == status) {
  169. if (_relays[id].target_status != status) {
  170. DEBUG_MSG_P(PSTR("[RELAY] #%d scheduled change cancelled\n"), id);
  171. _relays[id].target_status = status;
  172. _relays[id].report = false;
  173. _relays[id].group_report = false;
  174. changed = true;
  175. }
  176. // For RFBridge, keep sending the message even if the status is already the required
  177. #if RELAY_PROVIDER == RELAY_PROVIDER_RFBRIDGE
  178. rfbStatus(id, status);
  179. #endif
  180. // Update the pulse counter if the relay is already in the non-normal state (#454)
  181. relayPulse(id);
  182. } else {
  183. unsigned int current_time = millis();
  184. unsigned int fw_end = _relays[id].fw_start + 1000 * RELAY_FLOOD_WINDOW;
  185. unsigned long delay = status ? _relays[id].delay_on : _relays[id].delay_off;
  186. _relays[id].fw_count++;
  187. _relays[id].change_time = current_time + delay;
  188. // If current_time is off-limits the floodWindow...
  189. if (current_time < _relays[id].fw_start || fw_end <= current_time) {
  190. // We reset the floodWindow
  191. _relays[id].fw_start = current_time;
  192. _relays[id].fw_count = 1;
  193. // If current_time is in the floodWindow and there have been too many requests...
  194. } else if (_relays[id].fw_count >= RELAY_FLOOD_CHANGES) {
  195. // We schedule the changes to the end of the floodWindow
  196. // unless it's already delayed beyond that point
  197. if (fw_end - delay > current_time) {
  198. _relays[id].change_time = fw_end;
  199. }
  200. }
  201. _relays[id].target_status = status;
  202. if (report) _relays[id].report = true;
  203. if (group_report) _relays[id].group_report = true;
  204. relaySync(id);
  205. DEBUG_MSG_P(PSTR("[RELAY] #%d scheduled %s in %u ms\n"),
  206. id, status ? "ON" : "OFF",
  207. (_relays[id].change_time - current_time));
  208. changed = true;
  209. }
  210. return changed;
  211. }
  212. bool relayStatus(unsigned char id, bool status) {
  213. return relayStatus(id, status, true, true);
  214. }
  215. bool relayStatus(unsigned char id) {
  216. // Check relay ID
  217. if (id >= _relays.size()) return false;
  218. // Get status from storage
  219. return _relays[id].current_status;
  220. }
  221. void relaySync(unsigned char id) {
  222. // No sync if none or only one relay
  223. if (_relays.size() < 2) return;
  224. // Do not go on if we are comming from a previous sync
  225. if (_relayRecursive) return;
  226. // Flag sync mode
  227. _relayRecursive = true;
  228. byte relaySync = getSetting("relaySync", RELAY_SYNC).toInt();
  229. bool status = _relays[id].target_status;
  230. // If RELAY_SYNC_SAME all relays should have the same state
  231. if (relaySync == RELAY_SYNC_SAME) {
  232. for (unsigned short i=0; i<_relays.size(); i++) {
  233. if (i != id) relayStatus(i, status);
  234. }
  235. // If NONE_OR_ONE or ONE and setting ON we should set OFF all the others
  236. } else if (status) {
  237. if (relaySync != RELAY_SYNC_ANY) {
  238. for (unsigned short i=0; i<_relays.size(); i++) {
  239. if (i != id) relayStatus(i, false);
  240. }
  241. }
  242. // If ONLY_ONE and setting OFF we should set ON the other one
  243. } else {
  244. if (relaySync == RELAY_SYNC_ONE) {
  245. unsigned char i = (id + 1) % _relays.size();
  246. relayStatus(i, true);
  247. }
  248. }
  249. // Unflag sync mode
  250. _relayRecursive = false;
  251. }
  252. void relaySave() {
  253. unsigned char bit = 1;
  254. unsigned char mask = 0;
  255. for (unsigned int i=0; i < _relays.size(); i++) {
  256. if (relayStatus(i)) mask += bit;
  257. bit += bit;
  258. }
  259. EEPROM.write(EEPROM_RELAY_STATUS, mask);
  260. DEBUG_MSG_P(PSTR("[RELAY] Saving mask: %d\n"), mask);
  261. EEPROM.commit();
  262. }
  263. void relayToggle(unsigned char id, bool report, bool group_report) {
  264. if (id >= _relays.size()) return;
  265. relayStatus(id, !relayStatus(id), report, group_report);
  266. }
  267. void relayToggle(unsigned char id) {
  268. relayToggle(id, true, true);
  269. }
  270. unsigned char relayCount() {
  271. return _relays.size();
  272. }
  273. unsigned char relayParsePayload(const char * payload) {
  274. // Payload could be "OFF", "ON", "TOGGLE"
  275. // or its number equivalents: 0, 1 or 2
  276. if (payload[0] == '0') return 0;
  277. if (payload[0] == '1') return 1;
  278. if (payload[0] == '2') return 2;
  279. // trim payload
  280. char * p = ltrim((char *)payload);
  281. // to lower
  282. unsigned int l = strlen(p);
  283. if (l>6) l=6;
  284. for (unsigned char i=0; i<l; i++) {
  285. p[i] = tolower(p[i]);
  286. }
  287. unsigned int value = 0xFF;
  288. if (strcmp(p, "off") == 0) {
  289. value = 0;
  290. } else if (strcmp(p, "on") == 0) {
  291. value = 1;
  292. } else if (strcmp(p, "toggle") == 0) {
  293. value = 2;
  294. } else if (strcmp(p, "query") == 0) {
  295. value = 3;
  296. }
  297. return value;
  298. }
  299. // BACKWARDS COMPATIBILITY
  300. void _relayBackwards() {
  301. byte relayMode = getSetting("relayMode", RELAY_BOOT_MODE).toInt();
  302. byte relayPulseMode = getSetting("relayPulseMode", RELAY_PULSE_MODE).toInt();
  303. float relayPulseTime = getSetting("relayPulseTime", RELAY_PULSE_TIME).toFloat();
  304. if (relayPulseMode == RELAY_PULSE_NONE) relayPulseTime = 0;
  305. for (unsigned int i=0; i<_relays.size(); i++) {
  306. if (!hasSetting("relayBoot", i)) setSetting("relayBoot", i, relayMode);
  307. if (!hasSetting("relayPulse", i)) setSetting("relayPulse", i, relayPulseMode);
  308. if (!hasSetting("relayTime", i)) setSetting("relayTime", i, relayPulseTime);
  309. }
  310. delSetting("relayMode");
  311. delSetting("relayPulseMode");
  312. delSetting("relayPulseTime");
  313. }
  314. void _relayBoot() {
  315. _relayRecursive = true;
  316. unsigned char bit = 1;
  317. bool trigger_save = false;
  318. // Get last statuses from EEPROM
  319. unsigned char mask = EEPROM.read(EEPROM_RELAY_STATUS);
  320. DEBUG_MSG_P(PSTR("[RELAY] Retrieving mask: %d\n"), mask);
  321. // Walk the relays
  322. bool status = false;
  323. for (unsigned int i=0; i<_relays.size(); i++) {
  324. unsigned char boot_mode = getSetting("relayBoot", i, RELAY_BOOT_MODE).toInt();
  325. DEBUG_MSG_P(PSTR("[RELAY] Relay #%d boot mode %d\n"), i, boot_mode);
  326. switch (boot_mode) {
  327. case RELAY_BOOT_SAME:
  328. status = ((mask & bit) == bit);
  329. break;
  330. case RELAY_BOOT_TOGGLE:
  331. status = ((mask & bit) != bit);
  332. mask ^= bit;
  333. trigger_save = true;
  334. break;
  335. case RELAY_BOOT_ON:
  336. status = true;
  337. break;
  338. case RELAY_BOOT_OFF:
  339. default:
  340. status = false;
  341. break;
  342. }
  343. _relays[i].current_status = !status;
  344. _relays[i].target_status = status;
  345. _relays[i].change_time = millis();
  346. bit <<= 1;
  347. }
  348. // Save if there is any relay in the RELAY_BOOT_TOGGLE mode
  349. if (trigger_save) {
  350. EEPROM.write(EEPROM_RELAY_STATUS, mask);
  351. EEPROM.commit();
  352. }
  353. _relayRecursive = false;
  354. }
  355. void _relayConfigure() {
  356. for (unsigned int i=0; i<_relays.size(); i++) {
  357. pinMode(_relays[i].pin, OUTPUT);
  358. if (_relays[i].type == RELAY_TYPE_LATCHED) pinMode(_relays[i].reset_pin, OUTPUT);
  359. _relays[i].pulse = getSetting("relayPulse", i, RELAY_PULSE_MODE).toInt();
  360. _relays[i].pulse_ms = 1000 * getSetting("relayTime", i, RELAY_PULSE_MODE).toFloat();
  361. }
  362. }
  363. //------------------------------------------------------------------------------
  364. // WEBSOCKETS
  365. //------------------------------------------------------------------------------
  366. #if WEB_SUPPORT
  367. void _relayWebSocketUpdate(JsonObject& root) {
  368. JsonArray& relay = root.createNestedArray("relayStatus");
  369. for (unsigned char i=0; i<relayCount(); i++) {
  370. relay.add(_relays[i].target_status);
  371. }
  372. }
  373. void _relayWebSocketOnStart(JsonObject& root) {
  374. if (relayCount() == 0) return;
  375. // Statuses
  376. _relayWebSocketUpdate(root);
  377. // Configuration
  378. JsonArray& config = root.createNestedArray("relayConfig");
  379. for (unsigned char i=0; i<relayCount(); i++) {
  380. JsonObject& line = config.createNestedObject();
  381. line["gpio"] = _relays[i].pin;
  382. line["type"] = _relays[i].type;
  383. line["reset"] = _relays[i].reset_pin;
  384. line["boot"] = getSetting("relayBoot", i, RELAY_BOOT_MODE).toInt();
  385. line["pulse"] = _relays[i].pulse;
  386. line["pulse_ms"] = _relays[i].pulse_ms / 1000.0;
  387. #if MQTT_SUPPORT
  388. line["group"] = getSetting("mqttGroup", i, "");
  389. line["group_inv"] = getSetting("mqttGroupInv", i, 0).toInt();
  390. #endif
  391. }
  392. if (relayCount() > 1) {
  393. root["multirelayVisible"] = 1;
  394. root["relaySync"] = getSetting("relaySync", RELAY_SYNC);
  395. }
  396. root["relayVisible"] = 1;
  397. }
  398. void _relayWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
  399. if (strcmp(action, "relay") != 0) return;
  400. if (data.containsKey("status")) {
  401. unsigned char value = relayParsePayload(data["status"]);
  402. if (value == 3) {
  403. wsSend(_relayWebSocketUpdate);
  404. } else if (value < 3) {
  405. unsigned int relayID = 0;
  406. if (data.containsKey("id")) {
  407. String value = data["id"];
  408. relayID = value.toInt();
  409. }
  410. // Action to perform
  411. if (value == 0) {
  412. relayStatus(relayID, false);
  413. } else if (value == 1) {
  414. relayStatus(relayID, true);
  415. } else if (value == 2) {
  416. relayToggle(relayID);
  417. }
  418. }
  419. }
  420. }
  421. void relaySetupWS() {
  422. wsOnSendRegister(_relayWebSocketOnStart);
  423. wsOnActionRegister(_relayWebSocketOnAction);
  424. wsOnAfterParseRegister(_relayConfigure);
  425. }
  426. #endif // WEB_SUPPORT
  427. //------------------------------------------------------------------------------
  428. // REST API
  429. //------------------------------------------------------------------------------
  430. #if WEB_SUPPORT
  431. void relaySetupAPI() {
  432. // API entry points (protected with apikey)
  433. for (unsigned int relayID=0; relayID<relayCount(); relayID++) {
  434. char key[15];
  435. snprintf_P(key, sizeof(key), PSTR("%s/%d"), MQTT_TOPIC_RELAY, relayID);
  436. apiRegister(key,
  437. [relayID](char * buffer, size_t len) {
  438. snprintf_P(buffer, len, PSTR("%d"), relayStatus(relayID) ? 1 : 0);
  439. },
  440. [relayID](const char * payload) {
  441. unsigned char value = relayParsePayload(payload);
  442. if (value == 0xFF) {
  443. DEBUG_MSG_P(PSTR("[RELAY] Wrong payload (%s)\n"), payload);
  444. return;
  445. }
  446. if (value == 0) {
  447. relayStatus(relayID, false);
  448. } else if (value == 1) {
  449. relayStatus(relayID, true);
  450. } else if (value == 2) {
  451. relayToggle(relayID);
  452. }
  453. }
  454. );
  455. }
  456. }
  457. #endif // WEB_SUPPORT
  458. //------------------------------------------------------------------------------
  459. // MQTT
  460. //------------------------------------------------------------------------------
  461. #if MQTT_SUPPORT
  462. void relayMQTT(unsigned char id) {
  463. if (id >= _relays.size()) return;
  464. // Send state topic
  465. if (_relays[id].report) {
  466. _relays[id].report = false;
  467. mqttSend(MQTT_TOPIC_RELAY, id, _relays[id].current_status ? "1" : "0");
  468. }
  469. // Check group topic
  470. if (_relays[id].group_report) {
  471. _relays[id].group_report = false;
  472. String t = getSetting("mqttGroup", id, "");
  473. if (t.length() > 0) {
  474. bool status = relayStatus(id);
  475. if (getSetting("mqttGroupInv", id, 0).toInt() == 1) status = !status;
  476. mqttSendRaw(t.c_str(), status ? "1" : "0");
  477. }
  478. }
  479. }
  480. void relayMQTT() {
  481. for (unsigned int id=0; id < _relays.size(); id++) {
  482. mqttSend(MQTT_TOPIC_RELAY, id, _relays[id].current_status ? "1" : "0");
  483. }
  484. }
  485. void relayStatusWrap(unsigned char id, unsigned char value, bool is_group_topic) {
  486. // Action to perform
  487. if (value == 0) {
  488. relayStatus(id, false, mqttForward(), !is_group_topic);
  489. } else if (value == 1) {
  490. relayStatus(id, true, mqttForward(), !is_group_topic);
  491. } else if (value == 2) {
  492. relayToggle(id, true, true);
  493. }
  494. }
  495. void relayMQTTCallback(unsigned int type, const char * topic, const char * payload) {
  496. if (type == MQTT_CONNECT_EVENT) {
  497. // Send status on connect
  498. #if not HEARTBEAT_REPORT_RELAY
  499. relayMQTT();
  500. #endif
  501. // Subscribe to own /set topic
  502. char buffer[strlen(MQTT_TOPIC_RELAY) + 3];
  503. snprintf_P(buffer, sizeof(buffer), PSTR("%s/+"), MQTT_TOPIC_RELAY);
  504. mqttSubscribe(buffer);
  505. // Subscribe to group topics
  506. for (unsigned int i=0; i < _relays.size(); i++) {
  507. String t = getSetting("mqttGroup", i, "");
  508. if (t.length() > 0) mqttSubscribeRaw(t.c_str());
  509. }
  510. }
  511. if (type == MQTT_MESSAGE_EVENT) {
  512. // Check relay topic
  513. String t = mqttMagnitude((char *) topic);
  514. if (t.startsWith(MQTT_TOPIC_RELAY)) {
  515. // Get value
  516. unsigned char value = relayParsePayload(payload);
  517. if (value == 0xFF) return;
  518. // Get relay ID
  519. unsigned int id = t.substring(strlen(MQTT_TOPIC_RELAY)+1).toInt();
  520. if (id >= relayCount()) {
  521. DEBUG_MSG_P(PSTR("[RELAY] Wrong relayID (%d)\n"), id);
  522. } else {
  523. relayStatusWrap(id, value, false);
  524. }
  525. return;
  526. }
  527. // Check group topics
  528. for (unsigned int i=0; i < _relays.size(); i++) {
  529. String t = getSetting("mqttGroup", i, "");
  530. if ((t.length() > 0) && t.equals(topic)) {
  531. unsigned char value = relayParsePayload(payload);
  532. if (value == 0xFF) return;
  533. if (value < 2) {
  534. if (getSetting("mqttGroupInv", i, 0).toInt() == 1) {
  535. value = 1 - value;
  536. }
  537. }
  538. DEBUG_MSG_P(PSTR("[RELAY] Matched group topic for relayID %d\n"), i);
  539. relayStatusWrap(i, value, true);
  540. }
  541. }
  542. }
  543. }
  544. void relaySetupMQTT() {
  545. mqttRegister(relayMQTTCallback);
  546. }
  547. #endif
  548. //------------------------------------------------------------------------------
  549. // InfluxDB
  550. //------------------------------------------------------------------------------
  551. #if INFLUXDB_SUPPORT
  552. void relayInfluxDB(unsigned char id) {
  553. if (id >= _relays.size()) return;
  554. idbSend(MQTT_TOPIC_RELAY, id, relayStatus(id) ? "1" : "0");
  555. }
  556. #endif
  557. //------------------------------------------------------------------------------
  558. // Settings
  559. //------------------------------------------------------------------------------
  560. #if TERMINAL_SUPPORT
  561. void _relayInitCommands() {
  562. settingsRegisterCommand(F("RELAY"), [](Embedis* e) {
  563. if (e->argc < 2) {
  564. DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
  565. }
  566. int id = String(e->argv[1]).toInt();
  567. if (e->argc > 2) {
  568. int value = String(e->argv[2]).toInt();
  569. if (value == 2) {
  570. relayToggle(id);
  571. } else {
  572. relayStatus(id, value == 1);
  573. }
  574. }
  575. DEBUG_MSG_P(PSTR("Status: %s\n"), relayStatus(id) ? "true" : "false");
  576. DEBUG_MSG_P(PSTR("+OK\n"));
  577. });
  578. }
  579. #endif // TERMINAL_SUPPORT
  580. //------------------------------------------------------------------------------
  581. // Setup
  582. //------------------------------------------------------------------------------
  583. void _relayLoop() {
  584. _relayProcess(false);
  585. _relayProcess(true);
  586. }
  587. void relaySetup() {
  588. // Dummy relays for AI Light, Magic Home LED Controller, H801,
  589. // Sonoff Dual and Sonoff RF Bridge
  590. #ifdef DUMMY_RELAY_COUNT
  591. for (unsigned char i=0; i < DUMMY_RELAY_COUNT; i++) {
  592. _relays.push_back((relay_t) {0, RELAY_TYPE_NORMAL});
  593. }
  594. #else
  595. #ifdef RELAY1_PIN
  596. _relays.push_back((relay_t) { RELAY1_PIN, RELAY1_TYPE, RELAY1_RESET_PIN, RELAY1_DELAY_ON, RELAY1_DELAY_OFF });
  597. #endif
  598. #ifdef RELAY2_PIN
  599. _relays.push_back((relay_t) { RELAY2_PIN, RELAY2_TYPE, RELAY2_RESET_PIN, RELAY2_DELAY_ON, RELAY2_DELAY_OFF });
  600. #endif
  601. #ifdef RELAY3_PIN
  602. _relays.push_back((relay_t) { RELAY3_PIN, RELAY3_TYPE, RELAY3_RESET_PIN, RELAY3_DELAY_ON, RELAY3_DELAY_OFF });
  603. #endif
  604. #ifdef RELAY4_PIN
  605. _relays.push_back((relay_t) { RELAY4_PIN, RELAY4_TYPE, RELAY4_RESET_PIN, RELAY4_DELAY_ON, RELAY4_DELAY_OFF });
  606. #endif
  607. #ifdef RELAY5_PIN
  608. _relays.push_back((relay_t) { RELAY5_PIN, RELAY5_TYPE, RELAY5_RESET_PIN, RELAY5_DELAY_ON, RELAY5_DELAY_OFF });
  609. #endif
  610. #ifdef RELAY6_PIN
  611. _relays.push_back((relay_t) { RELAY6_PIN, RELAY6_TYPE, RELAY6_RESET_PIN, RELAY6_DELAY_ON, RELAY6_DELAY_OFF });
  612. #endif
  613. #ifdef RELAY7_PIN
  614. _relays.push_back((relay_t) { RELAY7_PIN, RELAY7_TYPE, RELAY7_RESET_PIN, RELAY7_DELAY_ON, RELAY7_DELAY_OFF });
  615. #endif
  616. #ifdef RELAY8_PIN
  617. _relays.push_back((relay_t) { RELAY8_PIN, RELAY8_TYPE, RELAY8_RESET_PIN, RELAY8_DELAY_ON, RELAY8_DELAY_OFF });
  618. #endif
  619. #endif
  620. _relayBackwards();
  621. _relayConfigure();
  622. _relayBoot();
  623. _relayLoop();
  624. espurnaRegisterLoop(_relayLoop);
  625. #if WEB_SUPPORT
  626. relaySetupAPI();
  627. relaySetupWS();
  628. #endif
  629. #if MQTT_SUPPORT
  630. relaySetupMQTT();
  631. #endif
  632. #if TERMINAL_SUPPORT
  633. _relayInitCommands();
  634. #endif
  635. DEBUG_MSG_P(PSTR("[RELAY] Number of relays: %d\n"), _relays.size());
  636. }