PySwitchbot 2.4.0__tar.gz → 2.5.0__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-2.4.0 → pyswitchbot-2.5.0}/PKG-INFO +1 -1
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/pyproject.toml +1 -1
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/__init__.py +2 -0
- pyswitchbot-2.5.0/switchbot/__version__.py +1 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parser.py +13 -1
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/climate_panel.py +18 -0
- pyswitchbot-2.5.0/switchbot/adv_parsers/remote.py +42 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/const/__init__.py +1 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/const/fan.py +6 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/device.py +1 -0
- pyswitchbot-2.5.0/switchbot/devices/universal_remote.py +23 -0
- pyswitchbot-2.4.0/switchbot/__version__.py +0 -1
- pyswitchbot-2.4.0/switchbot/adv_parsers/remote.py +0 -23
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/LICENSE +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/README.md +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/__init__.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/_sensor_th.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/air_purifier.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/art_frame.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/blind_tilt.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/bot.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/bulb.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/ceiling_light.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/contact.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/curtain.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/fan.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/hub2.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/hub3.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/hubmini_matter.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/humidifier.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/keypad.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/keypad_vision.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/leak.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/light_strip.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/lock.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/meter.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/motion.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/plug.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/presence_sensor.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/relay_switch.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/roller_shade.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/smart_thermostat_radiator.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/vacuum.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/adv_parsers/weather_station.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/api_config.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/const/air_purifier.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/const/climate.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/const/evaporative_humidifier.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/const/hub2.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/const/hub3.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/const/light.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/const/lock.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/const/presence_sensor.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/__init__.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/air_purifier.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/art_frame.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/base_cover.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/base_light.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/blind_tilt.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/bot.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/bulb.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/ceiling_light.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/contact.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/curtain.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/evaporative_humidifier.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/fan.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/humidifier.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/keypad.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/keypad_vision.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/light_strip.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/lock.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/meter.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/meter_pro.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/motion.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/plug.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/relay_switch.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/roller_shade.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/smart_thermostat_radiator.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/devices/vacuum.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/discovery.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/enum.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/helpers.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/models.py +0 -0
- {pyswitchbot-2.4.0 → pyswitchbot-2.5.0}/switchbot/utils.py +0 -0
|
@@ -70,6 +70,7 @@ from .devices.relay_switch import (
|
|
|
70
70
|
)
|
|
71
71
|
from .devices.roller_shade import SwitchbotRollerShade
|
|
72
72
|
from .devices.smart_thermostat_radiator import SwitchbotSmartThermostatRadiator
|
|
73
|
+
from .devices.universal_remote import SwitchbotUniversalRemote
|
|
73
74
|
from .devices.vacuum import SwitchbotVacuum
|
|
74
75
|
from .discovery import GetSwitchbotDevices
|
|
75
76
|
from .models import SwitchBotAdvertisement
|
|
@@ -131,6 +132,7 @@ __all__ = [
|
|
|
131
132
|
"SwitchbotStandingFan",
|
|
132
133
|
"SwitchbotStripLight3",
|
|
133
134
|
"SwitchbotSupportedType",
|
|
135
|
+
"SwitchbotUniversalRemote",
|
|
134
136
|
"SwitchbotVacuum",
|
|
135
137
|
"VerticalOscillationAngle",
|
|
136
138
|
"close_stale_connections",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.5.0"
|
|
@@ -51,7 +51,7 @@ from .adv_parsers.relay_switch import (
|
|
|
51
51
|
process_relay_switch_2pm,
|
|
52
52
|
process_relay_switch_common_data,
|
|
53
53
|
)
|
|
54
|
-
from .adv_parsers.remote import process_woremote
|
|
54
|
+
from .adv_parsers.remote import process_woremote, process_wouniversal_remote
|
|
55
55
|
from .adv_parsers.roller_shade import process_worollershade
|
|
56
56
|
from .adv_parsers.smart_thermostat_radiator import process_smart_thermostat_radiator
|
|
57
57
|
from .adv_parsers.vacuum import process_vacuum, process_vacuum_k
|
|
@@ -402,6 +402,18 @@ SUPPORTED_TYPES: dict[str | bytes, SwitchbotSupportedType] = {
|
|
|
402
402
|
"func": process_woremote,
|
|
403
403
|
"manufacturer_id": 89,
|
|
404
404
|
},
|
|
405
|
+
"\x07": {
|
|
406
|
+
"modelName": SwitchbotModel.UNIVERSAL_REMOTE,
|
|
407
|
+
"modelFriendlyName": "Universal Remote",
|
|
408
|
+
"func": process_wouniversal_remote,
|
|
409
|
+
"manufacturer_id": 2409,
|
|
410
|
+
},
|
|
411
|
+
"'": {
|
|
412
|
+
"modelName": SwitchbotModel.UNIVERSAL_REMOTE,
|
|
413
|
+
"modelFriendlyName": "Universal Remote",
|
|
414
|
+
"func": process_wouniversal_remote,
|
|
415
|
+
"manufacturer_id": 2409,
|
|
416
|
+
},
|
|
405
417
|
",": {
|
|
406
418
|
"modelName": SwitchbotModel.ROLLER_SHADE,
|
|
407
419
|
"modelFriendlyName": "Roller Shade",
|
|
@@ -28,6 +28,18 @@ def process_climate_panel(
|
|
|
28
28
|
pir_state = bool(mfr_data[15] & 0x80)
|
|
29
29
|
is_light = ((mfr_data[15] >> 1) & 0x03) == 0x02
|
|
30
30
|
|
|
31
|
+
# Keystate bytes report physical ON/OFF button presses. For each byte,
|
|
32
|
+
# Bit[7:5] is the press mode (0: power-on init, 1: single, 2: double,
|
|
33
|
+
# 3: long) and Bit[4:0] is a 1-30 cyclic counter that resets to 1 when the
|
|
34
|
+
# mode changes. A value of 0x00 is the power-on state and must not trigger.
|
|
35
|
+
# The raw byte is also exposed so consumers can watch it for any change.
|
|
36
|
+
on_keystate = mfr_data[13]
|
|
37
|
+
off_keystate = mfr_data[14]
|
|
38
|
+
on_keystate_mode = (on_keystate >> 5) & 0x07
|
|
39
|
+
on_keystate_counter = on_keystate & 0x1F
|
|
40
|
+
off_keystate_mode = (off_keystate >> 5) & 0x07
|
|
41
|
+
off_keystate_counter = off_keystate & 0x1F
|
|
42
|
+
|
|
31
43
|
result = {
|
|
32
44
|
"sequence_number": seq_number,
|
|
33
45
|
"isOn": isOn,
|
|
@@ -38,6 +50,12 @@ def process_climate_panel(
|
|
|
38
50
|
"humidity_alarm": humidity_alarm,
|
|
39
51
|
"motion_detected": pir_state,
|
|
40
52
|
"is_light": is_light,
|
|
53
|
+
"on_keystate": on_keystate,
|
|
54
|
+
"off_keystate": off_keystate,
|
|
55
|
+
"on_keystate_mode": on_keystate_mode,
|
|
56
|
+
"on_keystate_counter": on_keystate_counter,
|
|
57
|
+
"off_keystate_mode": off_keystate_mode,
|
|
58
|
+
"off_keystate_counter": off_keystate_counter,
|
|
41
59
|
}
|
|
42
60
|
|
|
43
61
|
_LOGGER.debug(
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Remote adv parser."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
_LOGGER = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def process_woremote(
|
|
11
|
+
data: bytes | None, mfr_data: bytes | None
|
|
12
|
+
) -> dict[str, int | None]:
|
|
13
|
+
"""Process WoRemote adv data."""
|
|
14
|
+
if data is None or len(data) < 3:
|
|
15
|
+
return {
|
|
16
|
+
"battery": None,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
_LOGGER.debug("data: %s", data.hex())
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
"battery": data[2] & 0b01111111,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def process_wouniversal_remote(
|
|
27
|
+
data: bytes | None, mfr_data: bytes | None
|
|
28
|
+
) -> dict[str, bool | int | None]:
|
|
29
|
+
"""Process Universal Remote adv data."""
|
|
30
|
+
if mfr_data is None or len(mfr_data) < 8:
|
|
31
|
+
return {
|
|
32
|
+
"battery": None,
|
|
33
|
+
"charging": None,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_LOGGER.debug("mfr_data: %s", mfr_data.hex())
|
|
37
|
+
|
|
38
|
+
# mfr_data[7]: bit 7 is charging; bits 6-0 are battery percentage.
|
|
39
|
+
return {
|
|
40
|
+
"battery": mfr_data[7] & 0b01111111,
|
|
41
|
+
"charging": bool((mfr_data[7] >> 7) & 1),
|
|
42
|
+
}
|
|
@@ -82,6 +82,7 @@ class SwitchbotModel(StrEnum):
|
|
|
82
82
|
RELAY_SWITCH_1PM = "Relay Switch 1PM"
|
|
83
83
|
RELAY_SWITCH_1 = "Relay Switch 1"
|
|
84
84
|
REMOTE = "WoRemote"
|
|
85
|
+
UNIVERSAL_REMOTE = "WoUniversalRemote"
|
|
85
86
|
EVAPORATIVE_HUMIDIFIER = "Evaporative Humidifier"
|
|
86
87
|
ROLLER_SHADE = "Roller Shade"
|
|
87
88
|
HUBMINI_MATTER = "HubMini Matter"
|
|
@@ -74,6 +74,7 @@ API_MODEL_TO_ENUM: dict[str, SwitchbotModel] = {
|
|
|
74
74
|
"WoBlindTilt": SwitchbotModel.BLIND_TILT,
|
|
75
75
|
"WoIOSensor": SwitchbotModel.IO_METER, # Outdoor Meter
|
|
76
76
|
"WoButton": SwitchbotModel.REMOTE, # Remote button
|
|
77
|
+
"WoUniversalRemote": SwitchbotModel.UNIVERSAL_REMOTE, # Universal Remote
|
|
77
78
|
"WoLinkMini": SwitchbotModel.HUBMINI_MATTER, # Hub Mini
|
|
78
79
|
"WoFan2": SwitchbotModel.CIRCULATOR_FAN,
|
|
79
80
|
"WoHub2": SwitchbotModel.HUB2,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Library to handle connection with Switchbot Universal Remote."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from .device import SwitchbotDevice
|
|
9
|
+
|
|
10
|
+
_LOGGER = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SwitchbotUniversalRemote(SwitchbotDevice):
|
|
14
|
+
"""Representation of a Switchbot Universal Remote."""
|
|
15
|
+
|
|
16
|
+
async def get_basic_info(self) -> dict[str, Any] | None:
|
|
17
|
+
"""Get device basic settings."""
|
|
18
|
+
if not (_data := await self._get_basic_info()):
|
|
19
|
+
return None
|
|
20
|
+
return {
|
|
21
|
+
"battery": _data[1],
|
|
22
|
+
"charging": bool(_data[12]),
|
|
23
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.4.0"
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"""Remote adv parser."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import logging
|
|
6
|
-
|
|
7
|
-
_LOGGER = logging.getLogger(__name__)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def process_woremote(
|
|
11
|
-
data: bytes | None, mfr_data: bytes | None
|
|
12
|
-
) -> dict[str, int | None]:
|
|
13
|
-
"""Process WoRemote adv data."""
|
|
14
|
-
if data is None or len(data) < 3:
|
|
15
|
-
return {
|
|
16
|
-
"battery": None,
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
_LOGGER.debug("data: %s", data.hex())
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
"battery": data[2] & 0b01111111,
|
|
23
|
-
}
|
|
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
|