PySwitchbot 2.7.1__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.1 → pyswitchbot-2.8.0}/PKG-INFO +1 -1
  2. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/pyproject.toml +1 -1
  3. pyswitchbot-2.8.0/switchbot/__version__.py +1 -0
  4. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/ceiling_light.py +42 -4
  5. pyswitchbot-2.7.1/switchbot/__version__.py +0 -1
  6. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/LICENSE +0 -0
  7. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/README.md +0 -0
  8. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/__init__.py +0 -0
  9. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parser.py +0 -0
  10. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/__init__.py +0 -0
  11. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/_sensor_th.py +0 -0
  12. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/air_purifier.py +0 -0
  13. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/art_frame.py +0 -0
  14. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/blind_tilt.py +0 -0
  15. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/bot.py +0 -0
  16. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/bulb.py +0 -0
  17. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/ceiling_light.py +0 -0
  18. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/climate_panel.py +0 -0
  19. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/contact.py +0 -0
  20. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/curtain.py +0 -0
  21. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/fan.py +0 -0
  22. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/hub2.py +0 -0
  23. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/hub3.py +0 -0
  24. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/hubmini_matter.py +0 -0
  25. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/humidifier.py +0 -0
  26. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/keypad.py +0 -0
  27. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/keypad_vision.py +0 -0
  28. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/leak.py +0 -0
  29. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/light_strip.py +0 -0
  30. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/lock.py +0 -0
  31. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/meter.py +0 -0
  32. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/motion.py +0 -0
  33. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/plug.py +0 -0
  34. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/presence_sensor.py +0 -0
  35. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/relay_switch.py +0 -0
  36. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/remote.py +0 -0
  37. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/roller_shade.py +0 -0
  38. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/smart_thermostat_radiator.py +0 -0
  39. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/vacuum.py +0 -0
  40. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/adv_parsers/weather_station.py +0 -0
  41. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/api_config.py +0 -0
  42. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/const/__init__.py +0 -0
  43. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/const/air_purifier.py +0 -0
  44. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/const/climate.py +0 -0
  45. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/const/evaporative_humidifier.py +0 -0
  46. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/const/fan.py +0 -0
  47. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/const/hub2.py +0 -0
  48. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/const/hub3.py +0 -0
  49. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/const/light.py +0 -0
  50. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/const/lock.py +0 -0
  51. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/const/presence_sensor.py +0 -0
  52. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/__init__.py +0 -0
  53. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/air_purifier.py +0 -0
  54. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/art_frame.py +0 -0
  55. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/base_cover.py +0 -0
  56. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/base_light.py +0 -0
  57. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/blind_tilt.py +0 -0
  58. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/bot.py +0 -0
  59. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/bulb.py +0 -0
  60. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/contact.py +0 -0
  61. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/curtain.py +0 -0
  62. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/device.py +0 -0
  63. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/evaporative_humidifier.py +0 -0
  64. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/fan.py +0 -0
  65. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/humidifier.py +0 -0
  66. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/keypad.py +0 -0
  67. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/keypad_vision.py +0 -0
  68. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/light_strip.py +0 -0
  69. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/lock.py +0 -0
  70. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/meter.py +0 -0
  71. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/meter_pro.py +0 -0
  72. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/motion.py +0 -0
  73. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/plug.py +0 -0
  74. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/relay_switch.py +0 -0
  75. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/roller_shade.py +0 -0
  76. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/smart_thermostat_radiator.py +0 -0
  77. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/universal_remote.py +0 -0
  78. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/devices/vacuum.py +0 -0
  79. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/discovery.py +0 -0
  80. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/enum.py +0 -0
  81. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/helpers.py +0 -0
  82. {pyswitchbot-2.7.1 → pyswitchbot-2.8.0}/switchbot/models.py +0 -0
  83. {pyswitchbot-2.7.1 → 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.1
3
+ Version: 2.8.0
4
4
  Summary: A library to communicate with Switchbot
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "PySwitchbot"
3
- version = "2.7.1"
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 (
@@ -65,10 +99,14 @@ class SwitchbotCeilingLight(SwitchbotSequenceBaseLight):
65
99
  self._state["cw"] = color_temp
66
100
  else:
67
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
68
106
 
69
107
  return {
70
108
  "isOn": bool(_data[1] & 0b10000000),
71
- "color_mode": (_data[1] & 0b01000000) >> 6,
109
+ "color_mode": self._state["color_mode"],
72
110
  "brightness": _data[2] & 0b01111111,
73
111
  "cw": self._state["cw"],
74
112
  "firmware": _version_info[2] / 10.0,
@@ -1 +0,0 @@
1
- __version__ = "2.7.1"
File without changes
File without changes