Editor support for dependent cameras.
This commit is contained in:
+2
-6
@@ -48,6 +48,7 @@ import {
|
||||
frigateCardHasAction,
|
||||
getActionConfigGivenAction,
|
||||
getCameraIcon,
|
||||
getCameraID,
|
||||
getCameraTitle,
|
||||
homeAssistantSignPath,
|
||||
homeAssistantWSRequest,
|
||||
@@ -467,12 +468,7 @@ export class FrigateCard extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
const id =
|
||||
config.id ||
|
||||
config.camera_entity ||
|
||||
config.webrtc_card?.entity ||
|
||||
config.camera_name;
|
||||
|
||||
const id = getCameraID(config);
|
||||
if (!id) {
|
||||
this._setMessageAndUpdate({
|
||||
message: localize('error.no_camera_id'),
|
||||
|
||||
+39
-6
@@ -28,6 +28,7 @@ import {
|
||||
FrigateEvent,
|
||||
MediaShowInfo,
|
||||
Message,
|
||||
RawFrigateCardConfig,
|
||||
SignedPath,
|
||||
signedPathSchema,
|
||||
StateParameters,
|
||||
@@ -463,21 +464,53 @@ export function getEntityIcon(
|
||||
return hass && entity ? stateIcon(hass.states[entity]) : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a camera id.
|
||||
* @param config The camera config (either parsed or raw).
|
||||
* @returns A camera id.
|
||||
*/
|
||||
export function getCameraID(
|
||||
config?: CameraConfig | RawFrigateCardConfig | null,
|
||||
): string {
|
||||
return (
|
||||
(typeof config?.id === 'string' && config.id) ||
|
||||
(typeof config?.camera_entity === 'string' && config.camera_entity) ||
|
||||
(typeof config?.webrtc_card === 'object' && config.webrtc_card && typeof config.webrtc_card['entity'] === 'string' && config.webrtc_card['entity']) ||
|
||||
(typeof config?.camera_name === 'string' && config.camera_name) ||
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a camera text title.
|
||||
* @param hass The Home Assistant object.
|
||||
* @param config The camera config.
|
||||
* @param config The camera config (either parsed or raw).
|
||||
* @returns A title string.
|
||||
*/
|
||||
export function getCameraTitle(
|
||||
hass?: HomeAssistant,
|
||||
config?: CameraConfig | null,
|
||||
config?: CameraConfig | RawFrigateCardConfig | null,
|
||||
): string {
|
||||
// Attempt to render a recognizable name for the camera,
|
||||
// starting with the most likely to be useful and working our
|
||||
// ways towards the least useful. Extra type checking here since this is also
|
||||
// used on raw configuration in the editor.
|
||||
return (
|
||||
config?.title ||
|
||||
(config?.camera_entity ? getEntityTitle(hass, config.camera_entity) : '') ||
|
||||
(config?.camera_name ? prettifyFrigateName(config.camera_name) : '') ||
|
||||
config?.id ||
|
||||
(typeof config?.title === 'string' && config.title) ||
|
||||
(typeof config?.id === 'string' && config.id) ||
|
||||
[
|
||||
typeof config?.camera_entity === 'string'
|
||||
? getEntityTitle(hass, config.camera_entity)
|
||||
: '',
|
||||
typeof config?.client_id === 'string' ? config.client_id : '',
|
||||
typeof config?.camera_name === 'string'
|
||||
? prettifyFrigateName(config.camera_name)
|
||||
: '',
|
||||
typeof config?.label === 'string' ? prettifyFrigateName(config.label) : '',
|
||||
typeof config?.zone === 'string' ? prettifyFrigateName(config.zone) : '',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' / ') ||
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ export const CONF_CAMERAS_ARRAY_WEBRTC_CARD_ENTITY =
|
||||
export const CONF_CAMERAS_ARRAY_WEBRTC_CARD_URL = `${CONF_CAMERAS}.#.webrtc_card.url` as const;
|
||||
export const CONF_CAMERAS_ARRAY_LIVE_PROVIDER =
|
||||
`${CONF_CAMERAS}.#.live_provider` as const;
|
||||
export const CONF_CAMERAS_ARRAY_DEPENDENT_CAMERAS =
|
||||
`${CONF_CAMERAS}.#.dependent_cameras` as const;
|
||||
|
||||
export const CONF_VIEW = 'view' as const;
|
||||
export const CONF_VIEW_CAMERA_SELECT = `${CONF_VIEW}.camera_select` as const;
|
||||
|
||||
+67
-45
@@ -6,6 +6,7 @@ import { HomeAssistant, LovelaceCardEditor, fireEvent } from 'custom-card-helper
|
||||
import { localize } from './localize/localize.js';
|
||||
import {
|
||||
BUTTON_SIZE_MIN,
|
||||
CameraConfig,
|
||||
RawFrigateCardConfig,
|
||||
RawFrigateCardConfigArray,
|
||||
THUMBNAIL_WIDTH_MAX,
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
CONF_CAMERAS_ARRAY_CAMERA_ENTITY,
|
||||
CONF_CAMERAS_ARRAY_CAMERA_NAME,
|
||||
CONF_CAMERAS_ARRAY_CLIENT_ID,
|
||||
CONF_CAMERAS_ARRAY_DEPENDENT_CAMERAS,
|
||||
CONF_CAMERAS_ARRAY_ICON,
|
||||
CONF_CAMERAS_ARRAY_ID,
|
||||
CONF_CAMERAS_ARRAY_LABEL,
|
||||
@@ -88,7 +90,13 @@ import {
|
||||
CONF_VIEW_UPDATE_FORCE,
|
||||
CONF_VIEW_UPDATE_SECONDS,
|
||||
} from './const.js';
|
||||
import { arrayMove, getEntityTitle, prettifyFrigateName } from './common.js';
|
||||
import {
|
||||
arrayMove,
|
||||
getCameraID,
|
||||
getCameraTitle,
|
||||
getEntityTitle,
|
||||
prettifyFrigateName,
|
||||
} from './common.js';
|
||||
import {
|
||||
copyConfig,
|
||||
deleteConfigValue,
|
||||
@@ -119,6 +127,11 @@ interface EditorOptionSetTarget {
|
||||
optionSetName: string;
|
||||
}
|
||||
|
||||
interface EditorSelectOption {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
const options: EditorOptions = {
|
||||
cameras: {
|
||||
icon: 'video',
|
||||
@@ -193,7 +206,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
@property({ attribute: false })
|
||||
protected _expandedCameraIndex: number | null = null;
|
||||
|
||||
protected _viewModes = [
|
||||
protected _viewModes: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{ value: 'live', label: localize('config.view.views.live') },
|
||||
{ value: 'clips', label: localize('config.view.views.clips') },
|
||||
@@ -204,12 +217,12 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
{ value: 'timeline', label: localize('config.view.views.timeline') },
|
||||
];
|
||||
|
||||
protected _cameraSelectViewModes = [
|
||||
protected _cameraSelectViewModes: EditorSelectOption[] = [
|
||||
...this._viewModes,
|
||||
{ value: 'current', label: localize('config.view.views.current') },
|
||||
];
|
||||
|
||||
protected _menuModes = [
|
||||
protected _menuModes: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{ value: 'none', label: localize('config.menu.modes.none') },
|
||||
{ value: 'hidden-top', label: localize('config.menu.modes.hidden-top') },
|
||||
@@ -228,7 +241,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
{ value: 'below', label: localize('config.menu.modes.below') },
|
||||
];
|
||||
|
||||
protected _eventViewerNextPreviousControlStyles = [
|
||||
protected _eventViewerNextPreviousControlStyles: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{
|
||||
value: 'thumbnails',
|
||||
@@ -244,7 +257,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
},
|
||||
];
|
||||
|
||||
protected _liveNextPreviousControlStyles = [
|
||||
protected _liveNextPreviousControlStyles: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{
|
||||
value: 'chevrons',
|
||||
@@ -257,7 +270,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
{ value: 'none', label: localize('config.live.controls.next_previous.styles.none') },
|
||||
];
|
||||
|
||||
protected _aspectRatioModes = [
|
||||
protected _aspectRatioModes: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{
|
||||
value: 'dynamic',
|
||||
@@ -270,7 +283,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
},
|
||||
];
|
||||
|
||||
protected _thumbnailModes = [
|
||||
protected _thumbnailModes: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{
|
||||
value: 'none',
|
||||
@@ -294,7 +307,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
},
|
||||
];
|
||||
|
||||
protected _thumbnailMedias = [
|
||||
protected _thumbnailMedias: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{ value: 'clips', label: localize('config.live.controls.thumbnails.medias.clips') },
|
||||
{
|
||||
@@ -303,7 +316,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
},
|
||||
];
|
||||
|
||||
protected _titleModes = [
|
||||
protected _titleModes: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{ value: 'none', label: localize('config.event_viewer.controls.title.modes.none') },
|
||||
{
|
||||
@@ -324,27 +337,27 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
},
|
||||
];
|
||||
|
||||
protected _transitionEffects = [
|
||||
protected _transitionEffects: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{ value: 'none', label: localize('config.event_viewer.transition_effects.none') },
|
||||
{ value: 'slide', label: localize('config.event_viewer.transition_effects.slide') },
|
||||
];
|
||||
|
||||
protected _imageModes = [
|
||||
protected _imageModes: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{ value: 'camera', label: localize('config.image.modes.camera') },
|
||||
{ value: 'screensaver', label: localize('config.image.modes.screensaver') },
|
||||
{ value: 'url', label: localize('config.image.modes.url') },
|
||||
];
|
||||
|
||||
protected _timelineMediaTypes = [
|
||||
protected _timelineMediaTypes: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{ value: 'all', label: localize('config.timeline.medias.all') },
|
||||
{ value: 'clips', label: localize('config.timeline.medias.clips') },
|
||||
{ value: 'snapshots', label: localize('config.timeline.medias.snapshots') },
|
||||
];
|
||||
|
||||
protected _darkModes = [
|
||||
protected _darkModes: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{ value: 'on', label: localize('config.view.dark_modes.on') },
|
||||
{ value: 'off', label: localize('config.view.dark_modes.off') },
|
||||
@@ -456,6 +469,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
protected _renderOptionSelector(
|
||||
configPath: string,
|
||||
options: string[] | { value: string; label: string }[],
|
||||
multiple?: boolean,
|
||||
): TemplateResult | void {
|
||||
if (!this._config) {
|
||||
return;
|
||||
@@ -464,7 +478,9 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
return html`
|
||||
<ha-selector
|
||||
.hass=${this.hass}
|
||||
.selector=${{ select: { options: options } }}
|
||||
.selector=${{
|
||||
select: { mode: 'dropdown', multiple: !!multiple, options: options },
|
||||
}}
|
||||
.label=${this._getLabel(configPath)}
|
||||
.value=${getConfigValue(this._config, configPath, '')}
|
||||
.required=${false}
|
||||
@@ -516,6 +532,22 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
return html` <span class="info">${info}</span>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an editor title for the camera.
|
||||
* @param cameraIndex The index of the camera in the cameras array.
|
||||
* @param cameraConfig The raw camera configuration object.
|
||||
* @returns A string title.
|
||||
*/
|
||||
protected _getEditorCameraTitle(
|
||||
cameraIndex: number,
|
||||
cameraConfig: RawFrigateCardConfig,
|
||||
): string {
|
||||
return (
|
||||
getCameraTitle(this.hass, cameraConfig) ||
|
||||
localize('editor.camera') + ' #' + cameraIndex
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a camera header.
|
||||
* @param cameraIndex The index of the camera to edit/add.
|
||||
@@ -540,31 +572,9 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
? html` <span class="new-camera">
|
||||
[${localize('editor.add_new_camera')}...]
|
||||
</span>`
|
||||
: // Attempt to render a recognizable name for the camera,
|
||||
// starting with the most likely to be useful and working our
|
||||
// ways towards the least useful.
|
||||
html` <span>
|
||||
${cameraConfig?.title ||
|
||||
cameraConfig?.id ||
|
||||
[
|
||||
cameraConfig?.camera_entity
|
||||
? getEntityTitle(this.hass, String(cameraConfig.camera_entity))
|
||||
: '',
|
||||
cameraConfig?.client_id,
|
||||
cameraConfig?.camera_name
|
||||
? prettifyFrigateName(String(cameraConfig.camera_name))
|
||||
: '',
|
||||
cameraConfig?.label
|
||||
? prettifyFrigateName(String(cameraConfig.label))
|
||||
: '',
|
||||
cameraConfig?.zone
|
||||
? prettifyFrigateName(String(cameraConfig.zone))
|
||||
: '',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' / ') ||
|
||||
localize('editor.camera') + ' #' + cameraIndex}
|
||||
</span>`}
|
||||
: html`<span
|
||||
>${this._getEditorCameraTitle(cameraIndex, cameraConfig || {})}</span
|
||||
>`}
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
@@ -582,7 +592,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
cameraIndex: number,
|
||||
addNewCamera?: boolean,
|
||||
): TemplateResult | void {
|
||||
const liveProviders = [
|
||||
const liveProviders: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{ value: 'auto', label: localize('config.cameras.live_providers.auto') },
|
||||
{ value: 'ha', label: localize('config.cameras.live_providers.ha') },
|
||||
@@ -596,6 +606,16 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
},
|
||||
];
|
||||
|
||||
const dependentCameras: EditorSelectOption[] = [];
|
||||
cameras.forEach((camera, index) => {
|
||||
if (index !== cameraIndex) {
|
||||
dependentCameras.push({
|
||||
value: getCameraID(camera),
|
||||
label: this._getEditorCameraTitle(index, camera),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Make a new config and update the editor with changes on it,
|
||||
const modifyConfig = (func: (config: RawFrigateCardConfig) => boolean): void => {
|
||||
if (this._config) {
|
||||
@@ -711,6 +731,11 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
${this._renderStringInput(
|
||||
getArrayConfigPath(CONF_CAMERAS_ARRAY_WEBRTC_CARD_URL, cameraIndex),
|
||||
)}
|
||||
${this._renderOptionSelector(
|
||||
getArrayConfigPath(CONF_CAMERAS_ARRAY_DEPENDENT_CAMERAS, cameraIndex),
|
||||
dependentCameras,
|
||||
true,
|
||||
)}
|
||||
</div>`
|
||||
: ``}
|
||||
`;
|
||||
@@ -841,10 +866,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
CONF_VIEW_CAMERA_SELECT,
|
||||
this._cameraSelectViewModes,
|
||||
)}
|
||||
${this._renderOptionSelector(
|
||||
CONF_VIEW_DARK_MODE,
|
||||
this._darkModes,
|
||||
)}
|
||||
${this._renderOptionSelector(CONF_VIEW_DARK_MODE, this._darkModes)}
|
||||
${this._renderNumberInput(CONF_VIEW_TIMEOUT_SECONDS)}
|
||||
${this._renderNumberInput(CONF_VIEW_UPDATE_SECONDS)}
|
||||
${this._renderSwitch(CONF_VIEW_UPDATE_FORCE, defaults.view.update_force)}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"camera_entity": "Camera Entity",
|
||||
"camera_name": "Frigate camera name (Autodetected from entity)",
|
||||
"client_id": "Frigate client id (For >1 Frigate server)",
|
||||
"dependent_cameras": "Dependent cameras to also show events for",
|
||||
"id": "Unique id for this camera in this card",
|
||||
"label": "Frigate label/object filter",
|
||||
"frigate_url": "Frigate server URL",
|
||||
|
||||
@@ -28,10 +28,6 @@
|
||||
display: grid;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
ha-formfield {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
div.upgrade {
|
||||
width: auto;
|
||||
border: 1px dotted var(--primary-color);
|
||||
@@ -79,3 +75,8 @@ div.upgrade span {
|
||||
span.info {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
ha-selector {
|
||||
padding: 10px;
|
||||
border: 1px solid var(--divider-color);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user