Skip to content

Commit

Permalink
v2.3.12 (#932)
Browse files Browse the repository at this point in the history
* Start from index 1 for cruise_point/waypoint #835

* update_snapshot via MQTT

* fix camera status always online #907 #920

* Additional MQTT entities #921

* QSV related changes

* i965-va-drivers #736

* FIX power status #921

* Fix cruise_point type #921

Thanks @jhansche

* return index from command payload #921

* Update docker-image.yml

* Monitor and set preferred bitrate #929

* Default to `-` for cruise_point #921

* clear out stale entities #921

* changelog
  • Loading branch information
mrlt8 authored Jul 24, 2023
1 parent b7668ca commit f4c57c4
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 78 deletions.
23 changes: 13 additions & 10 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,23 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3

- name: matrix image type
id: image_type
run: |
echo "suffix=${{ matrix.dockerfile == 'hwaccel' && '-hw' || matrix.dockerfile == 'qsv' && '-qsv' ||'' }}" >> $GITHUB_OUTPUT
echo "platforms=${{ matrix.dockerfile == 'multiarch' && 'linux/amd64,linux/arm64,linux/arm/v7' || 'linux/amd64' }}" >> $GITHUB_OUTPUT
echo "arch=${{ matrix.dockerfile == 'multiarch' && 'amd64,armhf,aarch64' || 'amd64' }}" >> $GITHUB_OUTPUT
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
platforms: linux/amd64,linux/arm64,linux/arm
platforms: ${{ steps.image_type.outputs.platforms }}

- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
with:
platforms: ${{ steps.image_type.outputs.platforms }}

- name: Login to DockerHub
if: github.event_name != 'pull_request'
Expand All @@ -61,13 +70,6 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: matrix image type
id: image_type
run: |
echo "suffix=${{ matrix.dockerfile == 'hwaccel' && '-hw' || matrix.dockerfile == 'qsv' && '-qsv' ||'' }}" >> $GITHUB_OUTPUT
echo "platforms=${{ matrix.dockerfile == 'multiarch' && 'linux/amd64,linux/arm64,linux/arm' || 'linux/amd64' }}" >> $GITHUB_OUTPUT
echo "arch=${{ matrix.dockerfile == 'multiarch' && 'amd64,armhf,aarch64' || 'amd64' }}" >> $GITHUB_OUTPUT
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v4
Expand Down Expand Up @@ -112,8 +114,9 @@ jobs:
io.hass.type=addon
io.hass.arch=${{ steps.image_type.outputs.arch }}
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
cache-from: type=gha,scope=${{ matrix.dockerfile }}
cache-to: type=gha,mode=max,scope=${{ matrix.dockerfile }}
provenance: false

version_bump:
needs: [build]
Expand Down
26 changes: 8 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,17 @@ 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.11
## What's Changed in v2.3.12

* NEW:
* Add more MQTT entities when using MQTT discovery. Thanks @jhansche! #921 #922
* custom video filter - Use `FFMPEG_FILTER` or `FFMPEG_FILTER_CAM-NAME` to set custom ffmpeg video filters. #919
* NEW MQTT/REST commands:
* **SET** topic: `cruise_point` | payload: (int) 1-4 - Pan to predefined cruise_point/waypoint. Thanks @jhansche! (#835).
* **SET** topic: `time_zone` | payload: (str) `Area/Location`, e.g. `America/New_York` - Change camera timezone. Thanks @DennisGarvey! (#916)
* **GET/SET** topic: `osd_timestamp` | payload: (bool/int) `on/off` - toggle timestamp on video.
* **GET/SET** topic: `osd_logo` | payload: (bool/int) `on/off` - toggle wyze logo on video.
* **SET** topic: `quick_reponse` | payload: (int) 1-3 - Doorbell quick response.
* `update_snapshot` MQTT/REST API GET topic.
* Additional MQTT entities (#921)
* FIXES:
* Resend discovery message on HA online. Thanks @jhansche! #907 #920
* Return json response/value for commands. Thanks @jhansche! #835
* Fix threading issue on restart. Thanks @ZacTyAdams! #902
* Catch and disable MQTT on name resolution error.
* Fix SET cruise_points over MQTT.
* Updates:
* Wyze iOS App version from v2.43.0.12 to v2.43.5.3 (#914)
* MediaMTX version from v0.23.7 to v0.23.8 (#925)

* 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)


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

Expand Down
11 changes: 11 additions & 0 deletions app/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## 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)

## What's Changed in v2.3.11

* NEW:
Expand Down
2 changes: 1 addition & 1 deletion app/Dockerfile.hwaccel
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ 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 libmfx1 libva-drm2 libx11-6} \
${QSV:+i965-va-driver intel-media-va-driver-non-free libmfx1 libva-drm2 libx11-6 && apt-get install i965-va-driver-shaders} \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
COPY . /build/app/
Expand Down
2 changes: 1 addition & 1 deletion app/Dockerfile.qsv
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ 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 libmfx1 libva-drm2 libx11-6} \
${QSV:+i965-va-driver-shaders intel-media-va-driver-non-free intel-opencl-icd libmfx1 libva-drm2 libx11-6} \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
COPY . /build/app/
Expand Down
1 change: 1 addition & 0 deletions app/wyzebridge/ffmpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ def re_encode_video(uri: str, is_vertical: bool) -> list[str]:
[h264_enc]
+ v_filter
+ ["-b:v", "2000k", "-coder", "1", "-bufsize", "2000k"]
+ ["-maxrate", "2000k", "-minrate", "2000k"]
+ ["-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
79 changes: 67 additions & 12 deletions app/wyzebridge/mqtt.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,25 @@ def publish_discovery(cam_uri: str, cam: WyzeCamera, stopped: bool = True) -> No
},
}

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

for entity, data in get_entities(base, cam.is_pan_cam, cam.rtsp_fw).items():
topic = f"{MQTT_DISCOVERY}/{data['type']}/{cam.mac}/{entity}/config"
if "availability_topic" not in data["payload"]:
data["payload"]["availability_topic"] = f"{MQTT_TOPIC}/state"

payload = dict(
base_payload | data["payload"],
name=f"Wyze Cam {cam.nickname} {' '.join(entity.upper().split('_'))}",
name=f"Wyze Cam {cam.nickname} {' '.join(entity.title().split('_'))}",
uniq_id=f"WYZE{cam.mac}{entity.upper()}",
)

msgs.append((topic, json.dumps(payload)))

send_mqtt(msgs)
publish_messages(msgs)


@mqtt_enabled
Expand All @@ -87,18 +92,16 @@ def mqtt_sub_topic(m_topics: list, callback) -> Optional[paho.mqtt.client.Client
return client


def bridge_status(client: Optional[paho.mqtt.client.Client], cams: list):
def bridge_status(client: Optional[paho.mqtt.client.Client]):
"""Set bridge online if MQTT is enabled."""
if not client:
return
client.publish(f"{MQTT_TOPIC}/state", "online")
for cam in cams:
client.publish(f"{MQTT_TOPIC}/{cam}/state", "online")


@mqtt_enabled
def send_mqtt(messages: list) -> None:
"""Publish a message to the MQTT server."""
def publish_messages(messages: list) -> None:
"""Publish multiple messages to the MQTT server."""
paho.mqtt.publish.multiple(
messages,
hostname=MQTT_HOST,
Expand All @@ -112,7 +115,7 @@ def send_mqtt(messages: list) -> None:


@mqtt_enabled
def publish_message(topic: str, message=None):
def publish_topic(topic: str, message=None, retain=True):
paho.mqtt.publish.single(
topic=f"{MQTT_TOPIC}/{topic}",
payload=message,
Expand All @@ -123,20 +126,24 @@ def publish_message(topic: str, message=None):
if env_bool("MQTT_AUTH")
else None
),
retain=retain,
)


@mqtt_enabled
def update_mqtt_state(camera: str, state: str):
return publish_message(f"{camera}/state", state)
msg = [(f"{MQTT_TOPIC}/{camera}/state", state)]
if state == "online":
msg.append((f"{MQTT_TOPIC}/{camera}/power", "on"))
publish_messages(msg)


@mqtt_enabled
def update_preview(cam_name: str):
with contextlib.suppress(FileNotFoundError):
img_file = f"{IMG_PATH}{cam_name}.{env_bool('IMG_TYPE','jpg')}"
with open(img_file, "rb") as img:
publish_message(f"{cam_name}/image", img.read())
publish_topic(f"{cam_name}/image", img.read())


@mqtt_enabled
Expand All @@ -162,7 +169,7 @@ def _mqtt_discovery(client, cams, msg):
if msg.payload.decode().lower() != "online" or not cams:
return

bridge_status(client, [])
bridge_status(client)
for uri, cam in cams.items():
publish_discovery(uri, cam, False)

Expand Down Expand Up @@ -199,13 +206,35 @@ def get_entities(base_topic: str, pan_cam: bool = False, rtsp: bool = False) ->
"icon": "mdi:cctv",
},
},
"stream": {
"type": "switch",
"payload": {
"state_topic": f"{base_topic}state",
"command_topic": f"{base_topic}state/set",
"payload_on": "start",
"state_on": "online",
"payload_off": "stop",
"state_off": "stopped",
"icon": "mdi:play-pause",
},
},
"power": {
"type": "switch",
"payload": {
"state_topic": f"{base_topic}power",
"command_topic": f"{base_topic}power/set",
"payload_on": "on",
"payload_off": "off",
"icon": "mdi:power-plug",
},
},
"update_snapshot": {
"type": "button",
"payload": {
"command_topic": f"{base_topic}update_snapshot/get",
"icon": "mdi:camera",
},
},
"ir": {
"type": "switch",
"payload": {
Expand All @@ -227,7 +256,7 @@ def get_entities(base_topic: str, pan_cam: bool = False, rtsp: bool = False) ->
},
},
"alarm": {
"type": "switch",
"type": "siren",
"payload": {
"state_topic": f"{base_topic}alarm",
"command_topic": f"{base_topic}alarm/set",
Expand Down Expand Up @@ -305,6 +334,15 @@ def get_entities(base_topic: str, pan_cam: bool = False, rtsp: bool = False) ->
"entity_category": "diagnostic",
},
},
"reboot": {
"type": "button",
"payload": {
"command_topic": f"{base_topic}power/set",
"payload_press": "restart",
"icon": "mdi:restart",
"entity_category": "diagnostic",
},
},
}
if pan_cam:
entities |= {
Expand All @@ -328,6 +366,23 @@ def get_entities(base_topic: str, pan_cam: bool = False, rtsp: bool = False) ->
"icon": "mdi:motion-sensor",
},
},
"reset_rotation": {
"type": "button",
"payload": {
"command_topic": f"{base_topic}reset_rotation/set",
"icon": "mdi:restore",
},
},
"cruise_point": {
"type": "select",
"payload": {
"state_topic": f"{base_topic}cruise_point",
"command_topic": f"{base_topic}cruise_point/set",
"optimistic": False,
"options": ["-", "1", "2", "3", "4"],
"icon": "mdi:map-marker-multiple",
},
},
}
if rtsp:
entities |= {
Expand Down
16 changes: 13 additions & 3 deletions app/wyzebridge/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from wyzebridge.config import MQTT_DISCOVERY, SNAPSHOT_INT, SNAPSHOT_TYPE
from wyzebridge.ffmpeg import rtsp_snap_cmd
from wyzebridge.logging import logger
from wyzebridge.mqtt import bridge_status, cam_control, publish_message, update_preview
from wyzebridge.mqtt import bridge_status, cam_control, publish_topic, update_preview
from wyzebridge.rtsp_event import RtspEvent


Expand Down Expand Up @@ -105,7 +105,7 @@ def monitor_streams(self, mtx_health: Callable) -> None:
self.snap_all(cams)
if int(time.time()) % 15 == 0:
mtx_health()
bridge_status(mqtt, cams)
bridge_status(mqtt)
if mqtt:
mqtt.loop_stop()
logger.info("Stream monitoring stopped")
Expand Down Expand Up @@ -172,7 +172,17 @@ def send_cmd(
status = cam_resp.get("value") if cam_resp.get("status") == "success" else 0
if isinstance(status, dict):
status = json.dumps(status)
publish_message(f"{cam_name}/{cmd}", status)

if "update_snapshot" in cam_resp:
on_demand = not stream.connected
snap = self.get_rtsp_snap(cam_name)
if on_demand:
stream.stop()
publish_topic(f"{cam_name}/{cmd}", int(time.time()) if snap else 0)
return dict(resp, status="success", value=snap, response=snap)

publish_topic(f"{cam_name}/{cmd}", status)

return cam_resp if "status" in cam_resp else resp | cam_resp

def rtsp_snap_popen(self, cam_name: str, interval: bool = False) -> Optional[Popen]:
Expand Down
1 change: 1 addition & 0 deletions app/wyzebridge/wyze_commands.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
GET_CMDS = {
"state": None,
"power": None,
"update_snapshot": None,
"take_photo": "K10058TakePhoto",
"irled": "K10044GetIRLEDStatus",
"night_vision": "K10040GetNightVisionStatus",
Expand Down
Loading

0 comments on commit f4c57c4

Please sign in to comment.