import { h } from 'preact'; import Card from '../components/Card.jsx'; import Button from '../components/Button.jsx'; import Heading from '../components/Heading.jsx'; import Switch from '../components/Switch.jsx'; import { useResizeObserver } from '../hooks'; import { useCallback, useMemo, useRef, useState } from 'preact/hooks'; import { useApiHost, useConfig } from '../api'; export default function CameraMasks({ camera, url }) { const { data: config } = useConfig(); const apiHost = useApiHost(); const imageRef = useRef(null); const [snap, setSnap] = useState(true); const cameraConfig = config.cameras[camera]; const { width, height, motion: { mask: motionMask }, objects: { filters: objectFilters }, zones, } = cameraConfig; const [{ width: scaledWidth }] = useResizeObserver(imageRef); const imageScale = scaledWidth / width; const [motionMaskPoints, setMotionMaskPoints] = useState( Array.isArray(motionMask) ? motionMask.map((mask) => getPolylinePoints(mask)) : motionMask ? [getPolylinePoints(motionMask)] : [] ); const [zonePoints, setZonePoints] = useState( Object.keys(zones).reduce((memo, zone) => ({ ...memo, [zone]: getPolylinePoints(zones[zone].coordinates) }), {}) ); const [objectMaskPoints, setObjectMaskPoints] = useState( Object.keys(objectFilters).reduce( (memo, name) => ({ ...memo, [name]: Array.isArray(objectFilters[name].mask) ? objectFilters[name].mask.map((mask) => getPolylinePoints(mask)) : objectFilters[name].mask ? [getPolylinePoints(objectFilters[name].mask)] : [], }), {} ) ); const [editing, setEditing] = useState({ set: motionMaskPoints, key: 0, fn: setMotionMaskPoints }); const handleUpdateEditable = useCallback( (newPoints) => { let newSet; if (Array.isArray(editing.set)) { newSet = [...editing.set]; newSet[editing.key] = newPoints; } else if (editing.subkey !== undefined) { newSet = { ...editing.set }; newSet[editing.key][editing.subkey] = newPoints; } else { newSet = { ...editing.set, [editing.key]: newPoints }; } editing.set = newSet; editing.fn(newSet); }, [editing] ); // Motion mask methods const handleAddMask = useCallback(() => { const newMotionMaskPoints = [...motionMaskPoints, []]; setMotionMaskPoints(newMotionMaskPoints); setEditing({ set: newMotionMaskPoints, key: newMotionMaskPoints.length - 1, fn: setMotionMaskPoints }); }, [motionMaskPoints, setMotionMaskPoints]); const handleEditMask = useCallback( (key) => { setEditing({ set: motionMaskPoints, key, fn: setMotionMaskPoints }); }, [setEditing, motionMaskPoints, setMotionMaskPoints] ); const handleRemoveMask = useCallback( (key) => { const newMotionMaskPoints = [...motionMaskPoints]; newMotionMaskPoints.splice(key, 1); setMotionMaskPoints(newMotionMaskPoints); }, [motionMaskPoints, setMotionMaskPoints] ); const handleCopyMotionMasks = useCallback(async () => { await window.navigator.clipboard.writeText(` motion: mask: ${motionMaskPoints.map((mask, i) => ` - ${polylinePointsToPolyline(mask)}`).join('\n')}`); }, [motionMaskPoints]); // Zone methods const handleEditZone = useCallback( (key) => { setEditing({ set: zonePoints, key, fn: setZonePoints }); }, [setEditing, zonePoints, setZonePoints] ); const handleAddZone = useCallback(() => { const n = Object.keys(zonePoints).filter((name) => name.startsWith('zone_')).length; const zoneName = `zone_${n}`; const newZonePoints = { ...zonePoints, [zoneName]: [] }; setZonePoints(newZonePoints); setEditing({ set: newZonePoints, key: zoneName, fn: setZonePoints }); }, [zonePoints, setZonePoints]); const handleRemoveZone = useCallback( (key) => { const newZonePoints = { ...zonePoints }; delete newZonePoints[key]; setZonePoints(newZonePoints); }, [zonePoints, setZonePoints] ); const handleCopyZones = useCallback(async () => { await window.navigator.clipboard.writeText(` zones: ${Object.keys(zonePoints) .map( (zoneName) => ` ${zoneName}: coordinates: ${polylinePointsToPolyline(zonePoints[zoneName])}` ) .join('\n')}`); }, [zonePoints]); // Object methods const handleEditObjectMask = useCallback( (key, subkey) => { setEditing({ set: objectMaskPoints, key, subkey, fn: setObjectMaskPoints }); }, [setEditing, objectMaskPoints, setObjectMaskPoints] ); const handleAddObjectMask = useCallback(() => { const n = Object.keys(objectMaskPoints).filter((name) => name.startsWith('object_')).length; const newObjectName = `object_${n}`; const newObjectMaskPoints = { ...objectMaskPoints, [newObjectName]: [[]] }; setObjectMaskPoints(newObjectMaskPoints); setEditing({ set: newObjectMaskPoints, key: newObjectName, subkey: 0, fn: setObjectMaskPoints }); }, [objectMaskPoints, setObjectMaskPoints, setEditing]); const handleRemoveObjectMask = useCallback( (key, subkey) => { const newObjectMaskPoints = { ...objectMaskPoints }; delete newObjectMaskPoints[key][subkey]; setObjectMaskPoints(newObjectMaskPoints); }, [objectMaskPoints, setObjectMaskPoints] ); const handleCopyObjectMasks = useCallback(async () => { await window.navigator.clipboard.writeText(` objects: filters: ${Object.keys(objectMaskPoints) .map((objectName) => objectMaskPoints[objectName].length ? ` ${objectName}: mask: ${polylinePointsToPolyline(objectMaskPoints[objectName])}` : '' ) .filter(Boolean) .join('\n')}`); }, [objectMaskPoints]); const handleAddToObjectMask = useCallback( (key) => { const newObjectMaskPoints = { ...objectMaskPoints, [key]: [...objectMaskPoints[key], []] }; setObjectMaskPoints(newObjectMaskPoints); setEditing({ set: newObjectMaskPoints, key, subkey: newObjectMaskPoints[key].length - 1, fn: setObjectMaskPoints, }); }, [objectMaskPoints, setObjectMaskPoints, setEditing] ); const handleChangeSnap = useCallback( (id, value) => { setSnap(value); }, [setSnap] ); return (
config.yml
file restart your Frigate instance to
save your changes.
}
header="Warning"
/>
{yamlPrefix} {Object.keys(points).map((mainkey) => { if (isMulti) { return ({` ${mainkey}:\n mask:\n`} {onAdd && showButtons ? ( ) : null} {points[mainkey].map((item, subkey) => (); } return (- ))}
- ); })}