wyzeapy 0.5.31__tar.gz → 0.5.31rc1__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 (80) hide show
  1. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/workflows/auto-assign.yml +1 -4
  2. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/workflows/codeql.yml +7 -13
  3. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/workflows/lint.yml +1 -4
  4. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/workflows/pre-release.yml +1 -4
  5. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/workflows/publish.yml +1 -4
  6. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/workflows/test.yml +1 -4
  7. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/PKG-INFO +2 -2
  8. wyzeapy-0.5.31rc1/bin/act +0 -0
  9. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/pyproject.toml +2 -2
  10. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/bulb_service.py +4 -25
  11. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/camera_service.py +11 -3
  12. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/utils.py +4 -14
  13. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_camera_service.py +57 -0
  14. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/uv.lock +6 -6
  15. wyzeapy-0.5.31/.github/workflows/auto-approve.yml +0 -19
  16. wyzeapy-0.5.31/.github/workflows/scorecard.yml +0 -78
  17. wyzeapy-0.5.31/.pre-commit-config.yaml +0 -18
  18. wyzeapy-0.5.31/SECURITY.md +0 -79
  19. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.coveragerc +0 -0
  20. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/CODEOWNERS +0 -0
  21. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/dependabot.yml +0 -0
  22. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.gitignore +0 -0
  23. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.reuse/dep5 +0 -0
  24. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/CHANGELOG.md +0 -0
  25. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/LICENSES/GPL-3.0-only.txt +0 -0
  26. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/README.md +0 -0
  27. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/index.html +0 -0
  28. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/search.js +0 -0
  29. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/const.html +0 -0
  30. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/crypto.html +0 -0
  31. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/exceptions.html +0 -0
  32. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/payload_factory.html +0 -0
  33. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/base_service.html +0 -0
  34. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/bulb_service.html +0 -0
  35. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/camera_service.html +0 -0
  36. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/hms_service.html +0 -0
  37. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/lock_service.html +0 -0
  38. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/sensor_service.html +0 -0
  39. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/switch_service.html +0 -0
  40. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/thermostat_service.html +0 -0
  41. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/update_manager.html +0 -0
  42. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/wall_switch_service.html +0 -0
  43. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services.html +0 -0
  44. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/types.html +0 -0
  45. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/utils.html +0 -0
  46. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/wyze_auth_lib.html +0 -0
  47. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy.html +0 -0
  48. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/scripts/create_pre_release.sh +0 -0
  49. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/__init__.py +0 -0
  50. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/const.py +0 -0
  51. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/crypto.py +0 -0
  52. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/exceptions.py +0 -0
  53. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/payload_factory.py +0 -0
  54. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/__init__.py +0 -0
  55. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/base_service.py +0 -0
  56. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/hms_service.py +0 -0
  57. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/irrigation_service.py +0 -0
  58. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/lock_service.py +0 -0
  59. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/sensor_service.py +0 -0
  60. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/switch_service.py +0 -0
  61. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/thermostat_service.py +0 -0
  62. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/update_manager.py +0 -0
  63. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/wall_switch_service.py +0 -0
  64. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/tests/test_irrigation_service.py +0 -0
  65. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/types.py +0 -0
  66. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/wyze_auth_lib.py +0 -0
  67. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/__init__.py +0 -0
  68. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_bulb_service.py +0 -0
  69. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_hms_service.py +0 -0
  70. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_lock_service.py +0 -0
  71. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_payload_factory.py +0 -0
  72. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_sensor_service.py +0 -0
  73. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_switch_service.py +0 -0
  74. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_thermostat_service.py +0 -0
  75. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_types.py +0 -0
  76. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_update_manager.py +0 -0
  77. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_utils.py +0 -0
  78. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_wall_switch_service.py +0 -0
  79. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_wyze_auth_lib.py +0 -0
  80. {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_wyzeapy.py +0 -0
@@ -4,9 +4,6 @@ on:
4
4
  issues:
5
5
  types: [opened]
6
6
 
7
- permissions:
8
- contents: read
9
-
10
7
  jobs:
11
8
  assign:
12
9
  runs-on: ubuntu-latest
@@ -14,7 +11,7 @@ jobs:
14
11
  issues: write
15
12
  steps:
16
13
  - name: Assign issue to SecKatie
17
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
14
+ uses: actions/github-script@v7
18
15
  with:
19
16
  script: |
20
17
  await github.rest.issues.addAssignees({
@@ -11,9 +11,6 @@ on:
11
11
  schedule:
12
12
  - cron: '0 0 * * *'
13
13
 
14
- permissions:
15
- contents: read
16
-
17
14
  jobs:
18
15
  analyze:
19
16
  name: Analyze
@@ -31,23 +28,21 @@ jobs:
31
28
 
32
29
  strategy:
33
30
  fail-fast: false
34
- matrix:
35
- language: [ 'python' ]
31
+
36
32
 
37
33
  steps:
38
34
  - name: Checkout repository
39
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
35
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v3
40
36
 
41
37
  # Initializes the CodeQL tools for scanning.
42
38
  - name: Initialize CodeQL
43
- uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4
44
- with:
45
- languages: ${{ matrix.language }}
39
+ uses: github/codeql-action/init@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v3
40
+
46
41
 
47
42
  # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
48
43
  # If this step fails, then you should remove it and run the build manually (see below)
49
44
  - name: Autobuild
50
- uses: github/codeql-action/autobuild@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4
45
+ uses: github/codeql-action/autobuild@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v3
51
46
 
52
47
  # Command-line programs to run using the OS shell.
53
48
  # See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -60,6 +55,5 @@ jobs:
60
55
  # ./location_of_script_within_repo/buildscript.sh
61
56
 
62
57
  - name: Perform CodeQL Analysis
63
- uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4
64
- with:
65
- category: "/language:${{matrix.language}}"
58
+ uses: github/codeql-action/analyze@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v3
59
+
@@ -6,9 +6,6 @@ on:
6
6
  pull_request:
7
7
  branches: [ main, develop ]
8
8
 
9
- permissions:
10
- contents: read
11
-
12
9
  jobs:
13
10
  lint:
14
11
  runs-on: ubuntu-latest
@@ -22,7 +19,7 @@ jobs:
22
19
  python-version: '3.11'
23
20
 
24
21
  - name: Install uv
25
- uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7.2.0
22
+ uses: astral-sh/setup-uv@ed21f2f24f8dd64503750218de024bcf64c7250a # v7.1.5
26
23
 
27
24
  - name: Install dependencies
28
25
  run: |
@@ -5,9 +5,6 @@ on:
5
5
  branches:
6
6
  - release/*
7
7
 
8
- permissions:
9
- contents: read
10
-
11
8
  jobs:
12
9
  test-publish:
13
10
  runs-on: ubuntu-latest
@@ -20,7 +17,7 @@ jobs:
20
17
  python-version: "3.12"
21
18
 
22
19
  - name: Install uv
23
- uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v4
20
+ uses: astral-sh/setup-uv@ed21f2f24f8dd64503750218de024bcf64c7250a # v4
24
21
  with:
25
22
  enable-cache: true
26
23
 
@@ -4,9 +4,6 @@ on:
4
4
  release:
5
5
  types: [published]
6
6
 
7
- permissions:
8
- contents: read
9
-
10
7
  jobs:
11
8
  publish:
12
9
  runs-on: ubuntu-latest
@@ -22,7 +19,7 @@ jobs:
22
19
  python-version: "3.12"
23
20
 
24
21
  - name: Install uv
25
- uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v4
22
+ uses: astral-sh/setup-uv@ed21f2f24f8dd64503750218de024bcf64c7250a # v4
26
23
  with:
27
24
  enable-cache: true
28
25
 
@@ -6,9 +6,6 @@ on:
6
6
  pull_request:
7
7
  branches: [ main, develop ]
8
8
 
9
- permissions:
10
- contents: read
11
-
12
9
  jobs:
13
10
  test:
14
11
  runs-on: ubuntu-latest
@@ -25,7 +22,7 @@ jobs:
25
22
  python-version: ${{ matrix.python-version }}
26
23
 
27
24
  - name: Install uv
28
- uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7.2.0
25
+ uses: astral-sh/setup-uv@ed21f2f24f8dd64503750218de024bcf64c7250a # v7.1.5
29
26
 
30
27
  - name: Install dependencies
31
28
  run: |
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wyzeapy
3
- Version: 0.5.31
3
+ Version: 0.5.31rc1
4
4
  Summary: A library for interacting with Wyze devices
5
5
  Author-email: Katie Mulliken <katie@mulliken.net>
6
6
  License: GPL-3.0-only
7
7
  Requires-Python: >=3.11.0
8
- Requires-Dist: aiodns<5.0.0,>=3.2.0
8
+ Requires-Dist: aiodns<4.0.0,>=3.2.0
9
9
  Requires-Dist: aiohttp<4.0.0,>=3.11.12
10
10
  Requires-Dist: pycryptodome<4.0.0,>=3.21.0
11
11
  Provides-Extra: dev
Binary file
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "wyzeapy"
3
- version = "0.5.31"
3
+ version = "0.5.31pre1"
4
4
  description = "A library for interacting with Wyze devices"
5
5
  authors = [
6
6
  { name = "Katie Mulliken", email = "katie@mulliken.net" },
@@ -9,7 +9,7 @@ license = { text = "GPL-3.0-only" }
9
9
  requires-python = ">=3.11.0"
10
10
  dependencies = [
11
11
  "aiohttp>=3.11.12,<4.0.0",
12
- "aiodns>=3.2.0,<5.0.0",
12
+ "aiodns>=3.2.0,<4.0.0",
13
13
  "pycryptodome>=3.21.0,<4.0.0",
14
14
  ]
15
15
 
@@ -15,20 +15,7 @@ _LOGGER = logging.getLogger(__name__)
15
15
 
16
16
 
17
17
  class Bulb(Device):
18
- """Bulb class for interacting with Wyze bulbs.
19
-
20
- Note: When created via get_bulbs(), bulb properties (brightness, color,
21
- color_temp, on) are initialized with default values. Call
22
- BulbService.update(bulb) to fetch the actual current values from the
23
- Wyze API.
24
-
25
- Example:
26
- bulb_service = await client.bulb_service
27
- bulbs = await bulb_service.get_bulbs()
28
- for bulb in bulbs:
29
- bulb = await bulb_service.update(bulb) # Fetches actual values
30
- print(f"Brightness: {bulb.brightness}, Color: {bulb.color}")
31
- """
18
+ """Bulb class for interacting with Wyze bulbs."""
32
19
 
33
20
  _brightness: int = 0
34
21
  _color_temp: int = 1800
@@ -103,14 +90,10 @@ class BulbService(BaseService):
103
90
  """Bulb service for interacting with Wyze bulbs."""
104
91
 
105
92
  async def update(self, bulb: Bulb) -> Bulb:
106
- """Fetch and update the bulb's current state from the Wyze API.
107
-
108
- This method retrieves the actual values for brightness, color,
109
- color_temp, on/off state, and other properties from the Wyze API.
110
- Must be called after get_bulbs() to get accurate property values.
93
+ """Update the bulb object with the latest device parameters.
111
94
 
112
95
  :param bulb: Bulb object to update
113
- :return: Updated bulb object with current property values
96
+ :return: Updated bulb object
114
97
  """
115
98
  # Get updated device_params
116
99
  async with BaseService._update_lock:
@@ -148,11 +131,7 @@ class BulbService(BaseService):
148
131
  async def get_bulbs(self) -> List[Bulb]:
149
132
  """Get a list of all bulbs.
150
133
 
151
- Note: Returned bulbs have default property values (brightness=0,
152
- color="000000", etc.). Call update(bulb) on each bulb to fetch
153
- the actual current values from the Wyze API.
154
-
155
- :return: List of Bulb objects with default property values
134
+ :return: List of Bulb objects
156
135
  """
157
136
  if self._devices is None:
158
137
  self._devices = await self.get_object_list()
@@ -88,8 +88,10 @@ class CameraService(BaseService):
88
88
  if property is PropertyIDs.CAMERA_SIREN:
89
89
  camera.siren = value == "1"
90
90
  if property is PropertyIDs.ACCESSORY:
91
+ # Bulb Cam (HL_BC): '1' = ON, '2' = OFF
92
+ # Other cameras with accessories: same logic
91
93
  camera.floodlight = value == "1"
92
- if camera.device_params["dongle_product_model"] == "HL_CGDC":
94
+ if camera.device_params.get("dongle_product_model") == "HL_CGDC":
93
95
  camera.garage = (
94
96
  value == "1"
95
97
  ) # 1 = open, 2 = closed by automation or smart platform (Alexa, Google Home, Rules), 0 = closed by app
@@ -186,7 +188,7 @@ class CameraService(BaseService):
186
188
  else:
187
189
  await self._run_action(camera, "siren_off")
188
190
 
189
- # Also controls lamp socket and BCP spotlight
191
+ # Also controls lamp socket, BCP spotlight, and Bulb Cam light
190
192
  async def floodlight_on(self, camera: Camera):
191
193
  if camera.product_model == "AN_RSCW":
192
194
  await self._run_action_devicemgmt(
@@ -196,10 +198,13 @@ class CameraService(BaseService):
196
198
  await self._run_action_devicemgmt(
197
199
  camera, "floodlight", "1"
198
200
  ) # Some camera models use a diffrent api
201
+ elif camera.product_model == "HL_BC":
202
+ # Bulb Cam uses run_action with floodlight_on action
203
+ await self._run_action(camera, "floodlight_on")
199
204
  else:
200
205
  await self._set_property(camera, PropertyIDs.ACCESSORY.value, "1")
201
206
 
202
- # Also controls lamp socket and BCP spotlight
207
+ # Also controls lamp socket, BCP spotlight, and Bulb Cam light
203
208
  async def floodlight_off(self, camera: Camera):
204
209
  if camera.product_model == "AN_RSCW":
205
210
  await self._run_action_devicemgmt(
@@ -209,6 +214,9 @@ class CameraService(BaseService):
209
214
  await self._run_action_devicemgmt(
210
215
  camera, "floodlight", "0"
211
216
  ) # Some camera models use a diffrent api
217
+ elif camera.product_model == "HL_BC":
218
+ # Bulb Cam uses run_action with floodlight_off action
219
+ await self._run_action(camera, "floodlight_off")
212
220
  else:
213
221
  await self._set_property(camera, PropertyIDs.ACCESSORY.value, "2")
214
222
 
@@ -81,13 +81,8 @@ def wyze_decrypt_cbc(key: str, enc_hex_str: str) -> str:
81
81
 
82
82
  Returns:
83
83
  The decrypted plaintext string.
84
-
85
- Note:
86
- MD5 is used here because it is required by Wyze's proprietary API protocol.
87
- This is not a security vulnerability - it's mandatory for API compatibility.
88
84
  """
89
- # MD5 is required by Wyze's API protocol - not a security choice but API compatibility
90
- key_hash = hashlib.md5(key.encode("utf-8")).digest() # nosec B324
85
+ key_hash = hashlib.md5(key.encode("utf-8")).digest()
91
86
 
92
87
  iv = b"0123456789ABCDEF"
93
88
  cipher = AES.new(key_hash, AES.MODE_CBC, iv)
@@ -109,15 +104,10 @@ def create_password(password: str) -> str:
109
104
 
110
105
  Returns:
111
106
  The hashed password as a hex string.
112
-
113
- Note:
114
- Triple MD5 is mandated by Wyze's authentication API. This cannot be changed
115
- without breaking compatibility with Wyze's servers.
116
107
  """
117
- # MD5 is required by Wyze's authentication protocol - not a security choice
118
- hex1 = hashlib.md5(password.encode()).hexdigest() # nosec B324
119
- hex2 = hashlib.md5(hex1.encode()).hexdigest() # nosec B324
120
- return hashlib.md5(hex2.encode()).hexdigest() # nosec B324
108
+ hex1 = hashlib.md5(password.encode()).hexdigest()
109
+ hex2 = hashlib.md5(hex1.encode()).hexdigest()
110
+ return hashlib.md5(hex2.encode()).hexdigest()
121
111
 
122
112
 
123
113
  def check_for_errors_standard(service, response_json: Dict[str, Any]) -> None:
@@ -77,6 +77,18 @@ class TestCameraService(unittest.IsolatedAsyncioTestCase):
77
77
  }
78
78
  )
79
79
 
80
+ self.bulb_cam = Camera(
81
+ {
82
+ "device_type": DeviceTypes.CAMERA.value,
83
+ "product_type": DeviceTypes.CAMERA.value,
84
+ "product_model": "HL_BC", # Wyze Bulb Cam
85
+ "mac": "TEST_BC",
86
+ "nickname": "Test Bulb Cam",
87
+ "device_params": {"ip": "192.168.1.104"},
88
+ "raw_dict": {},
89
+ }
90
+ )
91
+
80
92
  async def test_update_legacy_camera(self):
81
93
  # Mock responses
82
94
  self.camera_service._get_event_list.return_value = {
@@ -216,6 +228,51 @@ class TestCameraService(unittest.IsolatedAsyncioTestCase):
216
228
  self.bcp_camera, "spotlight", "0"
217
229
  )
218
230
 
231
+ async def test_floodlight_control_bulb_cam(self):
232
+ """Test Bulb Cam (HL_BC) light control uses run_action."""
233
+ await self.camera_service.floodlight_on(self.bulb_cam)
234
+ self.camera_service._run_action.assert_awaited_with(
235
+ self.bulb_cam, "floodlight_on"
236
+ )
237
+
238
+ await self.camera_service.floodlight_off(self.bulb_cam)
239
+ self.camera_service._run_action.assert_awaited_with(
240
+ self.bulb_cam, "floodlight_off"
241
+ )
242
+
243
+ async def test_update_bulb_cam_light_state(self):
244
+ """Test that Bulb Cam correctly reads P1056 as light state.
245
+
246
+ For Bulb Cam (HL_BC):
247
+ - P1056='1' means light is ON
248
+ - P1056='2' means light is OFF
249
+ """
250
+ # Mock responses
251
+ self.camera_service._get_event_list.return_value = {
252
+ "data": {"event_list": []}
253
+ }
254
+ self.camera_service.get_updated_params.return_value = {"ip": "192.168.1.104"}
255
+
256
+ # Test light ON state (P1056='1')
257
+ self.camera_service._get_property_list.return_value = [
258
+ (PropertyIDs.AVAILABLE, "1"),
259
+ (PropertyIDs.ON, "1"),
260
+ (PropertyIDs.ACCESSORY, "1"), # Light is ON
261
+ ]
262
+
263
+ updated_camera = await self.camera_service.update(self.bulb_cam)
264
+ self.assertTrue(updated_camera.floodlight)
265
+
266
+ # Test light OFF state (P1056='2')
267
+ self.camera_service._get_property_list.return_value = [
268
+ (PropertyIDs.AVAILABLE, "1"),
269
+ (PropertyIDs.ON, "1"),
270
+ (PropertyIDs.ACCESSORY, "2"), # Light is OFF
271
+ ]
272
+
273
+ updated_camera = await self.camera_service.update(self.bulb_cam)
274
+ self.assertFalse(updated_camera.floodlight)
275
+
219
276
  async def test_notification_control_legacy_camera(self):
220
277
  await self.camera_service.turn_on_notifications(self.test_camera)
221
278
  self.camera_service._set_property.assert_awaited_with(
@@ -1,5 +1,5 @@
1
1
  version = 1
2
- revision = 3
2
+ revision = 2
3
3
  requires-python = ">=3.11.0"
4
4
 
5
5
  [[package]]
@@ -717,7 +717,7 @@ wheels = [
717
717
 
718
718
  [[package]]
719
719
  name = "wyzeapy"
720
- version = "0.5.31"
720
+ version = "0.5.30"
721
721
  source = { editable = "." }
722
722
  dependencies = [
723
723
  { name = "aiodns" },
@@ -740,18 +740,18 @@ dev = [
740
740
 
741
741
  [package.metadata]
742
742
  requires-dist = [
743
- { name = "aiodns", specifier = ">=3.2.0,<5.0.0" },
743
+ { name = "aiodns", specifier = ">=3.2.0,<4.0.0" },
744
744
  { name = "aiohttp", specifier = ">=3.11.12,<4.0.0" },
745
- { name = "pdoc", marker = "extra == 'dev'", specifier = ">=15.0.3,<17.0.0" },
745
+ { name = "pdoc", marker = "extra == 'dev'", specifier = ">=15.0.3,<16.0.0" },
746
746
  { name = "pycryptodome", specifier = ">=3.21.0,<4.0.0" },
747
- { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0,<10.0.0" },
747
+ { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0,<9.0.0" },
748
748
  ]
749
749
  provides-extras = ["dev"]
750
750
 
751
751
  [package.metadata.requires-dev]
752
752
  dev = [
753
753
  { name = "coverage", specifier = ">=7.9.2" },
754
- { name = "pytest", specifier = ">=7.0.0,<10.0.0" },
754
+ { name = "pytest", specifier = ">=7.0.0,<9.0.0" },
755
755
  { name = "ruff", specifier = ">=0.12.3" },
756
756
  ]
757
757
 
@@ -1,19 +0,0 @@
1
- name: Auto Approve Owner PRs
2
-
3
- on:
4
- pull_request_target:
5
- types: [opened, synchronize, reopened]
6
-
7
- permissions:
8
- pull-requests: write
9
-
10
- jobs:
11
- auto-approve:
12
- runs-on: ubuntu-latest
13
- # Only auto-approve PRs from the repository owner
14
- if: github.event.pull_request.user.login == github.repository_owner
15
- steps:
16
- - name: Auto Approve
17
- uses: hmarr/auto-approve-action@8f929096a962e83ccdfa8afcf855f39f12d4dac7 # v4
18
- with:
19
- github-token: ${{ secrets.GITHUB_TOKEN }}
@@ -1,78 +0,0 @@
1
- # This workflow uses actions that are not certified by GitHub. They are provided
2
- # by a third-party and are governed by separate terms of service, privacy
3
- # policy, and support documentation.
4
-
5
- name: Scorecard supply-chain security
6
- on:
7
- # For Branch-Protection check. Only the default branch is supported. See
8
- # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
9
- branch_protection_rule:
10
- # To guarantee Maintained check is occasionally updated. See
11
- # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
12
- schedule:
13
- - cron: '29 2 * * 4'
14
- push:
15
- branches: [ "main" ]
16
-
17
- # Declare default permissions as read only.
18
- permissions: read-all
19
-
20
- jobs:
21
- analysis:
22
- name: Scorecard analysis
23
- runs-on: ubuntu-latest
24
- # `publish_results: true` only works when run from the default branch. conditional can be removed if disabled.
25
- if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request'
26
- permissions:
27
- # Needed to upload the results to code-scanning dashboard.
28
- security-events: write
29
- # Needed to publish results and get a badge (see publish_results below).
30
- id-token: write
31
- # Uncomment the permissions below if installing in a private repository.
32
- # contents: read
33
- # actions: read
34
-
35
- steps:
36
- - name: "Checkout code"
37
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
38
- with:
39
- persist-credentials: false
40
-
41
- - name: "Run analysis"
42
- uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
43
- with:
44
- results_file: results.sarif
45
- results_format: sarif
46
- # (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
47
- # - you want to enable the Branch-Protection check on a *public* repository, or
48
- # - you are installing Scorecard on a *private* repository
49
- # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
50
- # repo_token: ${{ secrets.SCORECARD_TOKEN }}
51
-
52
- # Public repositories:
53
- # - Publish results to OpenSSF REST API for easy access by consumers
54
- # - Allows the repository to include the Scorecard badge.
55
- # - See https://github.com/ossf/scorecard-action#publishing-results.
56
- # For private repositories:
57
- # - `publish_results` will always be set to `false`, regardless
58
- # of the value entered here.
59
- publish_results: true
60
-
61
- # (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore
62
- # file_mode: git
63
-
64
- # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
65
- # format to the repository Actions tab.
66
- - name: "Upload artifact"
67
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
68
- with:
69
- name: SARIF file
70
- path: results.sarif
71
- retention-days: 5
72
-
73
- # Upload the results to GitHub's code scanning dashboard (optional).
74
- # Commenting out will disable upload of results to your repo's Code Scanning dashboard
75
- - name: "Upload to code-scanning"
76
- uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4
77
- with:
78
- sarif_file: results.sarif
@@ -1,18 +0,0 @@
1
- repos:
2
- - repo: https://github.com/gitleaks/gitleaks
3
- rev: v8.16.3
4
- hooks:
5
- - id: gitleaks
6
- - repo: https://github.com/jumanjihouse/pre-commit-hooks
7
- rev: 3.0.0
8
- hooks:
9
- - id: shellcheck
10
- - repo: https://github.com/pre-commit/pre-commit-hooks
11
- rev: v4.4.0
12
- hooks:
13
- - id: end-of-file-fixer
14
- - id: trailing-whitespace
15
- - repo: https://github.com/pylint-dev/pylint
16
- rev: v2.17.2
17
- hooks:
18
- - id: pylint
@@ -1,79 +0,0 @@
1
- # Security Policy
2
-
3
- ## Supported Versions
4
-
5
- The following versions of Wyzeapy are currently supported with security updates:
6
-
7
- | Version | Supported |
8
- | ------- | ------------------ |
9
- | Latest | :white_check_mark: |
10
-
11
- ## Reporting a Vulnerability
12
-
13
- We take the security of Wyzeapy seriously. If you believe you have found a security vulnerability in this project, please report it to us responsibly.
14
-
15
- ### How to Report
16
-
17
- **Please do not report security vulnerabilities through public GitHub issues.**
18
-
19
- Instead, please use one of the following methods:
20
-
21
- 1. **GitHub Private Vulnerability Reporting (Preferred):** Use GitHub's [private vulnerability reporting feature](https://github.com/SecKatie/wyzeapy/security/advisories/new) to submit your report directly through the repository.
22
-
23
- 2. **Email:** Send an email to [katie@mulliken.net](mailto:katie@mulliken.net) with the subject line "Wyzeapy Security Vulnerability Report".
24
-
25
- ### What to Include
26
-
27
- Please include the following information in your report:
28
-
29
- - A clear description of the vulnerability
30
- - Steps to reproduce the issue
31
- - Affected versions
32
- - Potential impact of the vulnerability
33
- - Any suggested fixes or mitigations (optional)
34
-
35
- ### What to Expect
36
-
37
- After you submit a vulnerability report:
38
-
39
- 1. **Acknowledgment:** You will receive an acknowledgment of your report within 48 hours.
40
-
41
- 2. **Initial Assessment:** Within 7 days, we will provide an initial assessment of the vulnerability and an estimated timeline for a fix.
42
-
43
- 3. **Resolution Timeline:** We aim to resolve critical vulnerabilities within 30 days and other vulnerabilities within 90 days of the initial report.
44
-
45
- 4. **Disclosure:** We follow coordinated vulnerability disclosure practices. We will work with you to determine an appropriate disclosure timeline once a fix is available.
46
-
47
- 5. **Credit:** If you would like to be credited for the discovery, please let us know how you would like to be acknowledged.
48
-
49
- ### Scope
50
-
51
- The following are considered in-scope for security vulnerability reports:
52
-
53
- - Authentication and authorization issues
54
- - Credential exposure or leakage
55
- - Injection vulnerabilities
56
- - Sensitive data exposure
57
- - Security misconfigurations in the library code
58
-
59
- The following are generally out of scope:
60
-
61
- - Issues in third-party dependencies (please report these to the respective maintainers)
62
- - Issues related to the Wyze API itself (please report these to Wyze)
63
- - Social engineering attacks
64
- - Denial of service attacks
65
-
66
- ## Security Best Practices for Users
67
-
68
- When using Wyzeapy:
69
-
70
- - Never commit credentials or API keys to version control
71
- - Store sensitive configuration in environment variables or secure vaults
72
- - Keep the library updated to the latest version
73
- - Review the permissions and scopes requested by your integration
74
-
75
- ## Contact
76
-
77
- For security-related inquiries, contact: [katie@mulliken.net](mailto:katie@mulliken.net)
78
-
79
- For general questions and non-security issues, please use [GitHub Issues](https://github.com/SecKatie/wyzeapy/issues).
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