PySwitchbot 0.68.0__tar.gz → 0.68.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/PKG-INFO +1 -1
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/PySwitchbot.egg-info/PKG-INFO +1 -1
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/setup.py +1 -1
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/device.py +6 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/lock.py +6 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_encrypted_device.py +19 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_lock.py +29 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/LICENSE +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/MANIFEST.in +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/PySwitchbot.egg-info/SOURCES.txt +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/PySwitchbot.egg-info/dependency_links.txt +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/PySwitchbot.egg-info/requires.txt +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/PySwitchbot.egg-info/top_level.txt +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/README.md +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/pyproject.toml +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/setup.cfg +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/__init__.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parser.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/__init__.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/air_purifier.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/blind_tilt.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/bot.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/bulb.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/ceiling_light.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/contact.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/curtain.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/fan.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/hub2.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/hub3.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/hubmini_matter.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/humidifier.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/keypad.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/leak.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/light_strip.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/lock.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/meter.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/motion.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/plug.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/relay_switch.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/remote.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/roller_shade.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/adv_parsers/vacuum.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/api_config.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/const/__init__.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/const/air_purifier.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/const/evaporative_humidifier.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/const/fan.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/const/hub2.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/const/hub3.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/const/light.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/const/lock.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/__init__.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/air_purifier.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/base_cover.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/base_light.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/blind_tilt.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/bot.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/bulb.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/ceiling_light.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/contact.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/curtain.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/evaporative_humidifier.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/fan.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/humidifier.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/keypad.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/light_strip.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/meter.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/motion.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/plug.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/relay_switch.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/roller_shade.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/devices/vacuum.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/discovery.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/enum.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/helpers.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/switchbot/models.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_adv_parser.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_air_purifier.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_base_cover.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_blind_tilt.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_bulb.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_ceiling_light.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_colormode_imports.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_curtain.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_evaporative_humidifier.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_fan.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_helpers.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_hub2.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_hub3.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_relay_switch.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_roller_shade.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_strip_light.py +0 -0
- {pyswitchbot-0.68.0 → pyswitchbot-0.68.1}/tests/test_vacuum.py +0 -0
|
@@ -955,6 +955,12 @@ class SwitchbotEncryptedDevice(SwitchbotDevice):
|
|
|
955
955
|
if len(data) == 0:
|
|
956
956
|
return b""
|
|
957
957
|
if self._iv is None:
|
|
958
|
+
if self._expected_disconnect:
|
|
959
|
+
_LOGGER.debug(
|
|
960
|
+
"%s: Cannot decrypt, IV is None during expected disconnect",
|
|
961
|
+
self.name,
|
|
962
|
+
)
|
|
963
|
+
return b""
|
|
958
964
|
raise RuntimeError("Cannot decrypt: IV is None")
|
|
959
965
|
decryptor = self._get_cipher().decryptor()
|
|
960
966
|
return decryptor.update(data) + decryptor.finalize()
|
|
@@ -214,6 +214,12 @@ class SwitchbotLock(SwitchbotSequenceDevice, SwitchbotEncryptedDevice):
|
|
|
214
214
|
|
|
215
215
|
def _notification_handler(self, _sender: int, data: bytearray) -> None:
|
|
216
216
|
if self._notifications_enabled and self._check_command_result(data, 0, {0xF}):
|
|
217
|
+
if self._expected_disconnect:
|
|
218
|
+
_LOGGER.debug(
|
|
219
|
+
"%s: Ignoring lock notification during expected disconnect",
|
|
220
|
+
self.name,
|
|
221
|
+
)
|
|
222
|
+
return
|
|
217
223
|
self._update_lock_status(data)
|
|
218
224
|
else:
|
|
219
225
|
super()._notification_handler(_sender, data)
|
|
@@ -365,3 +365,22 @@ async def test_empty_data_encryption_decryption() -> None:
|
|
|
365
365
|
# Test empty decryption
|
|
366
366
|
decrypted = device._decrypt(bytearray())
|
|
367
367
|
assert decrypted == b""
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
@pytest.mark.asyncio
|
|
371
|
+
async def test_decrypt_with_none_iv_during_disconnect() -> None:
|
|
372
|
+
"""Test that decryption returns empty bytes when IV is None during expected disconnect."""
|
|
373
|
+
device = create_encrypted_device()
|
|
374
|
+
|
|
375
|
+
# Simulate disconnection in progress
|
|
376
|
+
device._expected_disconnect = True
|
|
377
|
+
device._iv = None
|
|
378
|
+
|
|
379
|
+
# Should return empty bytes instead of raising
|
|
380
|
+
result = device._decrypt(bytearray(b"encrypted_data"))
|
|
381
|
+
assert result == b""
|
|
382
|
+
|
|
383
|
+
# Verify it still raises when not disconnecting
|
|
384
|
+
device._expected_disconnect = False
|
|
385
|
+
with pytest.raises(RuntimeError, match="Cannot decrypt: IV is None"):
|
|
386
|
+
device._decrypt(bytearray(b"encrypted_data"))
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from unittest.mock import AsyncMock, Mock, patch
|
|
2
3
|
|
|
3
4
|
import pytest
|
|
@@ -478,6 +479,34 @@ def test_notification_handler_not_enabled(model: str):
|
|
|
478
479
|
mock_super.assert_called_once()
|
|
479
480
|
|
|
480
481
|
|
|
482
|
+
@pytest.mark.parametrize(
|
|
483
|
+
"model",
|
|
484
|
+
[
|
|
485
|
+
SwitchbotModel.LOCK,
|
|
486
|
+
SwitchbotModel.LOCK_LITE,
|
|
487
|
+
SwitchbotModel.LOCK_PRO,
|
|
488
|
+
SwitchbotModel.LOCK_ULTRA,
|
|
489
|
+
],
|
|
490
|
+
)
|
|
491
|
+
def test_notification_handler_during_disconnect(
|
|
492
|
+
model: str, caplog: pytest.LogCaptureFixture
|
|
493
|
+
) -> None:
|
|
494
|
+
"""Test _notification_handler during expected disconnect."""
|
|
495
|
+
device = create_device_for_command_testing(model)
|
|
496
|
+
device._notifications_enabled = True
|
|
497
|
+
device._expected_disconnect = True
|
|
498
|
+
data = bytearray(b"\x0f\x00\x00\x00\x80\x00\x00\x00\x00\x00")
|
|
499
|
+
with (
|
|
500
|
+
patch.object(device, "_update_lock_status") as mock_update,
|
|
501
|
+
caplog.at_level(logging.DEBUG),
|
|
502
|
+
):
|
|
503
|
+
device._notification_handler(0, data)
|
|
504
|
+
# Should not update lock status during disconnect
|
|
505
|
+
mock_update.assert_not_called()
|
|
506
|
+
# Should log debug message
|
|
507
|
+
assert "Ignoring lock notification during expected disconnect" in caplog.text
|
|
508
|
+
|
|
509
|
+
|
|
481
510
|
@pytest.mark.parametrize(
|
|
482
511
|
"model",
|
|
483
512
|
[
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|