devWaves / SwitchBot-MQTT-BLE-ESP32

Allows for multiple SwitchBot bots and curtains to be controlled via MQTT sent to ESP32. ESP32 will send BLE commands to switchbots and return MQTT responses to the broker. Also supports Temperature, Motion, Contact sensors
MIT License
539 stars 66 forks source link

Support ON//OFF State für SwitchBot in Press-Mode #19

Closed gdolfen closed 3 years ago

gdolfen commented 3 years ago

Hi, I'm controlling my pool heatpump, wich only has a Power press button, with a SwitchBot in Pressmode and your Firmware on an esp32 and it works very well. Now I want to automate it depending on the energy amount of my Solar Power. To do this I need the ON/OFF State of the bot. Can you please enhance your Firmware, so that one could send ON and OFF commands in Pressmode to press the bot and the /state Channel would report the last successful commanded State?

devWaves commented 3 years ago

I'll whip something up in the next release. In a week or so. I'm camping atm, just kayaked out for a cell signal

devWaves commented 3 years ago

v5.1 has the feature you requested, please let me know if it works for you

There are 3 new lists to consider for bots in PRESS mode that you want to simulate ON/OFF

/*** Bots in PRESS mode to simulate ON/OFF - ESP32 will try to keep track of the ON/OFF state of your device while in PRESS mode***/
// Add bots while in PRESS mode that will simulate ON/OFF. Default state will be used if no MQTT retained on state topic
// false = default state = OFF
// true = default state = ON
static std::map<std::string, bool> botsSimulateONOFFinPRESSmode = {
  /*{ "switchbotone", false },
    { "switchbottwo", false }*/
};

//Add bots OFF hold time for simulated ON/OFF, if not in list, the current hold value will be used. Device must be in botsSimulateONOFFinPRESSmode list
static std::map<std::string, int> botsSimulatedOFFHoldTimes = {
  /*{ "switchbotone", 3 },
    { "switchbottwo", 10 }*/
};

//Add bots ON hold time for simulated ON/OFF, if not in list, the current hold value will be used. Device must be in botsSimulateONOFFinPRESSmode list
static std::map<std::string, int> botsSimulatedONHoldTimes = {
  /*{ "switchbotone", 15 },
    { "switchbottwo", 1}*/
};
/********************************************/

If your device's ON/OFF press action hold time would be the same for ON/OFF, simply include the devices in then botsSimulateONOFFinPRESSmode list

If you device's ON/OFF uses a different hold time for ON vs OFF, also add your devices to botsSimulatedOFFHoldTimes and/or botsSimulatedONHoldTimes. So when On is pressed it will hold for X seconds, when Off is pressed it will hold for Y seconds

If the state is incorrect, or you want to control the state of the device, you can manually call the set function for "STATEOFF" or "STATEON" to correct the state

gdolfen commented 2 years ago

Thank you for your work, but it seemes not to be the solution.

The most time, when I send the ON command (topic "switchbot/esp32/bot/pool/set"), I receive an OFF in topic "switchbot/esp32/bot/pool/state". And the topic "switchbot/esp32/bot/pool/attributes" does not update on both ON/OFF directly.

Here is my config:

static const char* mqtt_host = "***";                       //  MQTT Broker server ip
static const char* mqtt_user = "";                         //  MQTT Broker username. If empty or NULL, no authentication will be used
static const char* mqtt_pass = "";                         //  MQTT Broker password
static const int mqtt_port = 1883;                                  //  MQTT Port
static std::string mqtt_main_topic = "switchbot";                   //  MQTT main topic

/* Switchbot Bot Settings */
static std::map<std::string, std::string> allBots = {
  { "pool", "***" }
};
      static std::map<std::string, bool> botsSimulateONOFFinPRESSmode = {
        { "pool", false }
      };
/* Home Assistant Settings */
static bool home_assistant_mqtt_discovery = false;                    // Enable to publish Home Assistant MQTT Discovery config
static std::string home_assistant_mqtt_prefix = "homeassistant";     // MQTT Home Assistant prefix
static bool home_assistant_expose_seperate_curtain_position = false;  // When enabled, a seperate sensor will be added that will expose the curtain position. This is useful when using the Prometheus integration to graph curtain positions. The cover entity doesn't expose the position for Prometheus
static bool home_assistant_use_opt_mode = false;                     // For bots in switch mode assume on/off right away. Optimistic mode. (Icon will change in HA). If devices were already configured in HA, you need to delete them and reboot esp32

/* Switchbot General Settings */
static int tryConnecting = 60;                  // How many times to try connecting to bot
static int trySending = 60;                     // How many times to try sending command to bot
static int initialScan = 60;                   // How many seconds to scan for bots on ESP reboot and autoRescan. Once all devices are found scan stops, so you can set this to a big number
static int infoScanTime = 30;                   // How many seconds to scan for single device status updates
static int rescanTime = 30;                    // Automatically rescan for device info of all devices every X seconds (default 10 min)
static int queueSize = 50;                      // Max number of control/requestInfo/rescan MQTT commands stored in the queue. If you send more then queueSize, they will be ignored
static int defaultBotWaitTime = 2;              // wait at least X seconds between control command send to bots. ESP32 will detect if bot is in press mode with a hold time and will add hold time to this value per device
static int defaultCurtainWaitTime = 0;          // wait at least X seconds between control command send to curtains
static int waitForResponseSec = 5;             // How many seconds to wait for a bot/curtain response
static int noResponseRetryAmount = 5;           // How many times to retry if no response received
static int defaultScanAfterControlSecs = 10;    // Default How many seconds to wait for state/status update call after set/control command. *override with botScanTime list
static int defaultMeterScanSecs = 60;           // Default Scan for meter temp sensors every X seconds. *override with botScanTime list
static int waitForMQTTRetainMessages = 5;       // Only for bots in simulated ON/OFF: On boot ESP32 will look for retained MQTT state messages for X secs, otherwise default state is used

static bool autoRescan = true;                      // perform automatic rescan (uses rescanTime and initialScan).
static bool scanAfterControl = true;                // perform requestInfo after successful control command (uses botScanTime).
static bool waitBetweenControl = true;              // wait between commands sent to bot/curtain (avoids sending while bot is busy)
static bool getSettingsOnBoot = true;               // Currently only works for bot (curtain documentation not available but can probably be reverse engineered easily). Get bot extra settings values like firmware, holdSecs, inverted, number of timers. ***If holdSecs is available it is used by waitBetweenControl
static bool getBotResponse = true;                  // get a response from the bot devices. A response of "success" means the most recent command was successful. A response of "busy" means the bot was busy when the command was sent
static bool getCurtainResponse = false;              // get a response from the curtain devices. A response of "success" means the most recent command was successful. A response of "busy" means the bot was busy when the command was sent
static bool retryBotOnBusy = true;                  // Requires getBotResponse = true. if bot responds with busy, the last control command will retry until success
static bool retryCurtainOnBusy = false;              // Requires getCurtainResponse = true. if curtain responds with busy, the last control command will retry until success
static bool retryBotActionNoResponse = false;       // Retry if bot doesn't send a response. Bot default is false because no response can still mean the bot triggered.
static bool retryBotSetNoResponse = true;           // Retry if bot doesn't send a response when requesting settings (hold, firwmare etc) or settings hold/mode
static bool retryCurtainNoResponse = false;          // Retry if curtain doesn't send a response. Default is true. It shouldn't matter if curtain receives the same command twice (or multiple times)
static bool immediateBotStateUpdate = true;         // ESP32 will send ON/OFF state update as soon as MQTT is received. You can set this = false if not using Home Assistant Discovery.
static bool immediateCurtainStateUpdate = false;     // ESP32 will send OPEN/CLOSE and Position state update as soon as MQTT is received. You can set this = false if not using Home Assistant Discovery.
static bool assumeNoResponseMeansSuccess = true;    // Only for bots in simulated ON/OFF: If the ESP32 does not receive a response after sending command (after noResponseRetryAmount reached and retryBotActionNoResponse = true) assume it worked and change state

static bool printSerialOutputForDebugging = false;      

Is there somthing wrong in it?

Sorry for the late answer, I was on holiday.

devWaves commented 2 years ago

1) Does your bot use a password? if yes you don't have it set in the config you sent.

2) Everything in the advanced settings you can leave as the defaults. (Even though you aren't using curtains). Your settings work though for me. You can leave the HA stuff set to defaults, it just publishes extra to MQTT on boot. Doesn't affect anything if you aren't using HA

3) what is the RSSI value you get in the bot attributes? If the value is between -80 and -99 that means it isn't a great connection. The code will retry the number of times defined in the advanced settings but if it eventually fails completely the state will return back to the previous value (because the assumption is it failed and the state shouldn't have changed)

4) When setup correctly, when the bot is set in PRESS mode, and is also in the list botsSimulateONOFFinPRESSmode, you will notice that the attribute will return a mode: "PressOnOff"

switchbot/esp32/bot/pool/attributes payload: {"rssi":-83,"mode":"PressOnOff","state":"ON","batt":89}

5) When the bot is in PRESS mode and not in the botsSimulateONOFFinPRESSmode list, it will always return state= OFF

6) When the bot is in PRESS mode and is also in the botsSimulateONOFFinPRESSmode list, as soon as the ESP32 receives an ON/OFF set command and immediateBotStateUpdate = true, it will immediately send "state = the command you sent". If the set command fails though, the state will revert back to the previous value

it is working for me. Sorry not sure where your setup issue could be otherwise

devWaves commented 2 years ago

let me know if you get it working with 5.1, but....

I will release a version 5.2 to allow for more accurate on/off state guessing if the esp32 crashes mid process or loses power. I am going to create a new "last successful state" topic instead of relying on the normal state topic when the esp32 reboots and has to retrieve the last retained state.

gdolfen commented 2 years ago

I have added an external antenna to my esp. Now my rssi is between -80 and -90 and the results are more reliable. But sending OFF is not working for me (the bot does not react), I have to send PRESS for both ON/OFF (this leads to a PRESS state for a second in the queue).

During this day one PRESS was delivered to the bot, but the queue stayed at its old value, maybe there was no success response of the bot delivered. Is there a way (like rssi) to check the bluetooth signal power?

devWaves commented 2 years ago

the rssi in the attributes IS the BLE signal power between the switchbot to the esp32. it is not the WIFI rssi

it sounds like you need to get the esp32 closer to your pool pump. if your pool pump has power, plug the esp32 close to it.

the 2.4ghz wifi signal is easier to extend then the BLE signal. the esp32 is intended to be somewhat close to the switchbot and use WIFI as the means to extend the distance

if the signal reaches -90, the esp32 is too far from the switchbot. -80 and above, its so so and may only successfully send command half of the time

you would have to monitor the mqtt traffic closely and look for commandSent and success for the commands you send and why they arent working for you

note: if the last successful state is OFF and you send an OFF command, it is ignored, this is because sending off again would actually turn it ON.

devWaves commented 2 years ago

you can send PRESS, but doing this will just flip between the 2 states on/off and perform the action

you should only send on/off for this feature to ensure you aren't actioning the bot to turn off when it is already off, which would make it turn on

use STATEOFF and STATEON to correct the state if it is incorrect without performing the action

gdolfen commented 2 years ago

I placed the esp closer to the bot and now the signal ist between -30 an -40. Sending ON/OFF is now working for me. During my tests I had one problem: it seems if a command ist not sucessfully send to the bot, and during this time the same command is send twice, that there is no correct rollback to the previous state: 1) CommandSend: ON 2) Successful 3) State: ON 4) CommandSend: OFF (not successful) 5) CommandSend: OFF (will be ignored) 6) State: OFF (should be rolled back to ON)

devWaves commented 2 years ago

in 5.1 I think I only update the actual state after it was successful, so ill have to check if the 2nd off gets fully ignored

in 5.2 i'll look at doing it differentely though

-30 and -40 are REALLY good signal btw

gdolfen commented 2 years ago

Sounds good. I'll stay tuned for your next update. Thank you for your support. Perhaps you can also add a wifi watchdog, because if the esp loses the wifi connection, it has to be restarted.

devWaves commented 2 years ago

the wifi connection will reconnect already. I just tested and it works for me. I left the esp32 on, rebooted my router, and it continues to work once the 2.4ghz wifi is back and the MQTT connection is reconnected

the wifi connection is handled by the EspMQTTClient.h

I actually recently bought a new router, swapped it with my other one (without unplugging the esp32) and it still works. so I know that works.

devWaves commented 2 years ago

you can try adjusting client.setMqttReconnectionAttemptDelay(100);

it is set to try reconnecting every 100ms if it loses connection. Maybe try a bigger value. like 10000 = 10 secs.

Maybe 100 ms is too low if it loses connection alot?

devWaves commented 2 years ago

v5.2 released. let me know how it goes

I noticed that if you send ON or OFF (same command) 3 or more times while in simulated on/off mode there was a infinite loop that occurred. This may be why you 'lost connection'. I fixed that

I added a new assumedstate topic that will update whenever a command is seen as successful. So if they esp32 reboots for some reason it will grab the last retained message on that topic to set the current assumed state

the state should now update properly if the command was ignored or errored

gdolfen commented 2 years ago

With v5.2 everything works fine.

Thank you for your great support