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.
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/workflows/auto-assign.yml +1 -4
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/workflows/codeql.yml +7 -13
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/workflows/lint.yml +1 -4
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/workflows/pre-release.yml +1 -4
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/workflows/publish.yml +1 -4
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/workflows/test.yml +1 -4
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/PKG-INFO +2 -2
- wyzeapy-0.5.31rc1/bin/act +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/pyproject.toml +2 -2
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/bulb_service.py +4 -25
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/camera_service.py +11 -3
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/utils.py +4 -14
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_camera_service.py +57 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/uv.lock +6 -6
- wyzeapy-0.5.31/.github/workflows/auto-approve.yml +0 -19
- wyzeapy-0.5.31/.github/workflows/scorecard.yml +0 -78
- wyzeapy-0.5.31/.pre-commit-config.yaml +0 -18
- wyzeapy-0.5.31/SECURITY.md +0 -79
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.coveragerc +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/CODEOWNERS +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.github/dependabot.yml +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.gitignore +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/.reuse/dep5 +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/CHANGELOG.md +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/LICENSES/GPL-3.0-only.txt +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/README.md +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/index.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/search.js +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/const.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/crypto.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/exceptions.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/payload_factory.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/base_service.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/bulb_service.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/camera_service.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/hms_service.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/lock_service.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/sensor_service.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/switch_service.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/thermostat_service.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/update_manager.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services/wall_switch_service.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/services.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/types.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/utils.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy/wyze_auth_lib.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/docs/wyzeapy.html +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/scripts/create_pre_release.sh +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/__init__.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/const.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/crypto.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/exceptions.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/payload_factory.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/__init__.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/base_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/hms_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/irrigation_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/lock_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/sensor_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/switch_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/thermostat_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/update_manager.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/services/wall_switch_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/tests/test_irrigation_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/types.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/src/wyzeapy/wyze_auth_lib.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/__init__.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_bulb_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_hms_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_lock_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_payload_factory.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_sensor_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_switch_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_thermostat_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_types.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_update_manager.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_utils.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_wall_switch_service.py +0 -0
- {wyzeapy-0.5.31 → wyzeapy-0.5.31rc1}/tests/test_wyze_auth_lib.py +0 -0
- {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@
|
|
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
|
-
|
|
35
|
-
language: [ 'python' ]
|
|
31
|
+
|
|
36
32
|
|
|
37
33
|
steps:
|
|
38
34
|
- name: Checkout repository
|
|
39
|
-
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #
|
|
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@
|
|
44
|
-
|
|
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@
|
|
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@
|
|
64
|
-
|
|
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@
|
|
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@
|
|
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@
|
|
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@
|
|
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.
|
|
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<
|
|
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.
|
|
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,<
|
|
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
|
-
"""
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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 =
|
|
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.
|
|
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,<
|
|
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,<
|
|
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,<
|
|
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,<
|
|
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
|
wyzeapy-0.5.31/SECURITY.md
DELETED
|
@@ -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
|
|
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
|