PySwitchbot 2.7.0__tar.gz → 2.8.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.
Files changed (83) hide show
  1. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/PKG-INFO +9 -9
  2. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/README.md +8 -8
  3. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/pyproject.toml +1 -1
  4. pyswitchbot-2.8.0/switchbot/__version__.py +1 -0
  5. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/ceiling_light.py +47 -5
  6. pyswitchbot-2.7.0/switchbot/__version__.py +0 -1
  7. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/LICENSE +0 -0
  8. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/__init__.py +0 -0
  9. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parser.py +0 -0
  10. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/__init__.py +0 -0
  11. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/_sensor_th.py +0 -0
  12. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/air_purifier.py +0 -0
  13. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/art_frame.py +0 -0
  14. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/blind_tilt.py +0 -0
  15. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/bot.py +0 -0
  16. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/bulb.py +0 -0
  17. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/ceiling_light.py +0 -0
  18. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/climate_panel.py +0 -0
  19. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/contact.py +0 -0
  20. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/curtain.py +0 -0
  21. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/fan.py +0 -0
  22. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/hub2.py +0 -0
  23. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/hub3.py +0 -0
  24. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/hubmini_matter.py +0 -0
  25. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/humidifier.py +0 -0
  26. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/keypad.py +0 -0
  27. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/keypad_vision.py +0 -0
  28. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/leak.py +0 -0
  29. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/light_strip.py +0 -0
  30. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/lock.py +0 -0
  31. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/meter.py +0 -0
  32. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/motion.py +0 -0
  33. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/plug.py +0 -0
  34. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/presence_sensor.py +0 -0
  35. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/relay_switch.py +0 -0
  36. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/remote.py +0 -0
  37. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/roller_shade.py +0 -0
  38. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/smart_thermostat_radiator.py +0 -0
  39. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/vacuum.py +0 -0
  40. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/adv_parsers/weather_station.py +0 -0
  41. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/api_config.py +0 -0
  42. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/const/__init__.py +0 -0
  43. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/const/air_purifier.py +0 -0
  44. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/const/climate.py +0 -0
  45. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/const/evaporative_humidifier.py +0 -0
  46. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/const/fan.py +0 -0
  47. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/const/hub2.py +0 -0
  48. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/const/hub3.py +0 -0
  49. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/const/light.py +0 -0
  50. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/const/lock.py +0 -0
  51. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/const/presence_sensor.py +0 -0
  52. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/__init__.py +0 -0
  53. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/air_purifier.py +0 -0
  54. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/art_frame.py +0 -0
  55. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/base_cover.py +0 -0
  56. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/base_light.py +0 -0
  57. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/blind_tilt.py +0 -0
  58. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/bot.py +0 -0
  59. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/bulb.py +0 -0
  60. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/contact.py +0 -0
  61. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/curtain.py +0 -0
  62. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/device.py +0 -0
  63. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/evaporative_humidifier.py +0 -0
  64. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/fan.py +0 -0
  65. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/humidifier.py +0 -0
  66. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/keypad.py +0 -0
  67. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/keypad_vision.py +0 -0
  68. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/light_strip.py +0 -0
  69. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/lock.py +0 -0
  70. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/meter.py +0 -0
  71. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/meter_pro.py +0 -0
  72. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/motion.py +0 -0
  73. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/plug.py +0 -0
  74. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/relay_switch.py +0 -0
  75. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/roller_shade.py +0 -0
  76. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/smart_thermostat_radiator.py +0 -0
  77. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/universal_remote.py +0 -0
  78. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/devices/vacuum.py +0 -0
  79. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/discovery.py +0 -0
  80. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/enum.py +0 -0
  81. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/helpers.py +0 -0
  82. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/models.py +0 -0
  83. {pyswitchbot-2.7.0 → pyswitchbot-2.8.0}/switchbot/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PySwitchbot
3
- Version: 2.7.0
3
+ Version: 2.8.0
4
4
  Summary: A library to communicate with Switchbot
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -93,10 +93,10 @@ from switchbot.discovery import GetSwitchbotDevices
93
93
  from switchbot.devices import lock
94
94
  from switchbot.const import SwitchbotModel
95
95
 
96
- BLE_MAC="XX:XX:XX:XX:XX:XX" # The MAC of your lock
97
- KEY_ID="XX" # The key-ID of your encryption-key for your lock
98
- ENC_KEY="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # The encryption-key with key-ID "XX"
99
- LOCK_MODEL=SwitchbotModel.LOCK_PRO # Your lock model (here we use the Lock-Pro)
96
+ BLE_MAC = "XX:XX:XX:XX:XX:XX" # The MAC of your lock
97
+ KEY_ID = "XX" # The key-ID of your encryption-key for your lock
98
+ ENC_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # The encryption-key with key-ID "XX"
99
+ LOCK_MODEL = SwitchbotModel.LOCK_PRO # Your lock model (here we use the Lock-Pro)
100
100
 
101
101
 
102
102
  async def main():
@@ -117,10 +117,10 @@ from switchbot.discovery import GetSwitchbotDevices
117
117
  from switchbot.devices import lock
118
118
  from switchbot.const import SwitchbotModel
119
119
 
120
- BLE_MAC="XX:XX:XX:XX:XX:XX" # The MAC of your lock
121
- KEY_ID="XX" # The key-ID of your encryption-key for your lock
122
- ENC_KEY="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # The encryption-key with key-ID "XX"
123
- LOCK_MODEL=SwitchbotModel.LOCK_PRO # Your lock model (here we use the Lock-Pro)
120
+ BLE_MAC = "XX:XX:XX:XX:XX:XX" # The MAC of your lock
121
+ KEY_ID = "XX" # The key-ID of your encryption-key for your lock
122
+ ENC_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # The encryption-key with key-ID "XX"
123
+ LOCK_MODEL = SwitchbotModel.LOCK_PRO # Your lock model (here we use the Lock-Pro)
124
124
 
125
125
 
126
126
  async def main():
@@ -62,10 +62,10 @@ from switchbot.discovery import GetSwitchbotDevices
62
62
  from switchbot.devices import lock
63
63
  from switchbot.const import SwitchbotModel
64
64
 
65
- BLE_MAC="XX:XX:XX:XX:XX:XX" # The MAC of your lock
66
- KEY_ID="XX" # The key-ID of your encryption-key for your lock
67
- ENC_KEY="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # The encryption-key with key-ID "XX"
68
- LOCK_MODEL=SwitchbotModel.LOCK_PRO # Your lock model (here we use the Lock-Pro)
65
+ BLE_MAC = "XX:XX:XX:XX:XX:XX" # The MAC of your lock
66
+ KEY_ID = "XX" # The key-ID of your encryption-key for your lock
67
+ ENC_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # The encryption-key with key-ID "XX"
68
+ LOCK_MODEL = SwitchbotModel.LOCK_PRO # Your lock model (here we use the Lock-Pro)
69
69
 
70
70
 
71
71
  async def main():
@@ -86,10 +86,10 @@ from switchbot.discovery import GetSwitchbotDevices
86
86
  from switchbot.devices import lock
87
87
  from switchbot.const import SwitchbotModel
88
88
 
89
- BLE_MAC="XX:XX:XX:XX:XX:XX" # The MAC of your lock
90
- KEY_ID="XX" # The key-ID of your encryption-key for your lock
91
- ENC_KEY="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # The encryption-key with key-ID "XX"
92
- LOCK_MODEL=SwitchbotModel.LOCK_PRO # Your lock model (here we use the Lock-Pro)
89
+ BLE_MAC = "XX:XX:XX:XX:XX:XX" # The MAC of your lock
90
+ KEY_ID = "XX" # The key-ID of your encryption-key for your lock
91
+ ENC_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # The encryption-key with key-ID "XX"
92
+ LOCK_MODEL = SwitchbotModel.LOCK_PRO # Your lock model (here we use the Lock-Pro)
93
93
 
94
94
 
95
95
  async def main():
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "PySwitchbot"
3
- version = "2.7.0"
3
+ version = "2.8.0"
4
4
  description = "A library to communicate with Switchbot"
5
5
  authors = ["Daniel Hjelseth Hoyer"]
6
6
  license = "MIT"
@@ -0,0 +1 @@
1
+ __version__ = "2.8.0"
@@ -27,6 +27,7 @@ class SwitchbotCeilingLight(SwitchbotSequenceBaseLight):
27
27
  _turn_off_command = f"{CEILING_LIGHT_CONTROL_HEADER}02FF01FFFF"
28
28
  _set_brightness_command = f"{CEILING_LIGHT_CONTROL_HEADER}01FF01{{}}"
29
29
  _set_color_temp_command = f"{CEILING_LIGHT_CONTROL_HEADER}01FF01{{}}"
30
+ _set_night_light_command = f"{CEILING_LIGHT_CONTROL_HEADER}01{{}}01{{}}"
30
31
  _get_basic_info_command = ["5702", "570f5581"]
31
32
 
32
33
  @property
@@ -37,9 +38,8 @@ class SwitchbotCeilingLight(SwitchbotSequenceBaseLight):
37
38
  @property
38
39
  def color_mode(self) -> ColorMode:
39
40
  """Return the current color mode."""
40
- device_mode = CeilingLightColorMode(
41
- value if (value := self._get_adv_value("color_mode")) is not None else 10
42
- )
41
+ value = self._state.get("color_mode", self._get_adv_value("color_mode"))
42
+ device_mode = CeilingLightColorMode(value if value is not None else 10)
43
43
  return _CEILING_LIGHT_COLOR_MODE_MAP.get(device_mode, ColorMode.OFF)
44
44
 
45
45
  @update_after_operation
@@ -52,6 +52,40 @@ class SwitchbotCeilingLight(SwitchbotSequenceBaseLight):
52
52
  result = await self._send_command(self._set_brightness_command.format(hex_data))
53
53
  return self._check_command_result(result, 0, {1})
54
54
 
55
+ @update_after_operation
56
+ async def set_night_light(self, is_on: bool, brightness: int | None = None) -> bool:
57
+ """
58
+ Toggle night light color mode on or off.
59
+
60
+ Powers the light on and, unless `brightness` is given, resets
61
+ brightness to 20% (night) or 100% (otherwise) — mirroring the
62
+ official app's night light toggle.
63
+ """
64
+ color_mode = (
65
+ CeilingLightColorMode.NIGHT if is_on else CeilingLightColorMode.COLOR_TEMP
66
+ )
67
+ hex_mode = f"{color_mode.value:02X}"
68
+ if brightness is None:
69
+ # The app's night light toggle always pairs NIGHT with 20%
70
+ # brightness and COLOR_TEMP with 100%, carrying over the
71
+ # last-set color temp.
72
+ brightness = 20 if is_on else 100
73
+ else:
74
+ self._validate_brightness(brightness)
75
+ color_temp = self._state.get("cw", DEFAULT_COLOR_TEMP)
76
+ hex_data = f"{brightness:02X}{color_temp:04X}"
77
+ result = await self._send_command(
78
+ self._set_night_light_command.format(hex_mode, hex_data)
79
+ )
80
+ return self._check_command_result(result, 0, {1})
81
+
82
+ def is_night_light_on(self) -> bool | None:
83
+ """Return the cached night light color mode state."""
84
+ value = self._state.get("color_mode")
85
+ if value is None:
86
+ return None
87
+ return value == CeilingLightColorMode.NIGHT.value
88
+
55
89
  async def get_basic_info(self) -> dict[str, Any] | None:
56
90
  """Get device basic settings."""
57
91
  if not (
@@ -60,11 +94,19 @@ class SwitchbotCeilingLight(SwitchbotSequenceBaseLight):
60
94
  return None
61
95
  _version_info, _data = res
62
96
 
63
- self._state["cw"] = int.from_bytes(_data[3:5], "big")
97
+ color_temp = int.from_bytes(_data[3:5], "big")
98
+ if self.min_temp <= color_temp <= self.max_temp:
99
+ self._state["cw"] = color_temp
100
+ else:
101
+ self._state.setdefault("cw", DEFAULT_COLOR_TEMP)
102
+ # Cached in self._state (not the adv-data merge) because the
103
+ # advertisement parser hardcodes color_mode, which would otherwise
104
+ # clobber this value on the next advertisement.
105
+ self._state["color_mode"] = (_data[1] & 0b01000000) >> 6
64
106
 
65
107
  return {
66
108
  "isOn": bool(_data[1] & 0b10000000),
67
- "color_mode": (_data[1] & 0b01000000) >> 6,
109
+ "color_mode": self._state["color_mode"],
68
110
  "brightness": _data[2] & 0b01111111,
69
111
  "cw": self._state["cw"],
70
112
  "firmware": _version_info[2] / 10.0,
@@ -1 +0,0 @@
1
- __version__ = "2.7.0"
File without changes