fphammerle / switchbot-mqtt

MQTT client controlling SwitchBot button automators & curtain motors, compatible with Home Assistant :house_with_garden: 🐳
https://pypi.org/project/switchbot-mqtt/
GNU General Public License v3.0
51 stars 9 forks source link

Switchbot Curtain 3, crashes when started with --fetch-device-info #232

Open NiekD opened 3 months ago

NiekD commented 3 months ago

I am using a couple of Switchbot Curtain 3 devices and I would like to get battery- and position information. Unfortunately switchbot-mqtt (latest version) crashes immediately on the first attempt to do so.

Although I don't understand much of the Switchbot API yet, I noticed that the protocols for Curtain 2 and Curtain 3 are different, especially with respect to the device info. I guess that this is the reason for the crashes. If switchbot-mqtt is started without --fetch-device-info, it works well with my Curtain 3 devices.

@fphammerle, Is it possible to add support for Curtain 3 device info?

fphammerle commented 3 months ago

Does the log contain any information before switchbot-mqtt crashes?

NiekD commented 1 month ago

@fphammerle, sorry that it took so long for me to respond. This is the traceback of the crash that occurs when switcbot-mqtt is run with --fetch-device-info and a command is sent (CLOSE in this case). Apparently _switchbot_device_data is not properly being created with a Switchbot Curtain3. Possibly because there is a difference between the protocols of Curtain2 and Curtain3 with respect to the device info?

Traceback:

Successfully sent command to Switchbot (MAC: fc:85:6b:9b:77:03) switchbot curtain fc:85:6b:9b:77:03 closing Traceback (most recent call last): File "/root/.local/bin/switchbot-mqtt", line 10, in sys.exit(_main()) File "/root/.local/lib/python3.7/site-packages/switchbot_mqtt/_cli.py", line 169, in _main or bool(os.environ.get("FETCH_DEVICE_INFO")), File "/root/.local/lib/python3.7/site-packages/switchbot_mqtt/init.py", line 105, in _run mqtt_client.loop_forever() File "/usr/local/lib/python3.7/dist-packages/paho/mqtt/client.py", line 1756, in loop_forever rc = self._loop(timeout) File "/usr/local/lib/python3.7/dist-packages/paho/mqtt/client.py", line 1164, in _loop rc = self.loop_read() File "/usr/local/lib/python3.7/dist-packages/paho/mqtt/client.py", line 1556, in loop_read rc = self._packet_read() File "/usr/local/lib/python3.7/dist-packages/paho/mqtt/client.py", line 2439, in _packet_read rc = self._packet_handle() File "/usr/local/lib/python3.7/dist-packages/paho/mqtt/client.py", line 3033, in _packet_handle return self._handle_publish() File "/usr/local/lib/python3.7/dist-packages/paho/mqtt/client.py", line 3327, in _handle_publish self._handle_on_message(message) File "/usr/local/lib/python3.7/dist-packages/paho/mqtt/client.py", line 3556, in _handle_on_message callback(self, self._userdata, message) File "/root/.local/lib/python3.7/site-packages/switchbot_mqtt/_actors/base.py", line 239, in _mqtt_command_callback mqtt_topic_prefix=userdata.mqtt_topic_prefix, File "/root/.local/lib/python3.7/site-packages/switchbot_mqtt/_actors/init.py", line 249, in execute_command report_position=report_position, File "/root/.local/lib/python3.7/site-packages/switchbot_mqtt/_actors/init.py", line 184, in _update_and_report_device_info super()._update_and_report_device_info(mqtt_client, mqtt_topic_prefix) File "/root/.local/lib/python3.7/site-packages/switchbot_mqtt/_actors/base.py", line 151, in _update_and_report_device_info mqtt_client=mqtt_client, mqtt_topic_prefix=mqtt_topic_prefix File "/root/.local/lib/python3.7/site-packages/switchbot_mqtt/_actors/base.py", line 142, in _report_battery_level payload=str(self._get_device().get_battery_percent()).encode(), File "/root/.local/lib/python3.7/site-packages/switchbot/init.py", line 308, in get_battery_percent return self._switchbot_device_data["data"]["battery"] KeyError: 'data'

fphammerle commented 1 month ago

Could you try upgrading the pySwitchbot library and check if the issue persists?

NiekD commented 1 month ago

I was running switchbot-mqtt version 3.3.1 with pySwitchbot version 0.12.0. and was unable to ugrade pySwitchbot to a newer version. It appears that upgrading to a higher version of pySwitchbot (using pip install) is only possible with switchbot-mqtt version 4.0.0a0. I did a quick check with Rasperry Pi Os Bullseye, Python 3.9.2. and switchbot-mqtt 4.4.0a0 and got pySwitchbot 0.40.1.

I know4.0.0a0 is a pre-release, and you may be interested in my findings:

It is working well when used in a normal way (including the device info).

I see one (reproducible) crash that occurs when OPEN/CLOSE commands are being sent quickly after one another:

  File "/usr/local/lib/python3.9/dist-packages/switchbot/devices/curtain.py", line 99, in get_basic_info
    _position = max(min(_data[6], 100), 0)
IndexError: bytearray index out of range
fphammerle commented 1 month ago

Thanks for your report! Great to hear that switchbot-mqtt version 4.0.0a0 worked.

Unfortunately, I am unable to investigate the "IndexError: bytearray index out of range" error as I do not own a switchbot curtain device. Might alrighty be fixed in a newer version of pySwitchbot.

NiekD commented 1 month ago

I ran into another issue, that hopefully can be solved without having a curtain device at hand.

The issue is, that when the bluetooth signal is disturbed, the switchbot devices tend to disconnect sometimes. If that happens, apparently the bleak_retry_connector tries to reestablish the connection several times. When all attempts fail, it gives up and raises an error.

WoCtn3 (F3:3A:35:29:20:92): Device unexpectedly disconnected; RSSI: -63
WoCtn3 (F3:3A:35:29:20:92): Device unexpectedly disconnected; RSSI: -63
WoCtn3 (F3:3A:35:29:20:92): Device unexpectedly disconnected; RSSI: -63
WoCtn3 (F3:3A:35:29:20:92): Device unexpectedly disconnected; RSSI: -63
WoCtn3 (F3:3A:35:29:20:92): Device unexpectedly disconnected; RSSI: -63
WoCtn3 (F3:3A:35:29:20:92): communication failed; Stopping trying; RSSI: -63
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/bleak_retry_connector/__init__.py", line 343, in establish_connection
    await client.connect(
  File "/usr/local/lib/python3.9/dist-packages/bleak/__init__.py", line 605, in connect
    return await self._backend.connect(**kwargs)
  File "/usr/local/lib/python3.9/dist-packages/bleak/backends/bluezdbus/client.py", line 249, in connect
    assert_reply(reply)
  File "/usr/local/lib/python3.9/dist-packages/bleak/backends/bluezdbus/utils.py", line 22, in assert_reply
    raise BleakDBusError(reply.error_name, reply.body)
bleak.exc.BleakDBusError: [org.bluez.Error.Failed] Software caused connection abort

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/switchbot/devices/device.py", line 180, in _send_command
    return await self._send_command_locked(key, command)
  File "/usr/local/lib/python3.9/dist-packages/switchbot/devices/device.py", line 414, in _send_command_locked
    await self._ensure_connected()
  File "/usr/local/lib/python3.9/dist-packages/switchbot/devices/device.py", line 274, in _ensure_connected
    client: BleakClientWithServiceCache = await establish_connection(
  File "/usr/local/lib/python3.9/dist-packages/bleak_retry_connector/__init__.py", line 420, in establish_connection
    _raise_if_needed(name, device.address, exc)
  File "/usr/local/lib/python3.9/dist-packages/bleak_retry_connector/__init__.py", line 319, in _raise_if_needed
    raise BleakConnectionError(msg) from exc
bleak_retry_connector.BleakConnectionError: WoCtn3 (F3:3A:35:29:20:92) - F3:3A:35:29:20:92: Failed to connect: [org.bluez.Error.Failed] Software caused connection abort
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/bleak_retry_connector/__init__.py", line 343, in establish_connection
    await client.connect(
  File "/usr/local/lib/python3.9/dist-packages/bleak/__init__.py", line 605, in connect
    return await self._backend.connect(**kwargs)
  File "/usr/local/lib/python3.9/dist-packages/bleak/backends/bluezdbus/client.py", line 249, in connect
    assert_reply(reply)
  File "/usr/local/lib/python3.9/dist-packages/bleak/backends/bluezdbus/utils.py", line 22, in assert_reply
    raise BleakDBusError(reply.error_name, reply.body)
bleak.exc.BleakDBusError: [org.bluez.Error.Failed] Software caused connection abort

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/bin/switchbot-mqtt", line 8, in <module>
    sys.exit(_main())
  File "/usr/local/lib/python3.9/dist-packages/switchbot_mqtt/_cli.py", line 142, in _main
    asyncio.run(
  File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/usr/local/lib/python3.9/dist-packages/switchbot_mqtt/__init__.py", line 128, in _run
    await _listen(
  File "/usr/local/lib/python3.9/dist-packages/switchbot_mqtt/__init__.py", line 55, in _listen
    await callback(
  File "/usr/local/lib/python3.9/dist-packages/switchbot_mqtt/_actors/base.py", line 178, in _mqtt_update_device_info_callback
    await actor._update_and_report_device_info(
  File "/usr/local/lib/python3.9/dist-packages/switchbot_mqtt/_actors/__init__.py", line 193, in _update_and_report_device_info
    await super()._update_and_report_device_info(mqtt_client, mqtt_topic_prefix)
  File "/usr/local/lib/python3.9/dist-packages/switchbot_mqtt/_actors/base.py", line 105, in _update_and_report_device_info
    self._basic_device_info = await self._get_device().get_basic_info()
  File "/usr/local/lib/python3.9/dist-packages/switchbot/devices/curtain.py", line 96, in get_basic_info
    if not (_data := await self._get_basic_info()):
  File "/usr/local/lib/python3.9/dist-packages/switchbot/devices/device.py", line 529, in _get_basic_info
    _data = await self._send_command(
  File "/usr/local/lib/python3.9/dist-packages/switchbot/devices/device.py", line 180, in _send_command
    return await self._send_command_locked(key, command)
  File "/usr/local/lib/python3.9/dist-packages/switchbot/devices/device.py", line 414, in _send_command_locked
    await self._ensure_connected()
  File "/usr/local/lib/python3.9/dist-packages/switchbot/devices/device.py", line 274, in _ensure_connected
    client: BleakClientWithServiceCache = await establish_connection(
  File "/usr/local/lib/python3.9/dist-packages/bleak_retry_connector/__init__.py", line 420, in establish_connection
    _raise_if_needed(name, device.address, exc)
  File "/usr/local/lib/python3.9/dist-packages/bleak_retry_connector/__init__.py", line 319, in _raise_if_needed
    raise BleakConnectionError(msg) from exc
bleak_retry_connector.BleakConnectionError: WoCtn3 (F3:3A:35:29:20:92) - F3:3A:35:29:20:92: Failed to connect: [org.bluez.Error.Failed] Software caused connection abort

This error is not handled by switchbot-mqtt, so the program crashes.

Bluetooth connection failures can and will occur every now and then and imho switchbot-mqtt should keep running then. As it is now, I need to start switchbot-mqtt several times per day, because of these crashes, which is a nuisance, especally when I am not at home.

I asume that the type of switchbot device is not relevant for this issue, and as said, I hope this is something that can be solved, without having a curtain device.