diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 3709195fe..22641b3bc 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -3,6 +3,7 @@ * Add command SetOption60 0/1 to select dynamic sleep (0) or sleep (1) (#4497) * Update SR-04 driver to use NewPing library (#4488) * Add support for GPIO02 for newer Sonoff Basic (#4518) + * Add support for iFan02 Fanspeed in Domoticz using a selector (#4517) * * 6.3.0.14 20181127 * Add Command CalcRes to set number of decimals (0 - 7) used in commands ADD, SUB, MULT and SCALE (#4420) diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index 4a54f81be..71c761b28 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -74,6 +74,8 @@ typedef unsigned long power_t; // Power (Relay) type #define MAX_RULE_TIMERS 8 // Max number of rule timers (4 bytes / timer) #define MAX_RULE_VARS 5 // Max number of rule variables (10 bytes / variable) +#define MAX_FAN_SPEED 4 // Max number of iFan02 fan speeds (0 .. 3) + #define MQTT_TOKEN_PREFIX "%prefix%" // To be substituted by mqtt_prefix[x] #define MQTT_TOKEN_TOPIC "%topic%" // To be substituted by mqtt_topic, mqtt_grptopic, mqtt_buttontopic, mqtt_switchtopic #define MQTT_TOKEN_HOSTNAME "%hostname%" // To be substituted by mqtt_topic, mqtt_grptopic, mqtt_buttontopic, mqtt_switchtopic @@ -245,12 +247,14 @@ enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_MODULE_INIT, FUNC_PRE_INIT, FUN FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM, FUNC_BUTTON_PRESSED, FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER}; -const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 }; - enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER, SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_MAX }; const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|Timer|Rule|MaxPower|MaxEnergy|Light|Knx|Display|Wemo|Hue|Retry"; +const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 }; + +const uint8_t kIFan02Speed[MAX_FAN_SPEED][3] = {{6,6,6}, {7,6,6}, {7,7,6}, {7,6,7}}; // Do not use PROGMEM as it fails + /*********************************************************************************************\ * Extern global variables \*********************************************************************************************/ diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 869eaf7eb..379cc1d12 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -91,8 +91,6 @@ const char kTasmotaCommands[] PROGMEM = const char kSleepMode[] PROGMEM = "Dynamic|Normal"; -const uint8_t kIFan02Speed[4][3] = {{6,6,6}, {7,6,6}, {7,7,6}, {7,6,7}}; - // Global variables SerialConfig serial_config = SERIAL_8N1; // Serial interface configuration 8 data bits, No parity, 1 stop bit @@ -390,7 +388,7 @@ uint8_t GetFanspeed(void) void SetFanspeed(uint8_t fanspeed) { - for (byte i = 0; i < 3; i++) { + for (byte i = 0; i < MAX_FAN_SPEED -1; i++) { uint8_t state = kIFan02Speed[fanspeed][i]; // uint8_t state = pgm_read_byte(kIFan02Speed +(speed *3) +i); ExecuteCommandPower(i +2, state, SRC_IGNORE); // Use relay 2, 3 and 4 @@ -566,14 +564,14 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) if (data_len > 0) { if ('-' == dataBuf[0]) { payload = (int16_t)GetFanspeed() -1; - if (payload < 0) { payload = 3; } + if (payload < 0) { payload = MAX_FAN_SPEED -1; } } else if ('+' == dataBuf[0]) { payload = GetFanspeed() +1; - if (payload > 3) { payload = 0; } + if (payload > MAX_FAN_SPEED -1) { payload = 0; } } } - if ((payload >= 0) && (payload <= 3) && (payload != GetFanspeed())) { + if ((payload >= 0) && (payload < MAX_FAN_SPEED) && (payload != GetFanspeed())) { SetFanspeed(payload); } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, GetFanspeed()); diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index cd0204a48..b5c73aea5 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -628,7 +628,7 @@ void HandleRoot(void) if (SONOFF_IFAN02 == Settings.module) { snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_CONTROL, 36, 1, D_BUTTON_TOGGLE, ""); page += mqtt_data; - for (byte i = 0; i < 4; i++) { + for (byte i = 0; i < MAX_FAN_SPEED; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i); snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_CONTROL, 16, i +2, stemp, ""); page += mqtt_data; diff --git a/sonoff/xdrv_02_mqtt.ino b/sonoff/xdrv_02_mqtt.ino index 2e026c71f..77153abfd 100644 --- a/sonoff/xdrv_02_mqtt.ino +++ b/sonoff/xdrv_02_mqtt.ino @@ -334,7 +334,7 @@ void MqttPublishPowerState(byte device) if ((device < 1) || (device > devices_present)) { device = 1; } if ((SONOFF_IFAN02 == Settings.module) && (device > 1)) { - if (GetFanspeed() < 4) { // 4 occurs when fanspeed is 3 and RC button 2 is pressed + if (GetFanspeed() < MAX_FAN_SPEED) { // 4 occurs when fanspeed is 3 and RC button 2 is pressed snprintf_P(scommand, sizeof(scommand), PSTR(D_CMND_FANSPEED)); GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT); snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, scommand, GetFanspeed()); diff --git a/sonoff/xdrv_07_domoticz.ino b/sonoff/xdrv_07_domoticz.ino index 40c3127ec..3e2170701 100644 --- a/sonoff/xdrv_07_domoticz.ino +++ b/sonoff/xdrv_07_domoticz.ino @@ -73,16 +73,25 @@ int DomoticzRssiQuality(void) void MqttPublishDomoticzPowerState(byte device) { - char sdimmer[8]; + char svalue[8]; // Dimmer or Fanspeed value - if ((device < 1) || (device > devices_present)) { - device = 1; - } - if (Settings.flag.mqtt_enabled && Settings.domoticz_relay_idx[device -1]) { - snprintf_P(sdimmer, sizeof(sdimmer), PSTR("%d"), Settings.light_dimmer); - snprintf_P(mqtt_data, sizeof(mqtt_data), DOMOTICZ_MESSAGE, - Settings.domoticz_relay_idx[device -1], (power & (1 << (device -1))) ? 1 : 0, (light_type) ? sdimmer : "", DomoticzBatteryQuality(), DomoticzRssiQuality()); - MqttPublish(domoticz_in_topic); + if (Settings.flag.mqtt_enabled) { + if ((device < 1) || (device > devices_present)) { device = 1; } + if ((SONOFF_IFAN02 == Settings.module) && Settings.domoticz_relay_idx[1] && (device > 1)) { // device (relay) 1 handled below + if (4 == device) { // Wait for device (relay) 4 to get valid GetFanspeed + uint8_t fan_speed = GetFanspeed(); + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), (int)fan_speed * 10); + snprintf_P(mqtt_data, sizeof(mqtt_data), DOMOTICZ_MESSAGE, + Settings.domoticz_relay_idx[1], (0 == fan_speed) ? 0 : 2, svalue, DomoticzBatteryQuality(), DomoticzRssiQuality()); + MqttPublish(domoticz_in_topic); + } + } + else if (Settings.domoticz_relay_idx[device -1]) { + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), Settings.light_dimmer); + snprintf_P(mqtt_data, sizeof(mqtt_data), DOMOTICZ_MESSAGE, + Settings.domoticz_relay_idx[device -1], (power & (1 << (device -1))) ? 1 : 0, (light_type) ? svalue : "", DomoticzBatteryQuality(), DomoticzRssiQuality()); + MqttPublish(domoticz_in_topic); + } } } @@ -182,7 +191,18 @@ boolean DomoticzMqttData(void) if (idx == Settings.domoticz_relay_idx[i]) { bool iscolordimmer = strcmp_P(domoticz["dtype"],PSTR("Color Switch")) == 0; snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), i +1); - if (iscolordimmer && 10 == nvalue) { // Color_SetColor + if ((SONOFF_IFAN02 == Settings.module) && (1 == i)) { // Idx 2 is fanspeed + int16_t svalue = 0; + if (domoticz.containsKey("svalue1")) { + svalue = domoticz["svalue1"]; + } else { + return 1; + } + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_FANSPEED)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), (nvalue == 2) ? svalue / 10 : 0); + found = 1; + } + else if (iscolordimmer && 10 == nvalue) { // Color_SetColor JsonObject& color = domoticz["Color"]; uint16_t level = nvalue = domoticz["svalue1"]; uint16_t r = color["r"]; r = r * level / 100; @@ -193,8 +213,9 @@ boolean DomoticzMqttData(void) snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_COLOR)); snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%02x%02x%02x%02x%02x"), r, g, b, cw, ww); found = 1; - } else if ((!iscolordimmer && 2 == nvalue) || // gswitch_sSetLevel - (iscolordimmer && 15 == nvalue)) { // Color_SetBrightnessLevel + } + else if ((!iscolordimmer && 2 == nvalue) || // gswitch_sSetLevel + (iscolordimmer && 15 == nvalue)) { // Color_SetBrightnessLevel if (domoticz.containsKey("svalue1")) { nvalue = domoticz["svalue1"]; } else { @@ -206,7 +227,8 @@ boolean DomoticzMqttData(void) snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_DIMMER)); snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); found = 1; - } else if (1 == nvalue || 0 == nvalue) { + } + else if (1 == nvalue || 0 == nvalue) { if (((power >> i) &1) == (power_t)nvalue) { return 1; } @@ -422,6 +444,7 @@ void HandleDomoticzConfiguration(void) page.replace("{4", String((int)Settings.domoticz_switch_idx[i])); } page.replace("{1", String(i +1)); + if ((SONOFF_IFAN02 == Settings.module) && (1 == i)) { break; } } for (int i = 0; i < DZ_MAX_SENSORS; i++) { page += FPSTR(HTTP_FORM_DOMOTICZ_SENSOR);