feat!: move recording_url and motion_region to attributes

these are no longer sensors themselves but attributes of the
motion sensor and they update when a change to motion happens

also, store_recording_in_media now returns the file name
instead of the full path
pull/106/head
Jeff Culverhouse 3 months ago
parent c16680bd04
commit 459a7f023a

@ -138,19 +138,12 @@ class AmcrestMixin:
"name": "Motion", "name": "Motion",
"uniq_id": self.mqtt_helper.dev_unique_id(device_id, "motion"), "uniq_id": self.mqtt_helper.dev_unique_id(device_id, "motion"),
"stat_t": self.mqtt_helper.stat_t(device_id, "binary_sensor", "motion"), "stat_t": self.mqtt_helper.stat_t(device_id, "binary_sensor", "motion"),
"jsn_atr_t": self.mqtt_helper.stat_t(device_id, "attributes"), "json_attributes_topic": self.mqtt_helper.stat_t(device_id, "attributes"),
"payload_on": True, "payload_on": True,
"payload_off": False, "payload_off": False,
"device_class": "motion", "device_class": "motion",
"icon": "mdi:eye-outline", "icon": "mdi:eye-outline",
}, },
"motion_region": {
"p": "sensor",
"name": "Motion region",
"uniq_id": self.mqtt_helper.dev_unique_id(device_id, "motion_region"),
"stat_t": self.mqtt_helper.stat_t(device_id, "sensor", "motion_region"),
"icon": "mdi:map-marker",
},
"motion_snapshot": { "motion_snapshot": {
"p": "image", "p": "image",
"name": "Motion snapshot", "name": "Motion snapshot",
@ -243,15 +236,6 @@ class AmcrestMixin:
}, },
} }
if "media" in self.config and "media_source" in self.config["media"]:
device["cmps"]["recording_url"] = {
"p": "sensor",
"name": "Recording url",
"uniq_id": self.mqtt_helper.dev_unique_id(device_id, "recording_url"),
"stat_t": self.mqtt_helper.stat_t(device_id, "sensor", "recording_url"),
"icon": "mdi:web",
}
if camera.get("is_doorbell", None): if camera.get("is_doorbell", None):
device["cmps"]["doorbell"] = { device["cmps"]["doorbell"] = {
"p": "binary_sensor", "p": "binary_sensor",
@ -283,8 +267,10 @@ class AmcrestMixin:
webrtc=rtc_url, webrtc=rtc_url,
switch={"save_recordings": "ON" if "path" in self.config["media"] else "OFF"}, switch={"save_recordings": "ON" if "path" in self.config["media"] else "OFF"},
binary_sensor={"motion": False}, binary_sensor={"motion": False},
sensor={"motion_region": ""}, attributes={
attributes={"recording_url": f"{self.config["media"]["media_source"]}/{camera["device_name"]}-latest.mp4"}, "recording_url": f"{self.config["media"]["media_source"]}/{camera["device_name"]}-latest.mp4",
"region": "",
},
image={"motion_snapshot": ""}, image={"motion_snapshot": ""},
) )

@ -32,7 +32,10 @@ class EventsMixin:
event += ": snapshot" event += ": snapshot"
elif payload["file"].endswith(".mp4"): elif payload["file"].endswith(".mp4"):
if "path" in self.config["media"] and self.states[device_id]["switch"].get("save_recordings", "OFF") == "ON": if "path" in self.config["media"] and self.states[device_id]["switch"].get("save_recordings", "OFF") == "ON":
await self.store_recording_in_media(device_id, payload["file"]) file_name = await self.store_recording_in_media(device_id, payload["file"])
if file_name:
self.upsert_state(device_id, attributes={"recording_url": f"{self.config["media"]["media_source"]}/{file_name}"})
needs_publish.add(device_id)
event += ": video" event += ": video"
elif event == "motion": elif event == "motion":
region = payload["region"] if payload["state"] != "off" else "" region = payload["region"] if payload["state"] != "off" else ""
@ -41,7 +44,7 @@ class EventsMixin:
self.upsert_state( self.upsert_state(
device_id, device_id,
binary_sensor={"motion": payload["state"]}, binary_sensor={"motion": payload["state"]},
sensor={"motion_region": region}, attributes={"region": region},
) )
needs_publish.add(device_id) needs_publish.add(device_id)
event += motion event += motion

@ -12,7 +12,7 @@ import threading
from types import FrameType from types import FrameType
import yaml import yaml
from pathlib import Path from pathlib import Path
from datetime import datetime, timezone from datetime import datetime
from typing import TYPE_CHECKING, Any, cast from typing import TYPE_CHECKING, Any, cast
if TYPE_CHECKING: if TYPE_CHECKING:
@ -272,11 +272,7 @@ class HelpersMixin:
self.logger.error(f"failed to save recording to {file_path}: {err!r}") self.logger.error(f"failed to save recording to {file_path}: {err!r}")
return None return None
self.upsert_state( # update the latest symlink
device_id,
media={"recording": str(file_path)},
sensor={"recording_time": datetime.now(timezone.utc).isoformat()},
)
local_file = Path(f"./{file_name}") local_file = Path(f"./{file_name}")
latest_link = Path(f"{path}/{name}-latest.mp4") latest_link = Path(f"{path}/{name}-latest.mp4")
@ -288,29 +284,7 @@ class HelpersMixin:
self.logger.error(f"failed to save symlink {latest_link} -> {local_file}: {err!r}") self.logger.error(f"failed to save symlink {latest_link} -> {local_file}: {err!r}")
pass pass
if "media_source" in self.config["media"]: return file_name
url = f"{self.config["media"]["media_source"]}/{file_name}"
self.upsert_state(device_id, sensor={"recording_url": url})
return url
self.upsert_state(
device_id,
media={"recording": file_path},
sensor={"recording_time": datetime.now(timezone.utc).isoformat()},
)
# update symlink to "lastest" recording
local_file = Path(f"./{file_name}")
latest_link = Path(f"{path}/{name}-latest.mp4")
if latest_link.is_symlink():
latest_link.unlink()
latest_link.symlink_to(local_file)
if "media_source" in self.config["media"]:
url = f"{self.config["media"]["media_source"]}/{file_name}"
self.upsert_state(device_id, sensor={"recording_url": url})
return url
return None
def handle_signal(self: Amcrest2Mqtt, signum: int, _: FrameType | None) -> Any: def handle_signal(self: Amcrest2Mqtt, signum: int, _: FrameType | None) -> Any:
sig_name = signal.Signals(signum).name sig_name = signal.Signals(signum).name

@ -169,7 +169,11 @@ class PublishMixin:
for state, value in self.states[device_id].items(): for state, value in self.states[device_id].items():
if subject and state != subject: if subject and state != subject:
continue continue
if isinstance(value, dict): # Attributes need to be published as a single JSON object to the attributes topic
if state == "attributes" and isinstance(value, dict):
topic = self.mqtt_helper.stat_t(device_id, "attributes")
await asyncio.to_thread(self.mqtt_helper.safe_publish, topic, json.dumps(value))
elif isinstance(value, dict):
for k, v in value.items(): for k, v in value.items():
if sub and k != sub: if sub and k != sub:
continue continue

Loading…
Cancel
Save