SwitchBot bot/curtain/meter/contact/motion MQTT ESP32 bridge - Local control

this might be the reason. mqtt HA discovery devices are retained, could be that some motion stuff remains and messes it up.

Ok thanks for the heads up about in/out stuff, I will take a look later

Oh, also the in and out sensors turned on when the referring count sensors had value 0. I tweaked that in your code too, but not have it with me right now. I’ll post that after dinner too so you can have a look at it. It basically comes down to changing an expression to only send the EXITED/ENTERED payloads when the count value is not 0. This primitive solution also worked out as expected. To be clear: it didn’t increase the amount of connection errors so that can’t be the cause of before mentioned problems.

Cool. Now I already have some ideas what to try when I get back home. And happy to help! Let me know if you have any questions about it or need me to run tests!

you would still want the value updating when count is 0

the count is a roll over sequence number. I forget the range atm but lets say 0-15. When it changes from 15 back to 0 that counts as as entrance/exit…Im pretty sure anyways. Cant say I thoroughly tested that

The range reported by Switchbot cycles between 1 and 15. Zero is not included. This is also somewhere in their API docs (don’t have the link for you rn)

For some reason, if I would enter the room, the incount would increment with 1, but the outcount would change to 0 in HA. Also the other way around. I believe somewhere in your code it makes some calculation of entranceCountA + entranceCountB and the same for exitCountA + B. I don’t understand the code there but I assume in some cases the outcome of that sum can be 0 and reported to Home Assistant when the value in the BLE advertised data for either in/outcount changes and the other remains the same.

Sorry to be this vague, but as mentioned before I didn’t use anything like MQTT or ESP before this week. I do have an excellent learning capacity and feeling for languages, so that’s why I dug into the code and was able to solve it…

I hope you can make sense of what I’ve said, otherwise let me know!

no problem. all good. This is also my first real arduino/esp project. we are all learning. No doubt that I probably messed up. I wrote that before owning contact sensor and I’m not really using the in/out feature personally

I can confirm the entering/entered bug, I will fix that later tonight. Thanks

range is 1 to 15 for the button push, for in/out it is 1-3. Your logic may still be right though if it is returning 0 because it says it should only be between 1-3, I will have to test that

this is the part for in/out count, max value should be 3. It is possible 0 is sent though and I am not dealing with that. It is just a 2 bit value. Possible values being 01,10,11 (and I guess 00)

   int entranceCountA = (byte8 & 0b10000000) ? 2 : 0;
        int entranceCountB = (byte8 & 0b01000000) ? 1 : 0;
        int entranceCount = entranceCountA + entranceCountB;
        doc["in"] = entranceCount;

        int outCountA = (byte8 & 0b00100000) ? 2 : 0;
        int outCountB = (byte8 & 0b00010000) ? 1 : 0;
        int outCount = outCountA + outCountB;
        doc["out"] = outCount;

I’ve mounted a Switchbot bot to my garage door opener.
The bot is in press-mode so the ESP tries to keep track of its virtual on/off state.
However the door opener will also often be manually operated so the virtual state will be unreliable.

What I did was adding a Zigbee door-/windows magnet sensor to the garage door together with an automation which sets the state of the “assumedstate” topic to ON or OFF according to the state of the door sensor in the action part of the automation:

Setting it to off when the door-sensor is closed:

action:
  - service: mqtt.publish
    data:
      topic: switchbot/esp32garage/bot/BotGarage/assumedstate
      payload: 'OFF'

(Setting to ON accordingly when the door sensor is open)

That way to virtual bot-state will always match the garage door open/close state.
@devWaves I think that’s the only way in case a Switchbot-operated opener is also operated manually, correct?

Then everything would be solved if you tweak the code so that the value 00 is not send to HA. I think that 00 is the outcome when the value in the BLE advertising for 1 of the counts changes, while the other is still the same. That is only my assumption though. Let’s wait for me to send the code that fixed the value 0 to come up in HA

Hey to set the state manually you would want to call a message on the set topic. You need to do it this way because the esp32 also needs to keep track of the state. the esp32 is not reading off the assumed state topic all the time. Only on boot

send a set STATEOFF or STATEON message to …

ESP32 will subscribe to MQTT 'set' topic for every configure device.
      - <ESPMQTTTopic>/bot/<name>/set

    Send a payload to the 'set' topic of the device you want to control
      Strings:
        - "STATEOFF"    (Only for bots in simulated ON/OFF mode)
        - "STATEON"     (Only for bots in simulated ON/OFF mode)

I tried that first but that way I end up with having set being set to STATEON and the state changes accordingly but the assumedstate still being OFF:

Bildschirmfoto 2022-02-13 um 19.02.32

I now set both:

  - service: mqtt.publish
    data:
      topic: switchbot/esp32garage/bot/BotGarage/assumedstate
      payload: 'OFF'
  - service: mqtt.publish
    data:
      topic: switchbot/esp32garage/bot/BotGarage/set
      payload: STATEOFF

When is the assumedstate being used and what for?

You only need to look at state. assumedstate is only for my esp32 code really

it is so when the esp32 reboots, it knows what the last state was. you should not write to the assumedstate topic. Maybe there is a retained OFF message. I believe my code sends to assumedstate, but you only need to look at state

1 Like

OK, thanks for the clarification. Please keep up the great work, much appreciated!
(Already using another ESP32 for two curtain-bots, works like a charm :+1: :grinning:)

I can confirm I am not immediately writing to the assumedstate topic when STATEON/STATEOFF is sent. I update it at the next scan or command sent. So ya there is a minor bug, I will fix that today with the other bug mentioned.

but ya don’t write to assumedstate, will probably just cause esp32 state sync issues

note that attributes “state” will not update until the next scan, this will stay that way. This is because I am not collecting the attributes until a scan is made. So for the latest state, just look at the state topic. I will fix the assumedstate thing though. Cheers

This is how I tweaked the ‘00’ bug. After adding !=0 to the expression, it only sends values that are not 0. Now the in/outcount sensor never displays 0 and the IN/OUT sensor only updates when the actual value advertised by Switchbot is changing.

It might not be perfectly implemented but that’s because the lack of experience with this language. It does the work though, so maybe you can use it in a proper way to resolve this for everyone :slight_smile:

        std::map<std::string, int>::iterator itE = entranceCounts.find(deviceMac);
        if (itE != entranceCounts.end())
        {
          int eCount = itE->second;
          if (eCount != entranceCount && entranceCount != 0) {
            shouldPublish = true;
            client.publish(deviceInTopic.c_str(), "ENTERING", false);
          }
        }
        entranceCounts[deviceMac] = entranceCount;

        std::map<std::string, int>::iterator itO = outCounts.find(deviceMac);
        if (itO != outCounts.end())
        {
          int oCount = itO->second;
          if (oCount != outCount && outCount != 0) {
            shouldPublish = true;
            client.publish(deviceOutTopic.c_str(), "EXITING", false);
          }
        }
        outCounts[deviceMac] = outCount;

Hey thanks. Ya I’ve got it figured out, but thanks for the code sample. I will apply a new release later. I have at least 3 minor fixes to apply

What I would like to know is when/why 0 is being sent to confirm that is the proper fix, so if you want to test that would be cool

My questions

  1. if 5 people enter/exit one after the other (with some proper delay), does the counter go 1, 2, 3, 1, 2 or is there one or more zeroes thrown in there?

  2. does 0 count just get sent randomly when no activity for a long time? does this reset the counter sequence back to start at 0-1 (even if it never made it to 2-3)

  3. maybe an entrance resets the exit counter, and an exit resets the entrance counter? don’t know

I’m guessing the simple fix of ignoring zeros will work, just want to make sure

I am wondering how the sequence counter for the contact button works now, if that one returns 0. I don’t think it does because I am using the button feature and haven’t noticed that

edit: I just tried the button and it goes from 1-15 and after 15 resets to 1, so ya for button I can for sure ignore the 0 if it happens

  1. Didn’t test it in the way you describe, but in some other way. For example: The exit counter cycles from 1,2,3,1,2,3,1 etc, as long as there is no enter event in between. If there is, it gets reset to 0 and starts with value 1 when there is an exit again.
  2. In the time before I adjusted the code the values were only reset to 0 simultaneously with an event on the opposite sensor
  3. This describes exactly what I thought was going on.

The button counter works perfectly, I’m already running an automation to run actions on consecutive presses. When the button is pressed, HA listens to the amount of times the buttoncount changes in the following 3 secs. According to that value it then performs certain actions. The only buggy thing in this case is when I press the buttons quite fast multiple times in a row, the button sensor sometimes misses one of the increments in the count sensor.

ok thanks. ya sounds like ignoring the 0 is the correct thing to do. I will put out a fix later

ya the button won’t work if you click it fast. I am only sending a push when it notices the count changes. I don’t think implementing multiple presses would be accurate enough

I made the following automation to handle consecutive button presses. Created a counter helper to count the consecutive presses because I’m not familiar enough with templating. I thin the ideal way would be to have an automation in single mode that triggers on button press, wait for 3 zecs and then compares the current buttoncount value against the value of buttoncount before the automation was triggered. I had tons of little projects waiting to be picked up so I chose the helper solution because I knew how to make it work.

‘’’
alias: (SWB CS) Button presses
description: ‘’
trigger:

  • platform: state
    entity_id: sensor.contactone_buttoncount
    from:
    • ‘1’
    • ‘2’
    • ‘3’
    • ‘4’
    • ‘5’
    • ‘6’
    • ‘7’
    • ‘8’
    • ‘9’
    • ‘10’
    • ‘11’
    • ‘12’
    • ‘13’
    • ‘14’
    • ‘15’
      to:
    • ‘1’
    • ‘2’
    • ‘3’
    • ‘4’
    • ‘5’
    • ‘6’
    • ‘7’
    • ‘8’
    • ‘9’
    • ‘10’
    • ‘11’
    • ‘12’
    • ‘13’
    • ‘14’
    • ‘15’
      condition: []
      action:
  • service: counter.increment
    target:
    entity_id: counter.contactone_consecutive_button_presses
  • wait_for_trigger:
    • platform: state
      entity_id: sensor.contactone_buttoncount
      continue_on_timeout: false
      timeout: ‘3’
  • service: counter.increment
    target:
    entity_id: counter.contactone_consecutive_button_presses
  • wait_for_trigger:
    • platform: state
      entity_id: sensor.contactone_buttoncount
      timeout: ‘3’
      continue_on_timeout: false
  • service: counter.increment
    target:
    entity_id: counter.contactone_consecutive_button_presses
  • wait_for_trigger:
    • platform: state
      entity_id: sensor.contactone_buttoncount
      timeout: ‘3’
      continue_on_timeout: false
  • service: counter.increment
    target:
    entity_id: counter.contactone_consecutive_button_presses
  • wait_for_trigger:
    • platform: state
      entity_id: sensor.contactone_buttoncount
      timeout: ‘3’
      continue_on_timeout: false
  • service: counter.increment
    target:
    entity_id: counter.contactone_consecutive_button_presses
  • delay:
    hours: 0
    minutes: 0
    seconds: 5
    milliseconds: 0
  • service: system_log.write
    data:
    message: ’ {{ (states(’‘sensor.contactone_buttoncount’’)) | int - trigger.from_state.state | int }}’
    mode: single
    ‘’’

The from and to attributes of the state trgger prevents the automation to be triggerd by unavailable states