Skip to content

Commit 098d43b

Browse files
authoredJun 19, 2024
Merge pull request #951 from fronzbot/dev
0.23.0
2 parents ae0e26f + bb0adb6 commit 098d43b

15 files changed

+219
-40
lines changed
 

‎.github/workflows/build.yml

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: build
2+
3+
on:
4+
push:
5+
branches: [ master, dev ]
6+
pull_request:
7+
branches: [ master, dev ]
8+
9+
jobs:
10+
build:
11+
runs-on: ${{ matrix.platform }}
12+
strategy:
13+
matrix:
14+
platform:
15+
- ubuntu-latest
16+
python-version: ['3.11']
17+
steps:
18+
- name: Check out code from GitHub
19+
uses: actions/checkout@v3
20+
- name: Set up Python ${{ matrix.python-version }}
21+
uses: actions/setup-python@v4
22+
with:
23+
python-version: ${{ matrix.python-version }}
24+
- name: Install dependencies
25+
run: |
26+
python -m pip install --upgrade pip
27+
pip install -r requirements.txt
28+
pip install -r requirements_test.txt
29+
pip install tox
30+
- name: Build Wheel
31+
run: |
32+
tox -r -e build

‎.github/workflows/coverage.yml

+32-17
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,15 @@ on:
88

99
jobs:
1010
coverage:
11-
runs-on: ${{ matrix.platform }}
11+
runs-on: ubuntu-latest
1212
strategy:
13-
max-parallel: 1
1413
matrix:
15-
platform:
16-
- ubuntu-latest
1714
python-version: ['3.11']
18-
1915
steps:
20-
- uses: actions/checkout@v4
16+
- name: Check out code from GitHub
17+
uses: actions/checkout@v4.1.6
2118
- name: Set up Python ${{ matrix.python-version }}
22-
uses: actions/setup-python@v5
19+
uses: actions/setup-python@v4
2320
with:
2421
python-version: ${{ matrix.python-version }}
2522
- name: Install dependencies
@@ -28,16 +25,34 @@ jobs:
2825
pip install -r requirements.txt
2926
pip install -r requirements_test.txt
3027
pip install tox
31-
pip install codecov
32-
- name: Test
28+
- name: Run Coverage
3329
run: |
3430
tox -r -e cov
35-
- name: Codecov
36-
uses: codecov/codecov-action@v4
37-
env:
38-
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
31+
- name: Upload coverage
32+
uses: actions/upload-artifact@v4.3.3
3933
with:
40-
flags: unittests
41-
file: ./coverage.xml
42-
name: blinkpy
43-
fail_ci_if_error: true
34+
name: coverage-${{ matrix.python-version }}
35+
path: coverage.xml
36+
overwrite: true
37+
upload-coverage:
38+
runs-on: ubuntu-latest
39+
strategy:
40+
matrix:
41+
python-version: ['3.11']
42+
needs:
43+
- coverage
44+
timeout-minutes: 10
45+
steps:
46+
- name: Check out code from GitHub
47+
uses: actions/checkout@v4.1.6
48+
- name: Download all coverage artifacts
49+
uses: actions/download-artifact@v4.1.7
50+
with:
51+
name: coverage-${{ matrix.python-version }}
52+
path: coverage.xml
53+
- name: Upload coverage to Codecov
54+
uses: codecov/codecov-action@v4.4.1
55+
with:
56+
fail_ci_if_error: true
57+
token: ${{ secrets.CODECOV_TOKEN }}
58+
name: blinkpy

‎.github/workflows/lint.yml

+10-5
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ on:
88
branches: [ master, dev ]
99
pull_request:
1010
branches: [ master, dev ]
11-
1211
jobs:
1312
lint:
1413
runs-on: ubuntu-latest
1514
strategy:
15+
max-parallel: 2
1616
matrix:
17-
python-version: [3.11]
17+
python-version: ['3.9', '3.10', '3.11', '3.12']
1818

1919
steps:
2020
- uses: actions/checkout@v4
@@ -27,7 +27,12 @@ jobs:
2727
python -m pip install --upgrade pip
2828
pip install -r requirements.txt
2929
pip install -r requirements_test.txt
30-
pip install tox
31-
- name: Lint
30+
- name: Ruff
31+
run: |
32+
ruff check blinkpy tests blinkapp
33+
- name: Black
34+
run: |
35+
black --check --color --diff blinkpy tests blinkapp
36+
- name: RST-Lint
3237
run: |
33-
tox -r -e lint
38+
rst-lint README.rst CHANGES.rst CONTRIBUTING.rst

‎.github/workflows/tests.yml

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: build
1+
name: tests
22

33
on:
44
push:
@@ -7,17 +7,17 @@ on:
77
branches: [ master, dev ]
88

99
jobs:
10-
build:
10+
pytest:
1111
runs-on: ${{ matrix.platform }}
1212
strategy:
1313
max-parallel: 4
1414
matrix:
1515
platform:
1616
- ubuntu-latest
1717
python-version: ['3.9', '3.10', '3.11', '3.12']
18-
1918
steps:
20-
- uses: actions/checkout@v3
19+
- name: Check out code from GitHub
20+
uses: actions/checkout@v3
2121
- name: Set up Python ${{ matrix.python-version }}
2222
uses: actions/setup-python@v4
2323
with:
@@ -27,7 +27,11 @@ jobs:
2727
python -m pip install --upgrade pip
2828
pip install -r requirements.txt
2929
pip install -r requirements_test.txt
30-
pip install tox
30+
pip install .
3131
- name: Tests
3232
run: |
33-
tox -r
33+
python -m pytest \
34+
--timeout=30 \
35+
--durations=10 \
36+
--cov=blinkpy \
37+
--cov-report term-missing

‎CHANGES.rst

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ Changelog
44

55
A list of changes between each release
66

7+
0.23.0 (2024-06-19)
8+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9+
10+
See release notes: (`0.23.0 <https://github.com/fronzbot/blinkpy/releases/tag/v0.23.0>`__)
11+
12+
713
0.22.7 (2024-04-15)
814
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
915

‎blinkpy/api.py

+35-2
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@ async def request_login(
6262
async def request_verify(auth, blink, verify_key):
6363
"""Send verification key to blink servers."""
6464
url = (
65-
f"{blink.urls.base_url}/api/v4/account/{blink.account_id}"
66-
f"/client/{blink.client_id}/pin/verify"
65+
f"{blink.urls.base_url}/api/v5/accounts/{blink.account_id}"
66+
f"/users/{blink.auth.user_id}"
67+
f"/clients/{blink.client_id}/client_verification/pin/verify"
6768
)
6869
data = dumps({"pin": verify_key})
6970
return await auth.query(
@@ -165,6 +166,38 @@ async def request_system_disarm(blink, network, **kwargs):
165166
return response
166167

167168

169+
async def request_notification_flags(blink, **kwargs):
170+
"""
171+
Get system notification flags.
172+
173+
:param blink: Blink instance.
174+
"""
175+
url = (
176+
f"{blink.urls.base_url}/api/v1/accounts/{blink.account_id}"
177+
"/notifications/configuration"
178+
)
179+
response = await http_get(blink, url)
180+
await wait_for_command(blink, response)
181+
return response
182+
183+
184+
async def request_set_notification_flag(blink, data_dict):
185+
"""
186+
Set a system notification flag.
187+
188+
:param blink: Blink instance.
189+
:param data_dict: Dictionary of notifications to set.
190+
"""
191+
url = (
192+
f"{blink.urls.base_url}/api/v1/accounts/{blink.account_id}"
193+
"/notifications/configuration"
194+
)
195+
data = dumps({"notifications": data_dict})
196+
response = await http_post(blink, url, data=data, json=False)
197+
await wait_for_command(blink, response)
198+
return response
199+
200+
168201
async def request_command_status(blink, network, command_id):
169202
"""
170203
Request command status.

‎blinkpy/auth.py

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def __init__(
4949
self.region_id = login_data.get("region_id", None)
5050
self.client_id = login_data.get("client_id", None)
5151
self.account_id = login_data.get("account_id", None)
52+
self.user_id = login_data.get("user_id", None)
5253
self.login_response = None
5354
self.is_errored = False
5455
self.no_prompt = no_prompt
@@ -64,6 +65,7 @@ def login_attributes(self):
6465
self.data["region_id"] = self.region_id
6566
self.data["client_id"] = self.client_id
6667
self.data["account_id"] = self.account_id
68+
self.data["user_id"] = self.user_id
6769
return self.data
6870

6971
@property
@@ -130,6 +132,7 @@ def extract_login_info(self):
130132
self.token = self.login_response["auth"]["token"]
131133
self.client_id = self.login_response["account"]["client_id"]
132134
self.account_id = self.login_response["account"]["account_id"]
135+
self.user_id = self.login_response["account"].get("user_id", None)
133136

134137
async def startup(self):
135138
"""Initialize tokens for communication."""

‎blinkpy/blinkpy.py

+20-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import logging
1818
import datetime
1919
import aiofiles
20+
import aiofiles.ospath
2021
from requests.structures import CaseInsensitiveDict
2122
from dateutil.parser import parse
2223
from slugify import slugify
@@ -143,6 +144,8 @@ async def setup_prompt_2fa(self):
143144
async def setup_post_verify(self):
144145
"""Initialize blink system after verification."""
145146
try:
147+
if not self.homescreen:
148+
await self.get_homescreen()
146149
await self.setup_networks()
147150
networks = self.setup_network_ids()
148151
cameras = await self.setup_camera_list()
@@ -191,7 +194,7 @@ async def setup_owls(self):
191194
network_list.append(str(network_id))
192195
self.sync[name] = BlinkOwl(self, name, network_id, owl)
193196
await self.sync[name].start()
194-
except KeyError:
197+
except (KeyError, TypeError):
195198
# No sync-less devices found
196199
pass
197200

@@ -221,7 +224,7 @@ async def setup_lotus(self):
221224
network_list.append(str(network_id))
222225
self.sync[name] = BlinkLotus(self, name, network_id, lotus)
223226
await self.sync[name].start()
224-
except KeyError:
227+
except (KeyError, TypeError):
225228
# No sync-less devices found
226229
pass
227230

@@ -317,6 +320,21 @@ async def save(self, file_name):
317320
"""Save login data to file."""
318321
await util.json_save(self.auth.login_attributes, file_name)
319322

323+
async def get_status(self):
324+
"""Get the blink system notification status."""
325+
response = await api.request_notification_flags(self)
326+
return response.get("notifications", response)
327+
328+
async def set_status(self, data_dict={}):
329+
"""
330+
Set the blink system notification status.
331+
332+
:param data_dict: Dictionary of notification keys to modify.
333+
Example: {'low_battery': False, 'motion': False}
334+
"""
335+
response = await api.request_set_notification_flag(self, data_dict)
336+
return response
337+
320338
async def download_videos(
321339
self, path, since=None, camera="all", stop=10, delay=1, debug=False
322340
):

‎blinkpy/camera.py

+23
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,17 @@ async def async_arm(self, value):
516516
await api.wait_for_command(self.sync.blink, response)
517517
return response
518518

519+
async def record(self):
520+
"""Initiate clip recording for a blink mini camera."""
521+
url = (
522+
f"{self.sync.urls.base_url}/api/v1/accounts/"
523+
f"{self.sync.blink.account_id}/networks/"
524+
f"{self.network_id}/owls/{self.camera_id}/clip"
525+
)
526+
response = await api.http_post(self.sync.blink, url)
527+
await api.wait_for_command(self.sync.blink, response)
528+
return response
529+
519530
async def snap_picture(self):
520531
"""Snap picture for a blink mini camera."""
521532
url = (
@@ -575,6 +586,18 @@ async def async_arm(self, value):
575586
await api.wait_for_command(self.sync.blink, response)
576587
return response
577588

589+
async def record(self):
590+
"""Initiate clip recording for a blink doorbell camera."""
591+
url = (
592+
f"{self.sync.urls.base_url}/api/v1/accounts/"
593+
f"{self.sync.blink.account_id}/networks/"
594+
f"{self.sync.network_id}/doorbells/{self.camera_id}/clip"
595+
)
596+
597+
response = await api.http_post(self.sync.blink, url)
598+
await api.wait_for_command(self.sync.blink, response)
599+
return response
600+
578601
async def snap_picture(self):
579602
"""Snap picture for a blink doorbell camera."""
580603
url = (

‎pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "blinkpy"
7-
version = "0.22.7"
7+
version = "0.23.0"
88
license = {text = "MIT"}
99
description = "A Blink camera Python Library."
1010
readme = "README.rst"

‎requirements_test.txt

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
ruff==0.3.7
2-
black==24.3.0
1+
ruff==0.4.9
2+
black==24.4.2
33
build==1.2.1
4-
coverage==7.4.4
5-
pytest==8.1.1
4+
coverage==7.5.3
5+
pytest==8.2.2
66
pytest-cov==5.0.0
77
pytest-sugar==1.0.0
88
pytest-timeout==2.3.1
99
restructuredtext-lint==1.4.0
10-
pygments==2.17.2
10+
pygments==2.18.0
1111
testtools>=2.4.0
1212
sortedcontainers~=2.4.0
1313
pytest-asyncio>=0.21.0

0 commit comments

Comments
 (0)