Skip to content

Commit

Permalink
v2.3.14 (#950)
Browse files Browse the repository at this point in the history
* PTZ using cover #921

* set min max for rotate button? #921

* only show pan_tilt for pan_cam

* Add extra intel packages #736

* Update Dockerfile.hwaccel

* Revert "Update Dockerfile.hwaccel"

This reverts commit d7de34c.

* Update Dockerfile.qsv

* Update Dockerfile.qsv

* Update Dockerfile.qsv

* Fix typo #736

* copy bins #736

* fix typing warnings

* remove dot from version check

* DEBUG_FFMPEG to FFMPEG_LOGLEVEL

* case sensitive FFMPEG_CMD #736

* Add ffmpeg filter_complex config #919

* Adjust bitrate

* Skip audio buffer check #388

* Revert "Skip audio buffer check #388"

This reverts commit 79ace0c.

* changelog
  • Loading branch information
mrlt8 authored Aug 6, 2023
1 parent 80266b3 commit f28c021
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 38 deletions.
29 changes: 15 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ Based on [@noelhibbard's script](https://gist.github.com/noelhibbard/03703f55129

Please consider ⭐️ starring or [☕️ sponsoring](https://ko-fi.com/mrlt8) this project if you found it useful, or use the [affiliate link](https://amzn.to/3NLnbvt) when shopping on amazon!

## API Changes

As of July 2023, you will need to update your bridge to v2.3.x or newer for compatibility with the latest changes to the Wyze API.

## Quick Start

Install [docker](https://docs.docker.com/get-docker/) and run:
Expand All @@ -33,23 +37,20 @@ You can then use the web interface at `http://localhost:5000` where localhost is

See [basic usage](#basic-usage) for additional information or visit the [wiki page](https://github.com/mrlt8/docker-wyze-bridge/wiki/Home-Assistant) for additional information on using the bridge as a Home Assistant Add-on.

## What's Changed in v2.3.13
## What's Changed in v2.3.14

FIXES:
* Errors when SET/GET `bitrate`. Thanks @plat2on1! (#929)
* Prevent exception on empty GET/SET payload.
NEW:
* PTZ controls in MQTT discovery as "cover"
* Add ffmpeg `filter_complex` config (#919)

## What's Changed in v2.3.12

* NEW:
* `update_snapshot` MQTT/REST API GET topic.
* Additional MQTT entities (#921)
* FIXES:
* Monitor and set preferred bitrate if/when the wyze app changes it. Thanks @plat2on1! (#929)
* `cruise_point` index starts at 1 when setting via MQTT/REST API. (#835)
* Camera status was always online. (#907) (#920)
* Power status was incorrect when using MQTT discovery. (#921)

CHANGED:
* Adjust default bitrate for re-encoding to 3000k.
* Case sensitive FFMPEG_CMD (#736) Thanks @392media!
* `DEBUG_FFMPEG` is now `FFMPEG_LOGLEVEL` with customizable levels:
* `quiet`, `panic`, `fatal`, `error`, `warning`, `info`, `verbose`, `debug`.
* Defaults to `fatal`.
* Bump Wyze App version to v2.44.1.1 (#946)

[View previous changes](https://github.com/mrlt8/docker-wyze-bridge/releases)

Expand Down
15 changes: 15 additions & 0 deletions app/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
## What's Changed in v2.3.14

NEW:
* PTZ controls in MQTT discovery as "cover"
* Add ffmpeg `filter_complex` config (#919)


CHANGED:
* Adjust default bitrate for re-encoding to 3000k.
* Case sensitive FFMPEG_CMD (#736) Thanks @392media!
* `DEBUG_FFMPEG` is now `FFMPEG_LOGLEVEL` with customizable levels:
* `quiet`, `panic`, `fatal`, `error`, `warning`, `info`, `verbose`, `debug`.
* Defaults to `fatal`.
* Bump Wyze App version to v2.44.1.1 (#946)

## What's Changed in v2.3.13

FIXES:
Expand Down
5 changes: 3 additions & 2 deletions app/Dockerfile.qsv
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ ARG QSV
RUN if [ -n "$QSV" ]; then echo 'deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware' >/etc/apt/sources.list.d/debian-testing.list; fi \
&& apt-get update \
&& apt-get install -y curl tar xz-utils \
${QSV:+i965-va-driver-shaders intel-media-va-driver-non-free intel-opencl-icd libmfx1 libva-drm2 libx11-6} \
${QSV:+i965-va-driver intel-gpu-tools intel-media-va-driver-non-free intel-opencl-icd libmfx1 libva-drm2 libx11-6 vainfo} \
&& if [ -n "$QSV" ]; then apt-get install -y i965-va-driver-shaders; fi \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
COPY . /build/app/
Expand All @@ -20,7 +21,7 @@ RUN cd /build \
&& curl -SL https://github.com/bluenviron/mediamtx/releases/download/v${MTX_TAG}/mediamtx_v${MTX_TAG}_linux_amd64.tar.gz \
| tar -xzf - -C app --wildcards 'mediamtx*' \
&& cp app/amd.lib usr/local/lib/libIOTCAPIs_ALL.so \
&& if [ -n "$QSV" ]; then cp -R /usr/lib/x86_64-linux-gnu/ usr/lib/; fi \
&& if [ -n "$QSV" ]; then cp -R /usr/lib/x86_64-linux-gnu/ usr/lib/ && cp -R /usr/bin/ usr/bin; fi \
&& rm app/*.txt app/*.lib

FROM base
Expand Down
2 changes: 1 addition & 1 deletion app/static/site.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ document.addEventListener("DOMContentLoaded", () => {
.then((data) => {
let apiVersion = data.tag_name.replace(/[^0-9\.]/g, "");
if (apiVersion.localeCompare(checkAPI.dataset.version, undefined, { numeric: true }) === 1) {
sendNotification('Update available!', `🎉 v.${apiVersion}`, "warning");
sendNotification('Update available!', `🎉 v${apiVersion}`, "warning");
} else {
sendNotification('All up to date!', '✅ Running the latest version!', "success");
}
Expand Down
8 changes: 6 additions & 2 deletions app/wyzebridge/bridge_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
from wyzecam.api_models import WyzeCamera


def env_cam(env: str, uri: str, default="") -> str:
return env_bool(f"{env}_{uri}", env_bool(env, env_bool(f"{env}_all", default)))
def env_cam(env: str, uri: str, default="", style="") -> str:
return env_bool(
f"{env}_{uri}",
env_bool(env, env_bool(f"{env}_all", default, style=style), style=style),
style=style,
)


def env_bool(env: str, false="", true="", style="") -> Any:
Expand Down
2 changes: 1 addition & 1 deletion app/wyzebridge/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@


DEPRECATED = {
"DEBUG_LEVEL",
"DEBUG_FFMPEG",
}

for env in DEPRECATED:
Expand Down
33 changes: 26 additions & 7 deletions app/wyzebridge/ffmpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ def get_ffmpeg_cmd(
rtsp_transport = "udp" if "udp" in env_bool("MTX_PROTOCOLS") else "tcp"
rss_cmd = f"[{{}}f=rtsp:{rtsp_transport=:}:bsfs/v=dump_extra=freq=keyframe]rtsp://0.0.0.0:8554/{uri}"
rtsp_ss = rss_cmd.format("")
if env_cam("AUDIO_STREAM", uri) and audio:
if env_cam("AUDIO_STREAM", uri, style="original") and audio:
rtsp_ss += "|" + rss_cmd.format("select=a:") + "_audio"
h264_enc = env_bool("h264_enc").partition("_")[2]

cmd = env_cam("FFMPEG_CMD", uri).format(
cmd = env_cam("FFMPEG_CMD", uri, style="original").format(
cam_name=uri, CAM_NAME=uri.upper(), audio_in=audio_in
).split() or (
["-loglevel", "verbose" if env_bool("DEBUG_FFMPEG") else "fatal"]
["-hide_banner", "-loglevel", get_log_level()]
+ env_cam("FFMPEG_FLAGS", uri, flags).strip("'\"\n ").split()
+ ["-thread_queue_size", "100"]
+ (["-hwaccel", h264_enc] if h264_enc in {"vaapi", "qsv"} else [])
Expand All @@ -66,11 +66,29 @@ def get_ffmpeg_cmd(
)
if "ffmpeg" not in cmd[0].lower():
cmd.insert(0, "ffmpeg")
if env_bool("DEBUG_FFMPEG"):
if env_bool("FFMPEG_LOGLEVEL") in {"info", "verbose", "debug"}:
logger.info(f"[FFMPEG_CMD] {' '.join(cmd)}")
return cmd


def get_log_level():
level = env_bool("FFMPEG_LOGLEVEL", "fatal").lower()

if level in {
"quiet",
"panic",
"fatal",
"error",
"warning",
"info",
"verbose",
"debug",
}:
return level

return "verbose"


def re_encode_video(uri: str, is_vertical: bool) -> list[str]:
"""
Check if stream needs to be re-encoded.
Expand All @@ -92,6 +110,7 @@ def re_encode_video(uri: str, is_vertical: bool) -> list[str]:
"""
h264_enc: str = env_bool("h264_enc", "libx264")
custom_filter = env_cam("FFMPEG_FILTER", uri)
filter_complex = env_cam("FFMPEG_FILTER_COMPLEX", uri)
v_filter = []
transpose = "clock"
if (env_bool("ROTATE_DOOR") and is_vertical) or env_bool(f"ROTATE_CAM_{uri}"):
Expand All @@ -106,7 +125,7 @@ def re_encode_video(uri: str, is_vertical: bool) -> list[str]:
elif h264_enc == "h264_qsv":
v_filter[1] = f"vpp_qsv=transpose={transpose}"

if not env_bool("FORCE_ENCODE") and not v_filter and not custom_filter:
if not (env_bool("FORCE_ENCODE") or v_filter or custom_filter or filter_complex):
return ["copy"]

logger.info(
Expand All @@ -121,8 +140,8 @@ def re_encode_video(uri: str, is_vertical: bool) -> list[str]:
return (
[h264_enc]
+ v_filter
+ ["-b:v", "2000k", "-coder", "1", "-bufsize", "2000k"]
+ ["-maxrate", "2000k", "-minrate", "2000k"]
+ (["-filter_complex", filter_complex, "-map", "[v]"] if filter_complex else [])
+ ["-b:v", "3000k", "-coder", "1", "-bufsize", "3000k"]
+ ["-profile:v", "77" if h264_enc == "h264_v4l2m2m" else "main"]
+ ["-preset", "fast" if h264_enc in {"h264_nvenc", "h264_qsv"} else "ultrafast"]
+ ["-forced-idr", "1", "-force_key_frames", "expr:gte(t,n_forced*2)"]
Expand Down
17 changes: 16 additions & 1 deletion app/wyzebridge/mqtt.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def publish_discovery(cam_uri: str, cam: WyzeCamera, stopped: bool = True) -> No
}

# Clear out old/renamed entities
REMOVE = {"alarm": "switch"}
REMOVE = {"alarm": "switch", "pan_tilt": "cover"}
for entity, type in REMOVE.items():
msgs.append((f"{MQTT_DISCOVERY}/{type}/{cam.mac}/{entity}/config", None))

Expand Down Expand Up @@ -390,6 +390,21 @@ def get_entities(base_topic: str, pan_cam: bool = False, rtsp: bool = False) ->
"icon": "mdi:map-marker-multiple",
},
},
"pan_tilt": {
"type": "cover",
"payload": {
"command_topic": f"{base_topic}rotary_degree/set",
"tilt_command_topic": f"{base_topic}rotary_degree/set",
"payload_open": "up",
"payload_close": "down",
"payload_stop": None,
"tilt_opened_value": 90,
"tilt_closed_value": -90,
"tilt_min": -90,
"tilt_max": 90,
"icon": "mdi:rotate-orbit",
},
},
}
if rtsp:
entities |= {
Expand Down
26 changes: 17 additions & 9 deletions app/wyzecam/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,20 @@ def login(
payload = sort_dict(
{"email": email.strip(), "password": triplemd5(password), **(mfa or {})}
)
api_version = "old"
api_version = "v2"
if getenv("API_ID") and getenv("API_KEY"):
api_version = "api"
elif getenv("v3"):
api_version = "v3"
headers["appid"] = "umgm_78ae6013d158c4a5"
headers["signature2"] = sign_msg("v3", payload)

base_url = f"{AUTH_API}/{api_version}" if api_version in {"api", "v3"} else AUTH_API
resp = requests.post(f"{base_url}/user/login", data=payload, headers=headers)
resp = requests.post(
f"{AUTH_API}/{api_version}/user/login", data=payload, headers=headers
)
resp.raise_for_status()

return WyzeCredential.parse_obj(dict(resp.json(), phone_id=phone_id))
return WyzeCredential.model_validate(dict(resp.json(), phone_id=phone_id))


def send_sms_code(auth_info: WyzeCredential, phone: str = "Primary") -> str:
Expand Down Expand Up @@ -157,7 +158,7 @@ def refresh_token(auth_info: WyzeCredential) -> WyzeCredential:
)
resp_json = validate_resp(resp)

return WyzeCredential.parse_obj(
return WyzeCredential.model_validate(
dict(
resp_json["data"],
user_id=auth_info.user_id,
Expand Down Expand Up @@ -185,7 +186,9 @@ def get_user_info(auth_info: WyzeCredential) -> WyzeAccount:
)
resp_json = validate_resp(resp)

return WyzeAccount.parse_obj(dict(resp_json["data"], phone_id=auth_info.phone_id))
return WyzeAccount.model_validate(
dict(resp_json["data"], phone_id=auth_info.phone_id)
)


def get_homepage_object_list(auth_info: WyzeCredential) -> dict[str, Any]:
Expand All @@ -204,7 +207,7 @@ def get_camera_list(auth_info: WyzeCredential) -> list[WyzeCamera]:
"""Return a list of all cameras on the account."""
data = get_homepage_object_list(auth_info)
result = []
for device in data["device_list"]: # type: dict[str, Any]
for device in data["device_list"]:
if device["product_type"] != "Camera":
continue

Expand Down Expand Up @@ -308,6 +311,9 @@ def set_device_info(

def get_cam_webrtc(auth_info: WyzeCredential, mac_id: str) -> dict:
"""Get webrtc for camera."""
if not auth_info.access_token:
raise AccessTokenError()

ui_headers = get_headers()
ui_headers["content-type"] = "application/json"
ui_headers["authorization"] = auth_info.access_token
Expand Down Expand Up @@ -339,7 +345,9 @@ def validate_resp(resp):
return resp_json


def _get_payload(access_token: Optional[str], phone_id: str, req_path: str = "default"):
def _get_payload(
access_token: Optional[str], phone_id: Optional[str] = "", req_path: str = "default"
):
return {
"sc": SC_SV[req_path]["sc"],
"sv": SC_SV[req_path]["sv"],
Expand All @@ -353,7 +361,7 @@ def _get_payload(access_token: Optional[str], phone_id: str, req_path: str = "de
}


def get_headers(phone_id: str = "") -> dict[str, str]:
def get_headers(phone_id: Optional[str] = "") -> dict[str, str]:
if not phone_id:
return {"user-agent": SCALE_USER_AGENT}
id, key = getenv("API_ID"), getenv("API_KEY")
Expand Down
2 changes: 1 addition & 1 deletion app/wyzecam/tutk/tutk_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -888,7 +888,7 @@ class K11000SetRotaryByDegree(TutkWyzeProtocolMessage):
"""

def __init__(self, horizontal: int, vertical: int, speed: int = 5):
def __init__(self, horizontal: int, vertical: int = 0, speed: int = 5):
super().__init__(11000)
self.horizontal = horizontal
self.vertical = vertical
Expand Down

0 comments on commit f28c021

Please sign in to comment.