Browse Source

Merge branch 'rfbridge' into dev

Conflicts:
	code/espurna/relay.ino
fastled
Xose Pérez 7 years ago
parent
commit
068d1a9089
14 changed files with 581 additions and 22 deletions
  1. +1
    -0
      code/espurna/config/arduino.h
  2. +1
    -1
      code/espurna/config/debug.h
  3. +13
    -1
      code/espurna/config/general.h
  4. +24
    -0
      code/espurna/config/hardware.h
  5. +12
    -3
      code/espurna/espurna.ino
  6. +9
    -0
      code/espurna/hardware.ino
  7. +2
    -3
      code/espurna/mqtt.ino
  8. +26
    -9
      code/espurna/relay.ino
  9. +307
    -0
      code/espurna/rfbridge.ino
  10. +31
    -2
      code/espurna/web.ino
  11. +23
    -3
      code/html/custom.css
  12. +62
    -0
      code/html/custom.js
  13. +49
    -0
      code/html/index.html
  14. +21
    -0
      code/platformio.ini

+ 1
- 0
code/espurna/config/arduino.h View File

@ -30,6 +30,7 @@
//#define LED_CONTROLLER //#define LED_CONTROLLER
//#define H801_LED_CONTROLLER //#define H801_LED_CONTROLLER
//#define ESPURNA_H //#define ESPURNA_H
//#define SONOFF_RFBRIDGE
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// Features (values below are non-default values) // Features (values below are non-default values)


+ 1
- 1
code/espurna/config/debug.h View File

@ -1,6 +1,6 @@
#define DEBUG_MESSAGE_MAX_LENGTH 80 #define DEBUG_MESSAGE_MAX_LENGTH 80
#ifdef SONOFF_DUAL
#if defined(SONOFF_DUAL)
#undef DEBUG_PORT #undef DEBUG_PORT
#endif #endif


+ 13
- 1
code/espurna/config/general.h View File

@ -24,7 +24,7 @@
// nc -ul 8111 // nc -ul 8111
//#define DEBUG_UDP_IP IPAddress(192, 168, 1, 100) //#define DEBUG_UDP_IP IPAddress(192, 168, 1, 100)
//#define DEBUG_UDP_PORT 8111
//#define DEBUG_UDP_PORT 8113
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// EEPROM // EEPROM
@ -136,6 +136,7 @@ PROGMEM const char* const custom_reset_string[] = {
#define RELAY_PROVIDER_RELAY 0 #define RELAY_PROVIDER_RELAY 0
#define RELAY_PROVIDER_DUAL 1 #define RELAY_PROVIDER_DUAL 1
#define RELAY_PROVIDER_LIGHT 2 #define RELAY_PROVIDER_LIGHT 2
#define RELAY_PROVIDER_RFBRIDGE 3
// Pulse time in milliseconds // Pulse time in milliseconds
#define RELAY_PULSE_TIME 1.0 #define RELAY_PULSE_TIME 1.0
@ -249,6 +250,9 @@ PROGMEM const char* const custom_reset_string[] = {
#define MQTT_TOPIC_HOSTNAME "host" #define MQTT_TOPIC_HOSTNAME "host"
#define MQTT_TOPIC_TIME "time" #define MQTT_TOPIC_TIME "time"
#define MQTT_TOPIC_ANALOG "analog" #define MQTT_TOPIC_ANALOG "analog"
#define MQTT_TOPIC_RFOUT "rfout"
#define MQTT_TOPIC_RFIN "rfin"
#define MQTT_TOPIC_RFLEARN "rflearn"
#define MQTT_STATUS_ONLINE "1" // Value for the device ON message #define MQTT_STATUS_ONLINE "1" // Value for the device ON message
#define MQTT_STATUS_OFFLINE "0" // Value for the device OFF message (will) #define MQTT_STATUS_OFFLINE "0" // Value for the device OFF message (will)
@ -345,3 +349,11 @@ PROGMEM const char* const custom_reset_string[] = {
// this device should be discoberable and respond to Alexa commands. // this device should be discoberable and respond to Alexa commands.
// Both ENABLE_FAUXMO and fauxmoEnabled should be 1 for Alexa support to work. // Both ENABLE_FAUXMO and fauxmoEnabled should be 1 for Alexa support to work.
#define FAUXMO_ENABLED 1 #define FAUXMO_ENABLED 1
// -----------------------------------------------------------------------------
// RFBRIDGE
// -----------------------------------------------------------------------------
#define RF_SEND_TIMES 4 // How many times to send the message
#define RF_SEND_DELAY 250 // Interval between sendings in ms

+ 24
- 0
code/espurna/config/hardware.h View File

@ -161,6 +161,7 @@
#define SERIAL_BAUDRATE 19230 #define SERIAL_BAUDRATE 19230
#undef RELAY_PROVIDER #undef RELAY_PROVIDER
#define RELAY_PROVIDER RELAY_PROVIDER_DUAL #define RELAY_PROVIDER RELAY_PROVIDER_DUAL
#define DUMMY_RELAY_COUNT 2
#elif defined(SONOFF_4CH) #elif defined(SONOFF_4CH)
@ -222,6 +223,21 @@
#define LED1_PIN 13 #define LED1_PIN 13
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
#elif defined(SONOFF_RFBRIDGE)
#define MANUFACTURER "ITEAD"
#define DEVICE "RFBRIDGE"
#define BUTTON1_PIN 0
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define LED1_PIN 13
#define LED1_PIN_INVERSE 1
#undef SERIAL_BAUDRATE
#define SERIAL_BAUDRATE 19200
#undef RELAY_PROVIDER
#define RELAY_PROVIDER RELAY_PROVIDER_RFBRIDGE
#define DUMMY_RELAY_COUNT 6
#define TRACK_RELAY_STATUS 0
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Electrodragon boards // Electrodragon boards
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -269,6 +285,7 @@
#define DEVICE "AI_LIGHT" #define DEVICE "AI_LIGHT"
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT #define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
#define LIGHT_PROVIDER LIGHT_PROVIDER_MY9192 #define LIGHT_PROVIDER LIGHT_PROVIDER_MY9192
#define DUMMY_RELAY_COUNT 1
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// LED Controller // LED Controller
@ -282,6 +299,7 @@
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT #define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
#define LIGHT_PROVIDER LIGHT_PROVIDER_RGB #define LIGHT_PROVIDER LIGHT_PROVIDER_RGB
#define DUMMY_RELAY_COUNT 1
#undef RGBW_INVERSE_LOGIC #undef RGBW_INVERSE_LOGIC
#undef RGBW_RED_PIN #undef RGBW_RED_PIN
@ -307,6 +325,7 @@
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT #define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
#define LIGHT_PROVIDER LIGHT_PROVIDER_RGB2W #define LIGHT_PROVIDER LIGHT_PROVIDER_RGB2W
#define DUMMY_RELAY_COUNT 1
#undef RGBW_INVERSE_LOGIC #undef RGBW_INVERSE_LOGIC
#undef RGBW_RED_PIN #undef RGBW_RED_PIN
@ -528,6 +547,11 @@
#define BUTTON_SET_PULLUP 4 #define BUTTON_SET_PULLUP 4
#endif #endif
// Does the board track the relay status?
#ifndef TRACK_RELAY_STATUS
#define TRACK_RELAY_STATUS 1
#endif
// Relay providers // Relay providers
#ifndef RELAY_PROVIDER #ifndef RELAY_PROVIDER
#define RELAY_PROVIDER RELAY_PROVIDER_RELAY #define RELAY_PROVIDER RELAY_PROVIDER_RELAY


+ 12
- 3
code/espurna/espurna.ino View File

@ -120,19 +120,22 @@ unsigned char customReset() {
} }
void hardwareSetup() { void hardwareSetup() {
EEPROM.begin(4096); EEPROM.begin(4096);
#ifdef DEBUG_PORT #ifdef DEBUG_PORT
DEBUG_PORT.begin(SERIAL_BAUDRATE); DEBUG_PORT.begin(SERIAL_BAUDRATE);
if (customReset() == CUSTOM_RESET_HARDWARE) { if (customReset() == CUSTOM_RESET_HARDWARE) {
DEBUG_PORT.setDebugOutput(true); DEBUG_PORT.setDebugOutput(true);
} }
#endif
#ifdef SONOFF_DUAL
#elif defined(SERIAL_BAUDRATE)
Serial.begin(SERIAL_BAUDRATE); Serial.begin(SERIAL_BAUDRATE);
#endif #endif
#if not EMBEDDED_WEB #if not EMBEDDED_WEB
SPIFFS.begin(); SPIFFS.begin();
#endif #endif
} }
void hardwareLoop() { void hardwareLoop() {
@ -214,6 +217,9 @@ void setup() {
mqttSetup(); mqttSetup();
ntpSetup(); ntpSetup();
#ifdef SONOFF_RFBRIDGE
rfbSetup();
#endif
#if ENABLE_I2C #if ENABLE_I2C
i2cSetup(); i2cSetup();
#endif #endif
@ -267,9 +273,12 @@ void loop() {
#if ENABLE_FAUXMO #if ENABLE_FAUXMO
fauxmoLoop(); fauxmoLoop();
#endif #endif
#ifndef SONOFF_DUAL
#if !defined(SONOFF_DUAL) & !defined(SONOFF_RFBRIDGE)
settingsLoop(); settingsLoop();
#endif #endif
#ifdef SONOFF_RFBRIDGE
rfbLoop();
#endif
#if ENABLE_NOFUSS #if ENABLE_NOFUSS
nofussLoop(); nofussLoop();
#endif #endif


+ 9
- 0
code/espurna/hardware.ino View File

@ -17,6 +17,7 @@ the migration to future version 2 will be straigh forward.
#define RELAY_PROVIDER_RELAY 0 #define RELAY_PROVIDER_RELAY 0
#define RELAY_PROVIDER_DUAL 1 #define RELAY_PROVIDER_DUAL 1
#define RELAY_PROVIDER_LIGHT 2 #define RELAY_PROVIDER_LIGHT 2
#define RELAY_PROVIDER_RFBRIDGE 3
#define LIGHT_PROVIDER_NONE 0 #define LIGHT_PROVIDER_NONE 0
#define LIGHT_PROVIDER_WS2812 1 #define LIGHT_PROVIDER_WS2812 1
@ -278,6 +279,14 @@ void hwUpwardsCompatibility() {
setSetting("lightLogic", 1); setSetting("lightLogic", 1);
#endif #endif
#ifdef SONOFF_RFBRIDGE
setSetting("board", 26);
setSetting("ledGPIO", 1, 13);
setSetting("ledLogic", 1, 1);
setSetting("btnGPIO", 1, 0);
setSetting("relayProvider", RELAY_PROVIDER_RFBRIDGE);
#endif
saveSettings(); saveSettings();
} }

+ 2
- 3
code/espurna/mqtt.ino View File

@ -216,14 +216,13 @@ void _mqttOnMessage(char* topic, char* payload, unsigned int len) {
char message[len + 1]; char message[len + 1];
strlcpy(message, (char *) payload, len + 1); strlcpy(message, (char *) payload, len + 1);
DEBUG_MSG_P(PSTR("[MQTT] Received %s => %s"), topic, message);
#if MQTT_SKIP_RETAINED #if MQTT_SKIP_RETAINED
if (millis() - mqttConnectedAt < MQTT_SKIP_TIME) { if (millis() - mqttConnectedAt < MQTT_SKIP_TIME) {
DEBUG_MSG_P(PSTR(" - SKIPPED\n"));
DEBUG_MSG_P(PSTR("[MQTT] Received %s => %s - SKIPPED\n"), topic, message);
return; return;
} }
#endif #endif
DEBUG_MSG_P(PSTR("\n"));
DEBUG_MSG_P(PSTR("[MQTT] Received %s => %s\n"), topic, message);
// Check system topics // Check system topics
String t = mqttSubtopic((char *) topic); String t = mqttSubtopic((char *) topic);


+ 26
- 9
code/espurna/relay.ino View File

@ -20,6 +20,7 @@ typedef struct {
unsigned long delay_off; unsigned long delay_off;
unsigned int floodWindowStart; unsigned int floodWindowStart;
unsigned char floodWindowChanges; unsigned char floodWindowChanges;
bool scheduled;
unsigned int scheduledStatusTime; unsigned int scheduledStatusTime;
bool scheduledStatus; bool scheduledStatus;
bool scheduledReport; bool scheduledReport;
@ -40,6 +41,10 @@ void relayProviderStatus(unsigned char id, bool status) {
if (id >= _relays.size()) return; if (id >= _relays.size()) return;
#if RELAY_PROVIDER == RELAY_PROVIDER_RFBRIDGE
rfbStatus(id, status);
#endif
#if RELAY_PROVIDER == RELAY_PROVIDER_DUAL #if RELAY_PROVIDER == RELAY_PROVIDER_DUAL
_dual_status ^= (1 << id); _dual_status ^= (1 << id);
Serial.flush(); Serial.flush();
@ -64,6 +69,10 @@ bool relayProviderStatus(unsigned char id) {
if (id >= _relays.size()) return false; if (id >= _relays.size()) return false;
#if RELAY_PROVIDER == RELAY_PROVIDER_RFBRIDGE
return _relays[id].scheduledStatus;
#endif
#if RELAY_PROVIDER == RELAY_PROVIDER_DUAL #if RELAY_PROVIDER == RELAY_PROVIDER_DUAL
return ((_dual_status & (1 << id)) > 0); return ((_dual_status & (1 << id)) > 0);
#endif #endif
@ -142,7 +151,9 @@ bool relayStatus(unsigned char id, bool status, bool report) {
bool changed = false; bool changed = false;
#if TRACK_RELAY_STATUS
if (relayStatus(id) != status) { if (relayStatus(id) != status) {
#endif
unsigned int currentTime = millis(); unsigned int currentTime = millis();
unsigned int floodWindowEnd = _relays[id].floodWindowStart + 1000 * RELAY_FLOOD_WINDOW; unsigned int floodWindowEnd = _relays[id].floodWindowStart + 1000 * RELAY_FLOOD_WINDOW;
@ -169,6 +180,7 @@ bool relayStatus(unsigned char id, bool status, bool report) {
} }
_relays[id].scheduled = true;
_relays[id].scheduledStatus = status; _relays[id].scheduledStatus = status;
if (report) _relays[id].scheduledReport = true; if (report) _relays[id].scheduledReport = true;
@ -178,7 +190,9 @@ bool relayStatus(unsigned char id, bool status, bool report) {
changed = true; changed = true;
#if TRACK_RELAY_STATUS
} }
#endif
return changed; return changed;
} }
@ -400,16 +414,14 @@ void relayInfluxDB(unsigned char id) {
void relaySetup() { void relaySetup() {
#if defined(SONOFF_DUAL)
// Two dummy relays for the dual
_relays.push_back((relay_t) {0, 0, 0, RELAY1_DELAY_ON, RELAY1_DELAY_OFF});
_relays.push_back((relay_t) {0, 0, 0, RELAY2_DELAY_ON, RELAY2_DELAY_OFF});
#elif defined(AI_LIGHT) | defined(LED_CONTROLLER) | defined(H801_LED_CONTROLLER)
// Dummy relays for AI Light, Magic Home LED Controller, H801,
// Sonoff Dual and Sonoff RF Bridge
#ifdef DUMMY_RELAY_COUNT
// One dummy relay for the AI Thinker Light & Magic Home and H801 led controllers
_relays.push_back((relay_t) {0, 0, 0, RELAY1_DELAY_ON, RELAY1_DELAY_OFF});
for (unsigned char i=0; i < DUMMY_RELAY_COUNT; i++) {
_relays.push_back((relay_t) {0, 0});
_relays[i].scheduled = false;
}
#else #else
@ -454,7 +466,11 @@ void relayLoop(void) {
unsigned int currentTime = millis(); unsigned int currentTime = millis();
bool status = _relays[id].scheduledStatus; bool status = _relays[id].scheduledStatus;
#if TRACK_RELAY_STATUS
if (relayStatus(id) != status && currentTime >= _relays[id].scheduledStatusTime) { if (relayStatus(id) != status && currentTime >= _relays[id].scheduledStatusTime) {
#else
if (_relays[id].scheduled && currentTime >= _relays[id].scheduledStatusTime) {
#endif
DEBUG_MSG_P(PSTR("[RELAY] #%d set to %s\n"), id, status ? "ON" : "OFF"); DEBUG_MSG_P(PSTR("[RELAY] #%d set to %s\n"), id, status ? "ON" : "OFF");
@ -486,6 +502,7 @@ void relayLoop(void) {
relayInfluxDB(id); relayInfluxDB(id);
#endif #endif
_relays[id].scheduled = false;
_relays[id].scheduledReport = false; _relays[id].scheduledReport = false;
} }


+ 307
- 0
code/espurna/rfbridge.ino View File

@ -0,0 +1,307 @@
/*
ITEAD RF BRIDGE MODULE
Copyright (C) 2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
#ifdef SONOFF_RFBRIDGE
// -----------------------------------------------------------------------------
// DEFINITIONS
// -----------------------------------------------------------------------------
#define RF_MESSAGE_SIZE 9
#define RF_CODE_START 0xAA
#define RF_CODE_ACK 0xA0
#define RF_CODE_LEARN 0xA1
#define RF_CODE_LEARN_KO 0xA2
#define RF_CODE_LEARN_OK 0xA3
#define RF_CODE_RFIN 0xA4
#define RF_CODE_RFOUT 0xA5
#define RF_CODE_STOP 0x55
// -----------------------------------------------------------------------------
// GLOBALS TO THE MODULE
// -----------------------------------------------------------------------------
unsigned char _uartbuf[RF_MESSAGE_SIZE+3] = {0};
unsigned char _uartpos = 0;
unsigned char _learnId = 0;
bool _learnStatus = true;
bool _rfbin = false;
// -----------------------------------------------------------------------------
// PRIVATES
// -----------------------------------------------------------------------------
void _rfbAck() {
DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending ACK\n"));
Serial.println();
Serial.write(RF_CODE_START);
Serial.write(RF_CODE_ACK);
Serial.write(RF_CODE_STOP);
Serial.flush();
Serial.println();
}
void _rfbLearn() {
DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending LEARN\n"));
Serial.println();
Serial.write(RF_CODE_START);
Serial.write(RF_CODE_LEARN);
Serial.write(RF_CODE_STOP);
Serial.flush();
Serial.println();
char wsb[100];
sprintf_P(wsb, PSTR("{\"action\": \"rfbLearn\", \"data\":{\"id\": %d, \"status\": %d}}"), _learnId, _learnStatus ? 1 : 0);
wsSend(wsb);
}
void _rfbSend(byte * message) {
Serial.println();
Serial.write(RF_CODE_START);
Serial.write(RF_CODE_RFOUT);
for (unsigned char j=0; j<RF_MESSAGE_SIZE; j++) {
Serial.write(message[j]);
}
Serial.write(RF_CODE_STOP);
Serial.flush();
Serial.println();
}
void _rfbSend(byte * message, int times) {
char buffer[RF_MESSAGE_SIZE];
_rfbToChar(message, buffer);
DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending MESSAGE '%s' %d time(s)\n"), buffer, times);
for (int i=0; i<times; i++) {
if (i>0) {
unsigned long start = millis();
while (millis() - start < RF_SEND_DELAY) delay(1);
}
_rfbSend(message);
}
}
void _rfbDecode() {
byte action = _uartbuf[0];
char buffer[RF_MESSAGE_SIZE * 2 + 1] = {0};
DEBUG_MSG_P(PSTR("[RFBRIDGE] Action 0x%02X\n"), action);
if (action == RF_CODE_LEARN_KO) {
_rfbAck();
DEBUG_MSG_P(PSTR("[RFBRIDGE] Learn timeout\n"));
wsSend("{\"action\": \"rfbTimeout\"}");
}
if (action == RF_CODE_LEARN_OK || action == RF_CODE_RFIN) {
_rfbToChar(&_uartbuf[1], buffer);
mqttSend(MQTT_TOPIC_RFIN, buffer);
_rfbAck();
}
if (action == RF_CODE_LEARN_OK) {
DEBUG_MSG_P(PSTR("[RFBRIDGE] Learn success\n"));
rfbStore(_learnId, _learnStatus, buffer);
// Websocket update
char wsb[100];
sprintf_P(wsb, PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"%s\"}]}"), _learnId, _learnStatus ? 1 : 0, buffer);
wsSend(wsb);
}
if (action == RF_CODE_RFIN) {
DEBUG_MSG_P(PSTR("[RFBRIDGE] Forward message '%s'\n"), buffer);
// Look for the code
unsigned char id, status;
bool found = false;
for (id=0; id<relayCount(); id++) {
for (status=0; status<2; status++) {
String code = rfbRetrieve(id, status == 1);
if (code.length()) {
if (code.endsWith(&buffer[12])) {
found = true;
break;
}
}
}
if (found) break;
}
if (found) {
_rfbin = true;
relayStatus(id, status == 1);
}
}
}
void _rfbReceive() {
static bool receiving = false;
while (Serial.available()) {
yield();
byte c = Serial.read();
//DEBUG_MSG_P(PSTR("[RFBRIDGE] Received 0x%02X\n"), c);
if (receiving) {
if (c == RF_CODE_STOP) {
_rfbDecode();
receiving = false;
} else {
_uartbuf[_uartpos++] = c;
}
} else if (c == RF_CODE_START) {
_uartpos = 0;
receiving = true;
}
}
}
bool _rfbCompare(const char * code1, const char * code2) {
return strcmp(&code1[12], &code2[12]) == 0;
}
bool _rfbSameOnOff(unsigned char id) {
return _rfbCompare(rfbRetrieve(id, true).c_str(), rfbRetrieve(id, false).c_str());
}
/*
From an hexa char array ("A220EE...") to a byte array (half the size)
*/
bool _rfbToArray(const char * in, byte * out) {
if (strlen(in) != RF_MESSAGE_SIZE * 2) return false;
char tmp[3] = {0};
for (unsigned char p = 0; p<RF_MESSAGE_SIZE; p++) {
memcpy(tmp, &in[p*2], 2);
out[p] = strtol(tmp, NULL, 16);
}
return true;
}
/*
From a byte array to an hexa char array ("A220EE...", double the size)
*/
bool _rfbToChar(byte * in, char * out) {
for (unsigned char p = 0; p<RF_MESSAGE_SIZE; p++) {
sprintf(&out[p*2], "%02X", in[p]);
}
return true;
}
void _rfbMqttCallback(unsigned int type, const char * topic, const char * payload) {
if (type == MQTT_CONNECT_EVENT) {
char buffer[strlen(MQTT_TOPIC_RFLEARN) + 3];
sprintf(buffer, "%s/+", MQTT_TOPIC_RFLEARN);
mqttSubscribe(buffer);
mqttSubscribe(MQTT_TOPIC_RFOUT);
}
if (type == MQTT_MESSAGE_EVENT) {
// Match topic
String t = mqttSubtopic((char *) topic);
// Check if should go into learn mode
if (t.startsWith(MQTT_TOPIC_RFLEARN)) {
_learnId = t.substring(strlen(MQTT_TOPIC_RFLEARN)+1).toInt();
if (_learnId >= relayCount()) {
DEBUG_MSG_P(PSTR("[RFBRIDGE] Wrong learnID (%d)\n"), _learnId);
return;
}
_learnStatus = (char)payload[0] != '0';
_rfbLearn();
}
if (t.equals(MQTT_TOPIC_RFOUT)) {
byte message[RF_MESSAGE_SIZE];
if (_rfbToArray(payload, message)) {
_rfbSend(message, 1);
}
}
}
}
// -----------------------------------------------------------------------------
// PUBLIC
// -----------------------------------------------------------------------------
void rfbStore(unsigned char id, bool status, const char * code) {
DEBUG_MSG_P(PSTR("[RFBRIDGE] Storing %d-%s => '%s'\n"), id, status ? "ON" : "OFF", code);
char key[8] = {0};
sprintf(key, "rfb%d%s", id, status ? "on" : "off");
setSetting(key, code);
}
String rfbRetrieve(unsigned char id, bool status) {
char key[8] = {0};
sprintf(key, "rfb%d%s", id, status ? "on" : "off");
return getSetting(key);
}
void rfbStatus(unsigned char id, bool status) {
String value = rfbRetrieve(id, status);
if (value.length() > 0) {
bool same = _rfbSameOnOff(id);
byte message[RF_MESSAGE_SIZE];
_rfbToArray(value.c_str(), message);
unsigned char times = RF_SEND_TIMES;
if (same) times = _rfbin ? 0 : 1;
_rfbSend(message, times);
}
}
void rfbLearn(unsigned char id, bool status) {
_learnId = id;
_learnStatus = status;
_rfbLearn();
}
void rfbForget(unsigned char id, bool status) {
char key[8] = {0};
sprintf(key, "rfb%d%s", id, status ? "on" : "off");
delSetting(key);
// Websocket update
char wsb[100];
sprintf_P(wsb, PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"\"}]}"), id, status ? 1 : 0);
wsSend(wsb);
}
// -----------------------------------------------------------------------------
// SETUP & LOOP
// -----------------------------------------------------------------------------
void rfbSetup() {
mqttRegister(_rfbMqttCallback);
}
void rfbLoop() {
_rfbReceive();
}
#endif

+ 31
- 2
code/espurna/web.ino View File

@ -92,6 +92,21 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
ESP.restart(); ESP.restart();
} }
#ifdef SONOFF_RFBRIDGE
if (action.equals("rfblearn") && root.containsKey("data")) {
JsonObject& data = root["data"];
rfbLearn(data["id"], data["status"]);
}
if (action.equals("rfbforget") && root.containsKey("data")) {
JsonObject& data = root["data"];
rfbForget(data["id"], data["status"]);
}
if (action.equals("rfbsend") && root.containsKey("data")) {
JsonObject& data = root["data"];
rfbStore(data["id"], data["status"], data["data"].as<const char*>());
}
#endif
if (action.equals("restore") && root.containsKey("data")) { if (action.equals("restore") && root.containsKey("data")) {
JsonObject& data = root["data"]; JsonObject& data = root["data"];
@ -129,7 +144,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
if (data.containsKey("status")) { if (data.containsKey("status")) {
bool state = (strcmp(data["status"], "1") == 0);
bool status = (strcmp(data["status"], "1") == 0);
unsigned int relayID = 0; unsigned int relayID = 0;
if (data.containsKey("id")) { if (data.containsKey("id")) {
@ -137,7 +152,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
relayID = value.toInt(); relayID = value.toInt();
} }
relayStatus(relayID, state);
relayStatus(relayID, status);
} }
@ -565,6 +580,20 @@ void _wsStart(uint32_t client_id) {
root["powPowerFactor"] = String(getPowerFactor(), 2); root["powPowerFactor"] = String(getPowerFactor(), 2);
#endif #endif
#ifdef SONOFF_RFBRIDGE
root["rfbVisible"] = 1;
root["rfbCount"] = relayCount();
JsonArray& rfb = root.createNestedArray("rfb");
for (byte id=0; id<relayCount(); id++) {
for (byte status=0; status<2; status++) {
JsonObject& node = rfb.createNestedObject();
node["id"] = id;
node["status"] = status;
node["data"] = rfbRetrieve(id, status == 1);
}
}
#endif
root["maxNetworks"] = WIFI_MAX_NETWORKS; root["maxNetworks"] = WIFI_MAX_NETWORKS;
JsonArray& wifi = root.createNestedArray("wifi"); JsonArray& wifi = root.createNestedArray("wifi");
for (byte i=0; i<WIFI_MAX_NETWORKS; i++) { for (byte i=0; i<WIFI_MAX_NETWORKS; i++) {


+ 23
- 3
code/html/custom.css View File

@ -46,7 +46,8 @@
background: #1f8dd6; background: #1f8dd6;
} }
.button-reset, .button-reset,
.button-reconnect {
.button-reconnect,
.button-rfb-forget {
background: rgb(202, 60, 60); background: rgb(202, 60, 60);
} }
.button-upgrade { .button-upgrade {
@ -58,13 +59,15 @@
background: rgb(0, 202, 0); background: rgb(0, 202, 0);
margin-left: 5px; margin-left: 5px;
} }
.button-add-network {
.button-add-network,
.button-rfb-learn {
background: rgb(28, 184, 65); background: rgb(28, 184, 65);
} }
.button-del-network { .button-del-network {
background: rgb(202, 60, 60); background: rgb(202, 60, 60);
} }
.button-more-network {
.button-more-network,
.button-rfb-send {
background: rgb(223, 117, 20); background: rgb(223, 117, 20);
} }
.button-settings-backup, .button-settings-backup,
@ -149,3 +152,20 @@ div.state {
.right { .right {
text-align: right; text-align: right;
} }
#panel-rfb fieldset {
margin: 10px 2px;
padding: 20px;
}
#panel-rfb input {
margin-right: 5px;
}
#panel-rfb label {
padding-top: 5px;
}
#panel-rfb input {
text-align: center;
}

+ 62
- 0
code/html/custom.js View File

@ -284,6 +284,45 @@ function addNetwork() {
} }
function addRfbNode() {
var numNodes = $("#rfbNodes > fieldset").length;
var template = $("#rfbNodeTemplate").children();
var line = $(template).clone();
var status = true;
$("span", line).html(numNodes+1);
$(line).find("input").each(function() {
$(this).attr("data_id", numNodes);
$(this).attr("data_status", status ? 1 : 0);
status = !status;
});
$(line).find(".button-rfb-learn").on('click', rfbLearn);
$(line).find(".button-rfb-forget").on('click', rfbForget);
$(line).find(".button-rfb-send").on('click', rfbSend);
line.appendTo("#rfbNodes");
return line;
}
function rfbLearn() {
var parent = $(this).parents(".pure-g");
var input = $("input", parent);
websock.send(JSON.stringify({'action': 'rfblearn', 'data' : {'id' : input.attr("data_id"), 'status': input.attr("data_status")}}));
}
function rfbForget() {
var parent = $(this).parents(".pure-g");
var input = $("input", parent);
websock.send(JSON.stringify({'action': 'rfbforget', 'data' : {'id' : input.attr("data_id"), 'status': input.attr("data_status")}}));
}
function rfbSend() {
var parent = $(this).parents(".pure-g");
var input = $("input", parent);
websock.send(JSON.stringify({'action': 'rfbsend', 'data' : {'id' : input.attr("data_id"), 'status': input.attr("data_status"), 'data': input.val()}}));
}
function forgetCredentials() { function forgetCredentials() {
$.ajax({ $.ajax({
'method': 'GET', 'method': 'GET',
@ -337,10 +376,33 @@ function processData(data) {
}, 1000); }, 1000);
} }
if (data.action == "rfbLearn") {
// Nothing to do?
}
if (data.action == "rfbTimeout") {
// Nothing to do?
}
return; return;
} }
if (key == "rfbCount") {
for (var i=0; i<data.rfbCount; i++) addRfbNode();
return;
}
if (key == "rfb") {
var nodes = data.rfb;
for (var i in nodes) {
var node = nodes[i];
var element = $("input[name=rfbcode][data_id=" + node["id"] + "][data_status=" + node["status"] + "]");
if (element.length) element.val(node["data"]);
}
return;
}
if (key == "color") { if (key == "color") {
$("input[name='color']").wheelColorPicker('setValue', data[key], true); $("input[name='color']").wheelColorPicker('setValue', data[key], true);
return; return;


+ 49
- 0
code/html/index.html View File

@ -114,6 +114,10 @@
<a href="#" class="pure-menu-link" data="panel-power">POWER</a> <a href="#" class="pure-menu-link" data="panel-power">POWER</a>
</li> </li>
<li class="pure-menu-item module module-rfb">
<a href="#" class="pure-menu-link" data="panel-rfb">RFBRIDGE</a>
</li>
<li class="pure-menu-item"> <li class="pure-menu-item">
<a href="#" class="pure-menu-link" data="panel-admin">ADMIN</a> <a href="#" class="pure-menu-link" data="panel-admin">ADMIN</a>
</li> </li>
@ -717,12 +721,57 @@
</div> </div>
</form> </form>
<div class="panel" id="panel-rfb">
<div class="header">
<h1>RFBRIDGE</h1>
<h2>
Sonoff 433 RF Bridge Configuration<br /><br />
To learn a new code click <strong>LEARN</strong>, the Sonoff RFBridge will beep, then press a button on the remote, the RFBridge will then double beep and the new code should show up. If the device double beeps but the code does not update it has not been properly learnt. Keep trying.<br /><br />
Modify or create new codes manually (all codes must be 18 characters long) and then click <strong>SAVE</strong> to store them in the device memory. If your controlled device uses the same code to switch ON and OFF, learn the code with the ON button and copy paste it to the OFF input box, then click SAVE on the last one to store the value.<br /><br />
Delete any code clicking the <strong>FORGET</strong> button.
</h2>
</div>
<div class="page">
<div id="rfbNodes" />
</div>
</div>
</div> <!-- content --> </div> <!-- content -->
</div> <!-- layout --> </div> <!-- layout -->
<!-- Templates --> <!-- Templates -->
<div id="rfbNodeTemplate" class="template">
<fieldset>
<legend>&nbsp;Switch <span></span>&nbsp;</legend>
<div class="pure-g">
<label class="pure-u-1-2 pure-u-sm-1-4">Switch ON</label>
<input class="pure-u-1-2 pure-u-sm-1-3" type="text" maxlength="18" name="rfbcode" data_id="1" data_status="1" />
<div class="pure-u-1-3 pure-u-md-1-8"><button type="button" class="pure-u-23-24 pure-button button-rfb-learn">LEARN</button></div>
<div class="pure-u-1-3 pure-u-md-1-8"><button type="button" class="pure-u-23-24 pure-button button-rfb-send">SAVE</button></div>
<div class="pure-u-1-3 pure-u-md-1-8"><button type="button" class="pure-u-23-24 pure-button button-rfb-forget">FORGET</button></div>
</div>
<div class="pure-g">
<label class="pure-u-1-2 pure-u-sm-1-4">Switch OFF</label>
<input class="pure-u-1-2 pure-u-sm-1-3" type="text" maxlength="18" name="rfbcode" data_id="1" data_status="0" />
<div class="pure-u-1-3 pure-u-md-1-8"><button type="button" class="pure-u-23-24 pure-button button-rfb-learn">LEARN</button></div>
<div class="pure-u-1-3 pure-u-md-1-8"><button type="button" class="pure-u-23-24 pure-button button-rfb-send">SAVE</button></div>
<div class="pure-u-1-3 pure-u-md-1-8"><button type="button" class="pure-u-23-24 pure-button button-rfb-forget">FORGET</button></div>
</div>
</fieldset>
</div>
<div id="networkTemplate" class="template"> <div id="networkTemplate" class="template">
<div class="pure-g"> <div class="pure-g">


+ 21
- 0
code/platformio.ini View File

@ -262,6 +262,27 @@ upload_speed = 115200
upload_port = "192.168.4.1" upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266 upload_flags = --auth=fibonacci --port 8266
[env:rfbridge-debug]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m} -DSONOFF_RFBRIDGE
[env:rfbridge-debug-ota]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m} -DSONOFF_RFBRIDGE
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=Algernon1 --port 8266
[env:1ch-inching-debug] [env:1ch-inching-debug]
platform = espressif8266 platform = espressif8266
framework = arduino framework = arduino


Loading…
Cancel
Save