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.

818 lines
21 KiB

7 years ago
  1. /*
  2. RF BRIDGE MODULE
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if RF_SUPPORT
  6. #include <queue>
  7. #include <Ticker.h>
  8. #if RFB_DIRECT
  9. #include <RCSwitch.h>
  10. #endif
  11. // -----------------------------------------------------------------------------
  12. // DEFINITIONS
  13. // -----------------------------------------------------------------------------
  14. // EFM8 Protocol
  15. #define RF_MESSAGE_SIZE 9
  16. #define RF_MAX_MESSAGE_SIZE (112+4)
  17. #define RF_CODE_START 0xAA
  18. #define RF_CODE_ACK 0xA0
  19. #define RF_CODE_LEARN 0xA1
  20. #define RF_CODE_LEARN_KO 0xA2
  21. #define RF_CODE_LEARN_OK 0xA3
  22. #define RF_CODE_RFIN 0xA4
  23. #define RF_CODE_RFOUT 0xA5
  24. #define RF_CODE_SNIFFING_ON 0xA6
  25. #define RF_CODE_SNIFFING_OFF 0xA7
  26. #define RF_CODE_RFOUT_NEW 0xA8
  27. #define RF_CODE_LEARN_NEW 0xA9
  28. #define RF_CODE_LEARN_KO_NEW 0xAA
  29. #define RF_CODE_LEARN_OK_NEW 0xAB
  30. #define RF_CODE_RFOUT_BUCKET 0xB0
  31. #define RF_CODE_STOP 0x55
  32. // Settings
  33. #define RF_MAX_KEY_LENGTH (9)
  34. // -----------------------------------------------------------------------------
  35. // GLOBALS TO THE MODULE
  36. // -----------------------------------------------------------------------------
  37. unsigned char _uartbuf[RF_MESSAGE_SIZE+3] = {0};
  38. unsigned char _uartpos = 0;
  39. unsigned char _learnId = 0;
  40. bool _learnStatus = true;
  41. bool _rfbin = false;
  42. typedef struct {
  43. byte code[RF_MESSAGE_SIZE];
  44. byte times;
  45. } rfb_message_t;
  46. static std::queue<rfb_message_t> _rfb_message_queue;
  47. #if RFB_DIRECT
  48. RCSwitch * _rfModem;
  49. bool _learning = false;
  50. #endif
  51. bool _rfb_receive = false;
  52. bool _rfb_transmit = false;
  53. unsigned char _rfb_repeat = RF_SEND_TIMES;
  54. #if WEB_SUPPORT
  55. Ticker _rfb_sendcodes;
  56. #endif
  57. // -----------------------------------------------------------------------------
  58. // PRIVATES
  59. // -----------------------------------------------------------------------------
  60. /*
  61. From a byte array to an hexa char array ("A220EE...", double the size)
  62. */
  63. static bool _rfbToChar(byte * in, char * out, int n = RF_MESSAGE_SIZE) {
  64. for (unsigned char p = 0; p<n; p++) {
  65. sprintf_P(&out[p*2], PSTR("%02X"), in[p]);
  66. }
  67. return true;
  68. }
  69. #if WEB_SUPPORT
  70. void _rfbWebSocketSendCodeArray(unsigned char start, unsigned char size) {
  71. DynamicJsonBuffer jsonBuffer;
  72. JsonObject& root = jsonBuffer.createObject();
  73. JsonObject& rfb = root.createNestedObject("rfb");
  74. rfb["size"] = size;
  75. rfb["start"] = start;
  76. JsonArray& on = rfb.createNestedArray("on");
  77. JsonArray& off = rfb.createNestedArray("off");
  78. for (byte id=start; id<start+size; id++) {
  79. on.add(rfbRetrieve(id, true));
  80. off.add(rfbRetrieve(id, false));
  81. }
  82. wsSend(root);
  83. }
  84. void _rfbWebSocketSendCode(unsigned char id) {
  85. _rfbWebSocketSendCodeArray(id, 1);
  86. }
  87. void _rfbWebSocketSendCodes() {
  88. _rfbWebSocketSendCodeArray(0, relayCount());
  89. }
  90. void _rfbWebSocketOnSend(JsonObject& root) {
  91. root["rfbVisible"] = 1;
  92. root["rfbRepeat"] = getSetting("rfbRepeat", RF_SEND_TIMES).toInt();
  93. root["rfbCount"] = relayCount();
  94. #if RFB_DIRECT
  95. root["rfbdirectVisible"] = 1;
  96. root["rfbRX"] = getSetting("rfbRX", RFB_RX_PIN).toInt();
  97. root["rfbTX"] = getSetting("rfbTX", RFB_TX_PIN).toInt();
  98. #endif
  99. _rfb_sendcodes.once_ms(1000, _rfbWebSocketSendCodes);
  100. }
  101. void _rfbWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
  102. if (strcmp(action, "rfblearn") == 0) rfbLearn(data["id"], data["status"]);
  103. if (strcmp(action, "rfbforget") == 0) rfbForget(data["id"], data["status"]);
  104. if (strcmp(action, "rfbsend") == 0) rfbStore(data["id"], data["status"], data["data"].as<const char*>());
  105. }
  106. bool _rfbWebSocketOnReceive(const char * key, JsonVariant& value) {
  107. return (strncmp(key, "rfb", 3) == 0);
  108. }
  109. #endif // WEB_SUPPORT
  110. /*
  111. From an hexa char array ("A220EE...") to a byte array (half the size)
  112. */
  113. static int _rfbToArray(const char * in, byte * out, int length = RF_MESSAGE_SIZE * 2) {
  114. int n = strlen(in);
  115. if (n > RF_MAX_MESSAGE_SIZE*2 || (length > 0 && n != length)) return 0;
  116. char tmp[3] = {0,0,0};
  117. n /= 2;
  118. for (unsigned char p = 0; p<n; p++) {
  119. memcpy(tmp, &in[p*2], 2);
  120. out[p] = strtol(tmp, NULL, 16);
  121. }
  122. return n;
  123. }
  124. void _rfbSendRaw(const byte *message, const unsigned char n = RF_MESSAGE_SIZE) {
  125. for (unsigned char j=0; j<n; j++) {
  126. Serial.write(message[j]);
  127. }
  128. }
  129. void _rfbSend() {
  130. if (!_rfb_transmit) return;
  131. // Check if there is something in the queue
  132. if (_rfb_message_queue.empty()) return;
  133. static unsigned long last = 0;
  134. if (millis() - last < RF_SEND_DELAY) return;
  135. last = millis();
  136. // Pop the first message and send it
  137. rfb_message_t message = _rfb_message_queue.front();
  138. _rfb_message_queue.pop();
  139. _rfbSend(message.code);
  140. // Push it to the stack again if we need to send it more than once
  141. if (message.times > 1) {
  142. message.times = message.times - 1;
  143. _rfb_message_queue.push(message);
  144. }
  145. yield();
  146. }
  147. void _rfbSend(byte * code, unsigned char times) {
  148. if (!_rfb_transmit) return;
  149. // rc-switch will repeat on its own
  150. #if RFB_DIRECT
  151. times = 1;
  152. #endif
  153. char buffer[RF_MESSAGE_SIZE];
  154. _rfbToChar(code, buffer);
  155. DEBUG_MSG_P(PSTR("[RF] Enqueuing MESSAGE '%s' %d time(s)\n"), buffer, times);
  156. rfb_message_t message;
  157. memcpy(message.code, code, RF_MESSAGE_SIZE);
  158. message.times = times;
  159. _rfb_message_queue.push(message);
  160. }
  161. void _rfbSendRawOnce(byte *code, unsigned char length) {
  162. char buffer[length*2];
  163. _rfbToChar(code, buffer, length);
  164. DEBUG_MSG_P(PSTR("[RF] Sending RAW MESSAGE '%s'\n"), buffer);
  165. _rfbSendRaw(code, length);
  166. }
  167. bool _rfbMatch(char* code, unsigned char& relayID, unsigned char& value, char* buffer = NULL) {
  168. if (strlen(code) != 18) return false;
  169. bool found = false;
  170. String compareto = String(&code[12]);
  171. compareto.toUpperCase();
  172. DEBUG_MSG_P(PSTR("[RF] Trying to match code %s\n"), compareto.c_str());
  173. for (unsigned char i=0; i<relayCount(); i++) {
  174. String code_on = rfbRetrieve(i, true);
  175. if (code_on.length() && code_on.endsWith(compareto)) {
  176. DEBUG_MSG_P(PSTR("[RF] Match ON code for relay %d\n"), i);
  177. value = 1;
  178. found = true;
  179. if (buffer) strcpy(buffer, code_on.c_str());
  180. }
  181. String code_off = rfbRetrieve(i, false);
  182. if (code_off.length() && code_off.endsWith(compareto)) {
  183. DEBUG_MSG_P(PSTR("[RF] Match OFF code for relay %d\n"), i);
  184. if (found) value = 2;
  185. found = true;
  186. if (buffer) strcpy(buffer, code_off.c_str());
  187. }
  188. if (found) {
  189. relayID = i;
  190. return true;
  191. }
  192. }
  193. return false;
  194. }
  195. void _rfbDecode() {
  196. static unsigned long last = 0;
  197. if (millis() - last < RF_RECEIVE_DELAY) return;
  198. last = millis();
  199. byte action = _uartbuf[0];
  200. char buffer[RF_MESSAGE_SIZE * 2 + 1] = {0};
  201. DEBUG_MSG_P(PSTR("[RF] Action 0x%02X\n"), action);
  202. if (action == RF_CODE_LEARN_KO) {
  203. _rfbAck();
  204. DEBUG_MSG_P(PSTR("[RF] Learn timeout\n"));
  205. #if WEB_SUPPORT
  206. wsSend_P(PSTR("{\"action\": \"rfbTimeout\"}"));
  207. #endif
  208. }
  209. if (action == RF_CODE_LEARN_OK || action == RF_CODE_RFIN) {
  210. _rfbAck();
  211. _rfbToChar(&_uartbuf[1], buffer);
  212. DEBUG_MSG_P(PSTR("[RF] Received message '%s'\n"), buffer);
  213. }
  214. if (action == RF_CODE_LEARN_OK) {
  215. DEBUG_MSG_P(PSTR("[RF] Learn success\n"));
  216. rfbStore(_learnId, _learnStatus, buffer);
  217. // Websocket update
  218. #if WEB_SUPPORT
  219. _rfbWebSocketSendCode(_learnId);
  220. #endif
  221. }
  222. if (action == RF_CODE_RFIN) {
  223. /* Look for the code, possibly replacing the code with the exact learned one on match
  224. * we want to do this on learn too to be sure that the learned code is the same if it
  225. * is equivalent
  226. */
  227. unsigned char id;
  228. unsigned char status;
  229. bool matched = _rfbMatch(buffer, id, status, buffer);
  230. if (matched) {
  231. DEBUG_MSG_P(PSTR("[RF] Matched message '%s'\n"), buffer);
  232. _rfbin = true;
  233. if (status == 2) {
  234. relayToggle(id);
  235. } else {
  236. relayStatus(id, status == 1);
  237. }
  238. }
  239. #if MQTT_SUPPORT
  240. mqttSend(MQTT_TOPIC_RFIN, buffer, false, false);
  241. #endif
  242. }
  243. }
  244. bool _rfbCompare(const char * code1, const char * code2) {
  245. return strcmp(&code1[12], &code2[12]) == 0;
  246. }
  247. bool _rfbSameOnOff(unsigned char id) {
  248. return _rfbCompare(rfbRetrieve(id, true).c_str(), rfbRetrieve(id, false).c_str());
  249. }
  250. void _rfbParseRaw(char * raw) {
  251. byte message[RF_MAX_MESSAGE_SIZE];
  252. int len = _rfbToArray(raw, message, 0);
  253. if (len > 0) {
  254. _rfbSendRawOnce(message, len);
  255. }
  256. }
  257. void _rfbParseCode(char * code) {
  258. // The payload may be a code in HEX format ([0-9A-Z]{18}) or
  259. // the code comma the number of times to transmit it.
  260. char * tok = strtok(code, ",");
  261. // Check if a switch is linked to that message
  262. unsigned char id;
  263. unsigned char status = 0;
  264. if (_rfbMatch(tok, id, status)) {
  265. if (status == 2) {
  266. relayToggle(id);
  267. } else {
  268. relayStatus(id, status == 1);
  269. }
  270. return;
  271. }
  272. byte message[RF_MESSAGE_SIZE];
  273. int len = _rfbToArray(tok, message, 0);
  274. if (len) {
  275. tok = strtok(NULL, ",");
  276. byte times = (tok != NULL) ? atoi(tok) : 1;
  277. _rfbSend(message, times);
  278. }
  279. }
  280. //
  281. // RF handler implementations
  282. //
  283. #if !RFB_DIRECT // Default for ITEAD SONOFF RFBRIDGE
  284. void _rfbAck() {
  285. DEBUG_MSG_P(PSTR("[RF] Sending ACK\n"));
  286. Serial.println();
  287. Serial.write(RF_CODE_START);
  288. Serial.write(RF_CODE_ACK);
  289. Serial.write(RF_CODE_STOP);
  290. Serial.flush();
  291. Serial.println();
  292. }
  293. void _rfbLearnImpl() {
  294. DEBUG_MSG_P(PSTR("[RF] Sending LEARN\n"));
  295. Serial.println();
  296. Serial.write(RF_CODE_START);
  297. Serial.write(RF_CODE_LEARN);
  298. Serial.write(RF_CODE_STOP);
  299. Serial.flush();
  300. Serial.println();
  301. }
  302. void _rfbSend(byte * message) {
  303. Serial.println();
  304. Serial.write(RF_CODE_START);
  305. Serial.write(RF_CODE_RFOUT);
  306. _rfbSendRaw(message);
  307. Serial.write(RF_CODE_STOP);
  308. Serial.flush();
  309. Serial.println();
  310. }
  311. void _rfbReceive() {
  312. static bool receiving = false;
  313. while (Serial.available()) {
  314. yield();
  315. byte c = Serial.read();
  316. //DEBUG_MSG_P(PSTR("[RF] Received 0x%02X\n"), c);
  317. if (receiving) {
  318. if (c == RF_CODE_STOP && (_uartpos == 1 || _uartpos == RF_MESSAGE_SIZE + 1)) {
  319. _rfbDecode();
  320. receiving = false;
  321. } else if (_uartpos <= RF_MESSAGE_SIZE) {
  322. _uartbuf[_uartpos++] = c;
  323. } else {
  324. // wrong message, should have received a RF_CODE_STOP
  325. receiving = false;
  326. }
  327. } else if (c == RF_CODE_START) {
  328. _uartpos = 0;
  329. receiving = true;
  330. }
  331. }
  332. }
  333. #else // RFB_DIRECT
  334. void _rfbAck() {}
  335. void _rfbLearnImpl() {
  336. DEBUG_MSG_P(PSTR("[RF] Entering LEARN mode\n"));
  337. _learning = true;
  338. }
  339. void _rfbSend(byte * message) {
  340. if (!_rfb_transmit) return;
  341. unsigned int protocol = message[1];
  342. unsigned int timing =
  343. (message[2] << 8) |
  344. (message[3] << 0) ;
  345. unsigned int bitlength = message[4];
  346. unsigned long rf_code =
  347. (message[5] << 24) |
  348. (message[6] << 16) |
  349. (message[7] << 8) |
  350. (message[8] << 0) ;
  351. _rfModem->setProtocol(protocol);
  352. if (timing > 0) {
  353. _rfModem->setPulseLength(timing);
  354. }
  355. _rfModem->send(rf_code, bitlength);
  356. _rfModem->resetAvailable();
  357. }
  358. void _rfbReceive() {
  359. if (!_rfb_receive) return;
  360. static long learn_start = 0;
  361. if (!_learning && learn_start) {
  362. learn_start = 0;
  363. }
  364. if (_learning) {
  365. if (!learn_start) {
  366. DEBUG_MSG_P(PSTR("[RF] Arming learn timeout\n"));
  367. learn_start = millis();
  368. }
  369. if (learn_start > 0 && millis() - learn_start > RF_LEARN_TIMEOUT) {
  370. DEBUG_MSG_P(PSTR("[RF] Learn timeout triggered\n"));
  371. memset(_uartbuf, 0, sizeof(_uartbuf));
  372. _uartbuf[0] = RF_CODE_LEARN_KO;
  373. _rfbDecode();
  374. _learning = false;
  375. }
  376. }
  377. if (_rfModem->available()) {
  378. static unsigned long last = 0;
  379. if (millis() - last > RF_DEBOUNCE) {
  380. last = millis();
  381. unsigned long rf_code = _rfModem->getReceivedValue();
  382. if ( rf_code > 0) {
  383. DEBUG_MSG_P(PSTR("[RF] Received code: %08X\n"), rf_code);
  384. unsigned int timing = _rfModem->getReceivedDelay();
  385. memset(_uartbuf, 0, sizeof(_uartbuf));
  386. unsigned char *msgbuf = _uartbuf + 1;
  387. _uartbuf[0] = _learning ? RF_CODE_LEARN_OK: RF_CODE_RFIN;
  388. msgbuf[0] = 0xC0;
  389. msgbuf[1] = _rfModem->getReceivedProtocol();
  390. msgbuf[2] = timing >> 8;
  391. msgbuf[3] = timing >> 0;
  392. msgbuf[4] = _rfModem->getReceivedBitlength();
  393. msgbuf[5] = rf_code >> 24;
  394. msgbuf[6] = rf_code >> 16;
  395. msgbuf[7] = rf_code >> 8;
  396. msgbuf[8] = rf_code >> 0;
  397. _rfbDecode();
  398. _learning = false;
  399. }
  400. }
  401. _rfModem->resetAvailable();
  402. }
  403. yield();
  404. }
  405. #endif // RFB_DIRECT
  406. void _rfbLearn() {
  407. _rfbLearnImpl();
  408. #if WEB_SUPPORT
  409. char buffer[100];
  410. snprintf_P(buffer, sizeof(buffer), PSTR("{\"action\": \"rfbLearn\", \"data\":{\"id\": %d, \"status\": %d}}"), _learnId, _learnStatus ? 1 : 0);
  411. wsSend(buffer);
  412. #endif
  413. }
  414. #if MQTT_SUPPORT
  415. void _rfbMqttCallback(unsigned int type, const char * topic, const char * payload) {
  416. if (type == MQTT_CONNECT_EVENT) {
  417. char buffer[strlen(MQTT_TOPIC_RFLEARN) + 3];
  418. snprintf_P(buffer, sizeof(buffer), PSTR("%s/+"), MQTT_TOPIC_RFLEARN);
  419. mqttSubscribe(buffer);
  420. if (_rfb_transmit) {
  421. mqttSubscribe(MQTT_TOPIC_RFOUT);
  422. }
  423. #if !RFB_DIRECT
  424. mqttSubscribe(MQTT_TOPIC_RFRAW);
  425. #endif
  426. }
  427. if (type == MQTT_MESSAGE_EVENT) {
  428. // Match topic
  429. String t = mqttMagnitude((char *) topic);
  430. // Check if should go into learn mode
  431. if (t.startsWith(MQTT_TOPIC_RFLEARN)) {
  432. _learnId = t.substring(strlen(MQTT_TOPIC_RFLEARN)+1).toInt();
  433. if (_learnId >= relayCount()) {
  434. DEBUG_MSG_P(PSTR("[RF] Wrong learnID (%d)\n"), _learnId);
  435. return;
  436. }
  437. _learnStatus = (char)payload[0] != '0';
  438. _rfbLearn();
  439. return;
  440. }
  441. if (t.equals(MQTT_TOPIC_RFOUT)) {
  442. _rfbParseCode((char *) payload);
  443. }
  444. #if !RFB_DIRECT
  445. if (t.equals(MQTT_TOPIC_RFRAW)) {
  446. _rfbParseRaw((char *) payload);
  447. }
  448. #endif
  449. }
  450. }
  451. #endif // MQTT_SUPPORT
  452. #if API_SUPPORT
  453. void _rfbAPISetup() {
  454. apiRegister(MQTT_TOPIC_RFOUT,
  455. [](char * buffer, size_t len) {
  456. snprintf_P(buffer, len, PSTR("OK"));
  457. },
  458. [](const char * payload) {
  459. _rfbParseCode((char *) payload);
  460. }
  461. );
  462. apiRegister(MQTT_TOPIC_RFLEARN,
  463. [](char * buffer, size_t len) {
  464. snprintf_P(buffer, len, PSTR("OK"));
  465. },
  466. [](const char * payload) {
  467. // The payload must be the relayID plus the mode (0 or 1)
  468. char * tok = strtok((char *) payload, ",");
  469. if (NULL == tok) return;
  470. if (!isNumber(tok)) return;
  471. _learnId = atoi(tok);
  472. if (_learnId >= relayCount()) {
  473. DEBUG_MSG_P(PSTR("[RF] Wrong learnID (%d)\n"), _learnId);
  474. return;
  475. }
  476. tok = strtok(NULL, ",");
  477. if (NULL == tok) return;
  478. _learnStatus = (char) tok[0] != '0';
  479. _rfbLearn();
  480. }
  481. );
  482. #if !RFB_DIRECT
  483. apiRegister(MQTT_TOPIC_RFRAW,
  484. [](char * buffer, size_t len) {
  485. snprintf_P(buffer, len, PSTR("OK"));
  486. },
  487. [](const char * payload) {
  488. _rfbParseRaw((char *)payload);
  489. }
  490. );
  491. #endif
  492. }
  493. #endif // API_SUPPORT
  494. #if TERMINAL_SUPPORT
  495. void _rfbInitCommands() {
  496. terminalRegisterCommand(F("LEARN"), [](Embedis* e) {
  497. if (e->argc < 3) {
  498. terminalError(F("Wrong arguments"));
  499. return;
  500. }
  501. int id = String(e->argv[1]).toInt();
  502. if (id >= relayCount()) {
  503. DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
  504. return;
  505. }
  506. int status = String(e->argv[2]).toInt();
  507. rfbLearn(id, status == 1);
  508. terminalOK();
  509. });
  510. terminalRegisterCommand(F("FORGET"), [](Embedis* e) {
  511. if (e->argc < 3) {
  512. terminalError(F("Wrong arguments"));
  513. return;
  514. }
  515. int id = String(e->argv[1]).toInt();
  516. if (id >= relayCount()) {
  517. DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
  518. return;
  519. }
  520. int status = String(e->argv[2]).toInt();
  521. rfbForget(id, status == 1);
  522. terminalOK();
  523. });
  524. }
  525. #endif // TERMINAL_SUPPORT
  526. // -----------------------------------------------------------------------------
  527. // PUBLIC
  528. // -----------------------------------------------------------------------------
  529. void rfbStore(unsigned char id, bool status, const char * code) {
  530. DEBUG_MSG_P(PSTR("[RF] Storing %d-%s => '%s'\n"), id, status ? "ON" : "OFF", code);
  531. char key[RF_MAX_KEY_LENGTH] = {0};
  532. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  533. setSetting(key, code);
  534. }
  535. String rfbRetrieve(unsigned char id, bool status) {
  536. char key[RF_MAX_KEY_LENGTH] = {0};
  537. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  538. return getSetting(key);
  539. }
  540. void rfbStatus(unsigned char id, bool status) {
  541. String value = rfbRetrieve(id, status);
  542. if (value.length() > 0) {
  543. bool same = _rfbSameOnOff(id);
  544. byte message[RF_MAX_MESSAGE_SIZE];
  545. int len = _rfbToArray(value.c_str(), message, 0);
  546. if (len == RF_MESSAGE_SIZE && // probably a standard msg
  547. (message[0] != RF_CODE_START || // raw would start with 0xAA
  548. message[1] != RF_CODE_RFOUT_BUCKET || // followed by 0xB0,
  549. message[2] + 4 != len || // needs a valid length,
  550. message[len-1] != RF_CODE_STOP)) { // and finish with 0x55
  551. if (!_rfbin) {
  552. unsigned char times = same ? 1 : _rfb_repeat;
  553. _rfbSend(message, times);
  554. }
  555. } else {
  556. _rfbSendRawOnce(message, len); // send a raw message
  557. }
  558. }
  559. _rfbin = false;
  560. }
  561. void rfbLearn(unsigned char id, bool status) {
  562. _learnId = id;
  563. _learnStatus = status;
  564. _rfbLearn();
  565. }
  566. void rfbForget(unsigned char id, bool status) {
  567. char key[RF_MAX_KEY_LENGTH] = {0};
  568. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  569. delSetting(key);
  570. // Websocket update
  571. #if WEB_SUPPORT
  572. char wsb[100];
  573. snprintf_P(wsb, sizeof(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"\"}]}"), id, status ? 1 : 0);
  574. wsSend(wsb);
  575. #endif
  576. }
  577. // -----------------------------------------------------------------------------
  578. // SETUP & LOOP
  579. // -----------------------------------------------------------------------------
  580. void rfbSetup() {
  581. #if MQTT_SUPPORT
  582. mqttRegister(_rfbMqttCallback);
  583. #endif
  584. #if API_SUPPORT
  585. _rfbAPISetup();
  586. #endif
  587. #if WEB_SUPPORT
  588. wsOnSendRegister(_rfbWebSocketOnSend);
  589. wsOnActionRegister(_rfbWebSocketOnAction);
  590. wsOnReceiveRegister(_rfbWebSocketOnReceive);
  591. #endif
  592. #if TERMINAL_SUPPORT
  593. _rfbInitCommands();
  594. #endif
  595. _rfb_repeat = getSetting("rfbRepeat", RF_SEND_TIMES).toInt();
  596. #if RFB_DIRECT
  597. unsigned char rx = getSetting("rfbRX", RFB_RX_PIN).toInt();
  598. unsigned char tx = getSetting("rfbTX", RFB_TX_PIN).toInt();
  599. _rfb_receive = gpioValid(rx);
  600. _rfb_transmit = gpioValid(tx);
  601. if (!_rfb_transmit && !_rfb_receive) {
  602. DEBUG_MSG_P(PSTR("[RF] Neither RX or TX are set\n"));
  603. return;
  604. }
  605. _rfModem = new RCSwitch();
  606. if (_rfb_receive) {
  607. _rfModem->enableReceive(rx);
  608. DEBUG_MSG_P(PSTR("[RF] RF receiver on GPIO %u\n"), rx);
  609. }
  610. if (_rfb_transmit) {
  611. _rfModem->enableTransmit(tx);
  612. _rfModem->setRepeatTransmit(_rfb_repeat);
  613. DEBUG_MSG_P(PSTR("[RF] RF transmitter on GPIO %u\n"), tx);
  614. }
  615. #else
  616. _rfb_receive = true;
  617. _rfb_transmit = true;
  618. #endif
  619. // Register loop only when properly configured
  620. espurnaRegisterLoop(rfbLoop);
  621. }
  622. void rfbLoop() {
  623. _rfbReceive();
  624. _rfbSend();
  625. }
  626. #endif