Skip to content

Commit 02b4cea

Browse files
authored
Merge pull request #1168 from fronzbot/dev
v0.25.3
2 parents 9858b46 + b236342 commit 02b4cea

13 files changed

Lines changed: 76 additions & 61 deletions

API.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Unofficial documentation for the Client API of the Blink Wire-Free HD Home Monit
33

44
Copied from https://github.com/MattTW/BlinkMonitorProtocol
55

6-
I am not affiliated with the company in any way - this documentation is strictly **"AS-IS"**. My goal was to uncover enough to arm and disarm the system programatically so that I can issue those commands in sync with my home alarm system arm/disarm. Just some raw notes at this point but should be enough for creating programmatic APIs. Lots more to be discovered and documented - feel free to contribute!
6+
I am not affiliated with the company in any way - this documentation is strictly **"AS-IS"**. My goal was to uncover enough to arm and disarm the system programmatically so that I can issue those commands in sync with my home alarm system arm/disarm. Just some raw notes at this point but should be enough for creating programmatic APIs. Lots more to be discovered and documented - feel free to contribute!
77

88
The Client API is a straightforward REST API using JSON and HTTPS.
99

@@ -121,7 +121,7 @@ Get events for a given network (sync module) -- Need network ID from home
121121
>curl -H "Host: prod.immedia-semi.com" -H "TOKEN_AUTH: *authtoken from login*" --compressed https://rest.prod.immedia-semi.com/events/network/*network__id*
122122
123123
**Response**
124-
A json list of evets incluing URL's. Replace the "mp4" with "jpg" extension to get the thumbnail of each clip
124+
A json list of evets including URL's. Replace the "mp4" with "jpg" extension to get the thumbnail of each clip
125125

126126

127127
**Request**

CHANGES.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ See release notes: (`0.22.7 <https://github.com/fronzbot/blinkpy/releases/tag/v0
2323

2424
- Test for None type in poll_local_storage_manifest (`@mkmer #859 <https://github.com/fronzbot/blinkpy/pull/859>`__)
2525
- Update image after snap by (`@mkmer #861 <https://github.com/fronzbot/blinkpy/pull/861>`__)
26-
- fix missing ':' before port number in rtsps adress (`@Rosi2143 #863 <https://github.com/fronzbot/blinkpy/pull/863>`__)
26+
- fix missing ':' before port number in rtsps address (`@Rosi2143 #863 <https://github.com/fronzbot/blinkpy/pull/863>`__)
2727
- New temperature location (`@mkmer #867 <https://github.com/fronzbot/blinkpy/pull/867>`__)
2828

2929
**Other changes**
@@ -366,12 +366,12 @@ Same as 0.22.1 (pypi upload issue)
366366
**New Features:**
367367

368368
- Add is_errored property to Auth class (`@fronzbot #275 <https://github.com/fronzbot/blinkpy/pull/275>`__)
369-
- Add new endpoint to get user infor (`@fronzbot #280 <https://github.com/fronzbot/blinkpy/pull/280>`__)
369+
- Add new endpoint to get user info (`@fronzbot #280 <https://github.com/fronzbot/blinkpy/pull/280>`__)
370370
- Add get_liveview command to camera module (`@fronzbot #289 <https://github.com/fronzbot/blinkpy/pull/289>`__)
371371
- Add blink Mini Camera support (`@fronzbot #290 <https://github.com/fronzbot/blinkpy/pull/290>`__)
372372
- Add option to skip homescreen check (`@fronzbot #305 <https://github.com/fronzbot/blinkpy/pull/305>`__)
373373
- Add different timeout for video and image retrieval (`@fronzbot #323 <https://github.com/fronzbot/blinkpy/pull/323>`__)
374-
- Modifiy session to use HTTPAdapter and handle retries (`@fronzbot #324 <https://github.com/fronzbot/blinkpy/pull/324>`__)
374+
- Modify session to use HTTPAdapter and handle retries (`@fronzbot #324 <https://github.com/fronzbot/blinkpy/pull/324>`__)
375375
- Add retry option overrides (`@fronzbot #339 <https://github.com/fronzbot/blinkpy/pull/339>`__)
376376

377377
**All changes:**
@@ -529,7 +529,7 @@ Wifi status reported in dBm again, instead of bars (which is great). Also, the
529529
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
530530
- Moved all API calls to own module for easier maintainability
531531
- Added network ids to sync module and cameras to allow for multi-network use
532-
- Removed dependency on video existance prior to camera setup (fixes `#93 <https://github.com/fronzbot/blinkpy/issues/#93>`__)
532+
- Removed dependency on video existence prior to camera setup (fixes `#93 <https://github.com/fronzbot/blinkpy/issues/#93>`__)
533533
- Camera wifi_strength now reported in wifi "bars" rather than dBm due to API endpoint change
534534
- Use homescreen thumbnail as fallback in case it's not in the camera endpoint
535535
- Removed "armed" and "status" attributes from camera (status of camera only reported by "motion_enabled" now)
@@ -569,7 +569,7 @@ Wifi status reported in dBm again, instead of bars (which is great). Also, the
569569
- Added support for battery voltage level (fixes `#64 <https://github.com/fronzbot/blinkpy/issues/64>`__)
570570
- Added motion detection per camera
571571
- Added fully accessible camera configuration dict
572-
- Added celcius property to camera (fixes `#60 <https://github.com/fronzbot/blinkpy/issues/60>`__)
572+
- Added celsius property to camera (fixes `#60 <https://github.com/fronzbot/blinkpy/issues/60>`__)
573573

574574
0.7.1 (2018-05-09)
575575
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -591,7 +591,7 @@ Wifi status reported in dBm again, instead of bars (which is great). Also, the
591591

592592
0.6.0 (2017-05-12)
593593
~~~~~~~~~~~~~~~~~~
594-
- Removed redundent properties that only called hidden variables
594+
- Removed redundant properties that only called hidden variables
595595
- Revised request wrapper function to be more intelligent
596596
- Added tests to ensure exceptions are caught and handled (100% coverage!)
597597
- Added auto-reauthorization (token refresh) when a request fails due to an expired token (`@tySwift93 <https://github.com/fronzbot/blinkpy/pull/24>`__)

CONTRIBUTING.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ You can then run all of the tests with the following command:
8787

8888
If you only want to see if you can pass the local tests, you can run ``tox -e py39`` (or whatever python version you have installed. Only ``py39`` through ``py312`` will be accepted). If you just want to check for style violations, you can run ``tox -e lint``. Regardless, when you submit a pull request, your code MUST pass both the unit tests, and the linters.
8989

90-
If you need to change anything in ``requirements.txt`` for any reason, you'll want to regenerate the virtual envrionments used by ``tox`` by running with the ``-r`` flag: ``tox -r``
90+
If you need to change anything in ``requirements.txt`` for any reason, you'll want to regenerate the virtual environments used by ``tox`` by running with the ``-r`` flag: ``tox -r``
9191

9292
If you want to run a single test (perhaps you only changed a small thing in one file) you can run ``tox -e py37 -- tests/<testname>.py -x``. This will run the test ``<testname>.py`` and stop testing upon the first failure, making it easier to figure out why a particular test might be failing. The test structure mimics the library structure, so if you changed something in ``sync_module.py``, the associated test file would be in ``test_sync_module.py`` (ie. the filename is prepended with ``test_``.
9393

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ Similar methods exist for individual cameras:
198198
199199
Download videos
200200
----------------
201-
You can also use this library to download all videos from the server. In order to do this, you must specify a ``path``. You may also specifiy a how far back in time to go to retrieve videos via the ``since=`` variable (a simple string such as ``"2017/09/21"`` is sufficient), as well as how many pages to traverse via the ``stop=`` variable. Note that by default, the library will search the first ten pages which is sufficient in most use cases. Additionally, you can specify one or more cameras via the ``camera=`` property. This can be a single string indicating the name of the camera, or a list of camera names. By default, it is set to the string ``'all'`` to grab videos from all cameras. If you are downloading many items, setting the ``delay`` parameter is advised in order to throttle sequential calls to the API. By default this is set to ``1`` but can be any integer representing the number of seconds to delay between calls.
201+
You can also use this library to download all videos from the server. In order to do this, you must specify a ``path``. You may also specify a how far back in time to go to retrieve videos via the ``since=`` variable (a simple string such as ``"2017/09/21"`` is sufficient), as well as how many pages to traverse via the ``stop=`` variable. Note that by default, the library will search the first ten pages which is sufficient in most use cases. Additionally, you can specify one or more cameras via the ``camera=`` property. This can be a single string indicating the name of the camera, or a list of camera names. By default, it is set to the string ``'all'`` to grab videos from all cameras. If you are downloading many items, setting the ``delay`` parameter is advised in order to throttle sequential calls to the API. By default this is set to ``1`` but can be any integer representing the number of seconds to delay between calls.
202202

203203
Example usage, which downloads all videos recorded since July 4th, 2018 at 9:34am to the ``/home/blink`` directory with a 2s delay between calls:
204204

blinkapp/run.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
if [ "$#" -ne 2 ]; then
55
echo ""
6-
echo "ERROR: Requries Blink username and password as arguments."
6+
echo "ERROR: Requires Blink username and password as arguments."
77
echo "bash run.sh [username] [password]"
88
echo ""
99
exit 1
@@ -28,4 +28,3 @@ docker run -it --name ${IMAGE} \
2828
-e PASSWORD=${PASSWORD} \
2929
$USER/$IMAGE \
3030
/bin/bash
31-

blinkpy/helpers/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ async def json_save(data, file_name):
3939

4040

4141
def json_dumps(json_in, indent=2):
42-
"""Return a well formated json string."""
42+
"""Return a well formatted json string."""
4343
return json.dumps(json_in, indent=indent)
4444

4545

blinkpy/livestream.py

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,25 @@ def __init__(self, camera, response):
2525
self.target_reader = None
2626
self.target_writer = None
2727

28+
def add_auth_header_string_field(self, auth_header, field_string, max_length):
29+
"""Add string field to authentication header."""
30+
_LOGGER.debug("Field string: %s", field_string)
31+
field_bytes = field_string.encode("utf-8")[:max_length]
32+
field_bytes = field_bytes.ljust(max_length, b"\x00")
33+
_LOGGER.debug("Field bytes: %s", field_bytes)
34+
field_length = len(field_bytes).to_bytes(4, byteorder="big")
35+
_LOGGER.debug("Field length: %s (%d)", field_length, len(field_length))
36+
auth_header.extend(field_length)
37+
auth_header.extend(field_bytes)
38+
2839
def get_auth_header(self):
2940
"""Get authentication header."""
3041
auth_header = bytearray()
42+
serial_max_length = 16
43+
token_field_max_length = 64
44+
conn_id_max_length = 16
3145

32-
# Magic numeber
46+
# Magic number (4 bytes)
3347
# fmt: off
3448
magic_number = [
3549
0x00, 0x00, 0x00, 0x28, # Magic number (4 bytes)
@@ -38,51 +52,39 @@ def get_auth_header(self):
3852
auth_header.extend(magic_number)
3953
# Total packet length: 4 bytes
4054

41-
# Unknown string field (4-byte length prefix, 16 unknown bytes)
42-
# fmt: off
43-
unknown_string_field = [
44-
0x00, 0x00, 0x00, 0x00, # Length prefix (4 bytes)
45-
] + ([0x00] * 16) # Unknown bytes (16 bytes)
46-
# fmt: on
47-
auth_header.extend(unknown_string_field)
55+
# Device Serial field (4-byte length prefix, 16 serial bytes)
56+
serial = self.camera.serial
57+
self.add_auth_header_string_field(auth_header, serial, serial_max_length)
4858
# Total packet length: 24 bytes
4959

50-
# Client ID field
60+
# Client ID field (4 bytes)
5161
client_id = urllib.parse.parse_qs(self.target.query).get("client_id", [0])[0]
5262
_LOGGER.debug("Client ID: %s", client_id)
5363
client_id_field = int(client_id).to_bytes(4, byteorder="big")
5464
_LOGGER.debug("Client ID field: %s (%d)", client_id_field, len(client_id_field))
5565
auth_header.extend(client_id_field)
5666
# Total packet length: 28 bytes
5767

58-
# Unknown prefix field (2-byte prefix, 4-byte length prefix, 64 unknown bytes)
68+
# Static field (2 bytes)
5969
# fmt: off
60-
unknown_prefix_field = [
61-
0x01, 0x08, # Static prefix (2 bytes)
62-
0x00, 0x00, 0x00, 0x00, # Length prefix (4 bytes)
63-
] + ([0x00] * 64) # Unknown bytes (64 bytes)
70+
static_field = [
71+
0x01, 0x08, # Static value (2 bytes)
72+
]
6473
# fmt: on
65-
auth_header.extend(unknown_prefix_field)
66-
# Total packet length: 98 bytes
74+
auth_header.extend(static_field)
75+
# Total packet length: 30 bytes
6776

68-
# Connection ID length field (4-byte length prefix)
77+
# Auth Token field (4-byte length prefix, 64 null bytes for now)
6978
# fmt: off
70-
conn_id_length_prefix = [
71-
0x00, 0x00, 0x00, 0x10,
72-
]
73-
# fmt: on
74-
auth_header.extend(conn_id_length_prefix)
75-
# Total packet length: 102 bytes
79+
token_length = token_field_max_length.to_bytes(4, byteorder="big")
80+
_LOGGER.debug("Null token length: %s (%d)", token_length, len(token_length))
81+
auth_header.extend(token_length)
82+
auth_header.extend([0x00] * token_field_max_length)
83+
# Total packet length: 98 bytes
7684

77-
# Connection ID field (UTF-8-encoded, 16 bytes)
85+
# Connection ID field (4-byte length prefix, 16 connection ID bytes)
7886
conn_id = self.target.path.split("/")[-1].split("__")[0]
79-
_LOGGER.debug("Connection ID: %s", conn_id)
80-
conn_id_field = conn_id.encode("utf-8")[:16]
81-
# Ensure it is exactly 16 bytes long
82-
if len(conn_id_field) < 16:
83-
conn_id_field += b"\x00" * (16 - len(conn_id_field))
84-
_LOGGER.debug("Connection ID field: %s (%d)", conn_id_field, len(conn_id_field))
85-
auth_header.extend(conn_id_field)
87+
self.add_auth_header_string_field(auth_header, conn_id, conn_id_max_length)
8688
# Total packet length: 118 bytes
8789

8890
# Trailer (static 4-byte trailer)
@@ -249,9 +251,14 @@ async def send(self):
249251
latency_stats_packet = [
250252
# [1-byte msgtype, 4-byte sequence (static 1000), 4-byte payload length]
251253
0x12, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x18, # 9-byte header
252-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # 1/3 of 24-byte payload
253-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # 2/3 of 24-byte payload
254-
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, # 3/3 of 24-byte payload
254+
0x00, 0x00, 0x00, 0x00, # 4-byte audioAverageLatencyInMS
255+
0x00, 0x00, 0x00, 0x00, # 4-byte audioMaxLatencyInMS
256+
0x00, 0x00, # 2-byte audioFramesPresented
257+
0x00, 0x00, # 2-byte audioFramesDropped
258+
0x00, 0x00, 0x00, 0x00, # 4-byte videoAverageLatencyInMS
259+
0x00, 0x00, 0x00, 0x00, # 4-byte videoMaxLatencyInMS
260+
0x00, 0x00, # 2-byte videoFramesPresented
261+
0x00, 0x00, # 2-byte videoFramesDropped
255262
]
256263
# fmt: on
257264
every10s = 0

pyproject.toml

Lines changed: 2 additions & 2 deletions
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.25.2"
7+
version = "0.25.3"
88
license = {text = "MIT"}
99
description = "A Blink camera Python Library."
1010
readme = "README.rst"
@@ -47,7 +47,7 @@ lint.select = [
4747
"E", # pydocstyle
4848
"F", # pyflakes/autoflake
4949
"G", # flake8-logging-format
50-
"N815", # Varible {name} in class scope should not be mixedCase
50+
"N815", # Variable {name} in class scope should not be mixedCase
5151
"PGH004", # Use specific rule codes when using noqa
5252
"PLC", # pylint
5353
"PLE", # pylint

requirements_test.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ruff==0.14.8
1+
ruff==0.14.10
22
black==25.11.0
33
build==1.3.0
44
coverage==7.10.7

tests/test_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ async def test_request_notification_flags(self, mock_resp):
117117
)
118118

119119
async def test_request_set_notification_flag(self, mock_resp):
120-
"""Test set of notifiaction flags."""
120+
"""Test set of notification flags."""
121121
mock_resp.side_effect = (
122122
mresp.MockResponse(COMMAND_RESPONSE, 200),
123123
COMMAND_COMPLETE,

0 commit comments

Comments
 (0)