Initial low performance profile mode.

This commit is contained in:
Dermot Duffy
2022-10-04 20:00:53 -07:00
parent 5f30fc67ff
commit c8c9098501
14 changed files with 203 additions and 23 deletions
+2 -2
View File
@@ -4,7 +4,7 @@ import type {
RawFrigateCardConfig, RawFrigateCardConfig,
} from './types'; } from './types';
import { HassEntities } from 'home-assistant-js-websocket'; import { HassEntities } from 'home-assistant-js-websocket';
import { cloneDeep, merge } from 'lodash-es'; import { merge } from 'lodash-es';
export interface ConditionState { export interface ConditionState {
view?: string; view?: string;
@@ -109,7 +109,7 @@ export function getOverriddenConfig(
overrides: Readonly<RawOverrides> | undefined, overrides: Readonly<RawOverrides> | undefined,
conditionState?: Readonly<ConditionState>, conditionState?: Readonly<ConditionState>,
): RawFrigateCardConfig { ): RawFrigateCardConfig {
const output = cloneDeep(config); const output = structuredClone(config);
let overridden = false; let overridden = false;
if (overrides) { if (overrides) {
for (const override of overrides) { for (const override of overrides) {
+6 -2
View File
@@ -101,6 +101,7 @@ import { View } from './view.js';
import pkg from '../package.json'; import pkg from '../package.json';
import { ViewContext } from 'view'; import { ViewContext } from 'view';
import { DataManager } from './utils/data-manager.js'; import { DataManager } from './utils/data-manager.js';
import { setLowPerformanceProfile } from './performance.js';
/** A note on media callbacks: /** A note on media callbacks:
* *
@@ -933,7 +934,10 @@ export class FrigateCard extends LitElement {
: localize('error.invalid_configuration_no_hint')), : localize('error.invalid_configuration_no_hint')),
); );
} }
const config = parseResult.data; const config =
parseResult.data.performance.profile !== 'low'
? parseResult.data
: setLowPerformanceProfile(inputConfig, parseResult.data);
if (config.test_gui) { if (config.test_gui) {
getLovelace().setEditMode(true); getLovelace().setEditMode(true);
@@ -1052,7 +1056,7 @@ export class FrigateCard extends LitElement {
} }
if (this._cameras && (changedProps.has('_config') || changedProps.has('_cameras'))) { if (this._cameras && (changedProps.has('_config') || changedProps.has('_cameras'))) {
this._dataManager = new DataManager(this._cameras, this._config.timeline.media); this._dataManager = new DataManager(this._cameras);
} }
} }
+1 -1
View File
@@ -23,7 +23,7 @@ import {
getFullDependentBrowseMediaQueryParametersOrDispatchError, getFullDependentBrowseMediaQueryParametersOrDispatchError,
} from '../utils/ha/browse-media'; } from '../utils/ha/browse-media';
import { changeViewToRecentRecordingForCameraAndDependents } from '../utils/media-to-view.js'; import { changeViewToRecentRecordingForCameraAndDependents } from '../utils/media-to-view.js';
import { DataManager } from '../utils/data-manager'; import { DataManager } from '../utils/data-manager.js';
import { View } from '../view.js'; import { View } from '../view.js';
import { renderProgressIndicator } from './message.js'; import { renderProgressIndicator } from './message.js';
import './thumbnail.js'; import './thumbnail.js';
+4 -4
View File
@@ -52,9 +52,9 @@ export class FrigateCardNextPreviousControl extends LitElement {
const classes = { const classes = {
controls: true, controls: true,
previous: this.direction == 'previous', previous: this.direction === 'previous',
next: this.direction == 'next', next: this.direction === 'next',
thumbnails: this._controlConfig.style == 'thumbnails', thumbnails: this._controlConfig.style === 'thumbnails',
icons: ['chevrons', 'icons'].includes(this._controlConfig.style), icons: ['chevrons', 'icons'].includes(this._controlConfig.style),
button: ['chevrons', 'icons'].includes(this._controlConfig.style), button: ['chevrons', 'icons'].includes(this._controlConfig.style),
}; };
@@ -62,7 +62,7 @@ export class FrigateCardNextPreviousControl extends LitElement {
if (['chevrons', 'icons'].includes(this._controlConfig.style)) { if (['chevrons', 'icons'].includes(this._controlConfig.style)) {
let icon: string; let icon: string;
if (this._controlConfig.style === 'chevrons') { if (this._controlConfig.style === 'chevrons') {
icon = this.direction == 'previous' ? 'mdi:chevron-left' : 'mdi:chevron-right'; icon = this.direction === 'previous' ? 'mdi:chevron-left' : 'mdi:chevron-right';
} else { } else {
if (!this.icon) { if (!this.icon) {
return html``; return html``;
+2 -2
View File
@@ -84,7 +84,7 @@ export class FrigateCardSurround extends LitElement {
this.inBackground || this.inBackground ||
!this.hass || !this.hass ||
!this.view || !this.view ||
this.view.target || this.view.target ||
!this.thumbnailConfig || !this.thumbnailConfig ||
this.thumbnailConfig.mode === 'none' || this.thumbnailConfig.mode === 'none' ||
!this.browseMediaParams || !this.browseMediaParams ||
@@ -200,7 +200,7 @@ export class FrigateCardSurround extends LitElement {
> >
</frigate-card-thumbnail-carousel>` </frigate-card-thumbnail-carousel>`
: ''} : ''}
${this.timelineConfig && !this.inBackground ${this.timelineConfig?.mode && this.timelineConfig.mode !== 'none' && !this.inBackground
? html` <frigate-card-timeline-core ? html` <frigate-card-timeline-core
slot=${this.timelineConfig.mode} slot=${this.timelineConfig.mode}
.hass=${this.hass} .hass=${this.hass}
+3 -3
View File
@@ -1,4 +1,4 @@
import { cloneDeep, get, isEqual, set } from 'lodash-es'; import { get, isEqual, set } from 'lodash-es';
import { import {
CONF_CAMERAS, CONF_CAMERAS,
CONF_CAMERAS_ARRAY_CAMERA_ENTITY, CONF_CAMERAS_ARRAY_CAMERA_ENTITY,
@@ -104,7 +104,7 @@ export const upgradeConfig = function (obj: RawFrigateCardConfig): boolean {
* @returns `true` if the configuration is upgradeable. * @returns `true` if the configuration is upgradeable.
*/ */
export const isConfigUpgradeable = function (obj: RawFrigateCardConfig): boolean { export const isConfigUpgradeable = function (obj: RawFrigateCardConfig): boolean {
const newObj = JSON.parse(JSON.stringify(obj)); const newObj = structuredClone(obj);
return upgradeConfig(newObj); return upgradeConfig(newObj);
}; };
@@ -136,7 +136,7 @@ export const trimConfig = function (obj: RawFrigateCardConfig): boolean {
* @returns A new deeply-copied configuration. * @returns A new deeply-copied configuration.
*/ */
export const copyConfig = function (obj: RawFrigateCardConfig): RawFrigateCardConfig { export const copyConfig = function (obj: RawFrigateCardConfig): RawFrigateCardConfig {
return cloneDeep(obj); return structuredClone(obj);
}; };
/** /**
+2
View File
@@ -192,7 +192,9 @@ export const CONF_MENU_BUTTONS_FRIGATE_UI = `${CONF_MENU}.buttons.frigate_ui` as
export const CONF_MENU_BUTTONS_FULLSCREEN = `${CONF_MENU}.buttons.fullscreen` as const; export const CONF_MENU_BUTTONS_FULLSCREEN = `${CONF_MENU}.buttons.fullscreen` as const;
export const CONF_MENU_BUTTONS_IMAGE = `${CONF_MENU}.buttons.image` as const; export const CONF_MENU_BUTTONS_IMAGE = `${CONF_MENU}.buttons.image` as const;
export const CONF_MENU_BUTTONS_LIVE = `${CONF_MENU}.buttons.live` as const; export const CONF_MENU_BUTTONS_LIVE = `${CONF_MENU}.buttons.live` as const;
export const CONF_MENU_BUTTONS_MEDIA_PLAYER = `${CONF_MENU}.buttons.media_player` as const;
export const CONF_MENU_BUTTONS_SNAPSHOTS = `${CONF_MENU}.buttons.snapshots` as const; export const CONF_MENU_BUTTONS_SNAPSHOTS = `${CONF_MENU}.buttons.snapshots` as const;
export const CONF_MENU_BUTTONS_TIMELINE = `${CONF_MENU}.buttons.timeline` as const;
export const CONF_DIMENSIONS = 'dimensions' as const; export const CONF_DIMENSIONS = 'dimensions' as const;
export const CONF_DIMENSIONS_ASPECT_RATIO = `${CONF_DIMENSIONS}.aspect_ratio` as const; export const CONF_DIMENSIONS_ASPECT_RATIO = `${CONF_DIMENSIONS}.aspect_ratio` as const;
+25 -3
View File
@@ -300,6 +300,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
value: 'none', value: 'none',
label: localize('config.common.controls.next_previous.styles.none'), label: localize('config.common.controls.next_previous.styles.none'),
}, },
{
value: 'thumbnails',
label: localize('config.common.controls.next_previous.styles.thumbnails'),
},
]; ];
protected _aspectRatioModes: EditorSelectOption[] = [ protected _aspectRatioModes: EditorSelectOption[] = [
@@ -912,6 +916,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
domain: string, domain: string,
configPathStyle: string, configPathStyle: string,
configPathSize: string, configPathSize: string,
options?: {
allowIcons?: boolean;
allowThumbnails?: boolean;
},
): TemplateResult | void { ): TemplateResult | void {
return this._putInSubmenu( return this._putInSubmenu(
domain, domain,
@@ -919,9 +927,17 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
'config.common.controls.next_previous.editor_label', 'config.common.controls.next_previous.editor_label',
{ name: 'mdi:arrow-right-bold-circle' }, { name: 'mdi:arrow-right-bold-circle' },
html` html`
${this._renderOptionSelector(configPathStyle, this._nextPreviousControlStyles, { ${this._renderOptionSelector(
label: localize('config.common.controls.next_previous.style'), configPathStyle,
})} this._nextPreviousControlStyles.filter(
(item) =>
(!!options?.allowThumbnails || item.value !== 'thumbnails') &&
(!!options?.allowIcons || item.value !== 'icons'),
),
{
label: localize('config.common.controls.next_previous.style'),
},
)}
${this._renderNumberInput(configPathSize, { ${this._renderNumberInput(configPathSize, {
min: BUTTON_SIZE_MIN, min: BUTTON_SIZE_MIN,
label: localize('config.common.controls.next_previous.size'), label: localize('config.common.controls.next_previous.size'),
@@ -1494,6 +1510,9 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
MENU_LIVE_CONTROLS_NEXT_PREVIOUS, MENU_LIVE_CONTROLS_NEXT_PREVIOUS,
CONF_LIVE_CONTROLS_NEXT_PREVIOUS_STYLE, CONF_LIVE_CONTROLS_NEXT_PREVIOUS_STYLE,
CONF_LIVE_CONTROLS_NEXT_PREVIOUS_SIZE, CONF_LIVE_CONTROLS_NEXT_PREVIOUS_SIZE,
{
allowIcons: true,
},
)} )}
${this._renderThumbnailsControls( ${this._renderThumbnailsControls(
MENU_LIVE_CONTROLS_THUMBNAILS, MENU_LIVE_CONTROLS_THUMBNAILS,
@@ -1584,6 +1603,9 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
MENU_MEDIA_VIEWER_CONTROLS_NEXT_PREVIOUS, MENU_MEDIA_VIEWER_CONTROLS_NEXT_PREVIOUS,
CONF_MEDIA_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE, CONF_MEDIA_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE,
CONF_MEDIA_VIEWER_CONTROLS_NEXT_PREVIOUS_SIZE, CONF_MEDIA_VIEWER_CONTROLS_NEXT_PREVIOUS_SIZE,
{
allowThumbnails: true,
},
)} )}
${this._renderThumbnailsControls( ${this._renderThumbnailsControls(
MENU_MEDIA_VIEWER_CONTROLS_THUMBNAILS, MENU_MEDIA_VIEWER_CONTROLS_THUMBNAILS,
+2 -1
View File
@@ -89,7 +89,8 @@
"styles": { "styles": {
"chevrons": "Chevrons", "chevrons": "Chevrons",
"icons": "Icons", "icons": "Icons",
"none": "None" "none": "None",
"thumbnails": "Thumbnails"
} }
}, },
"timeline": { "timeline": {
+2 -1
View File
@@ -88,7 +88,8 @@
"styles": { "styles": {
"chevrons": "Chevrons", "chevrons": "Chevrons",
"icons": "Icone", "icons": "Icone",
"none": "Icone" "none": "Nessuno",
"thumbnails": "Miniature"
} }
}, },
"timeline": { "timeline": {
+2 -1
View File
@@ -88,7 +88,8 @@
"styles": { "styles": {
"chevrons": "Setas", "chevrons": "Setas",
"icons": "Ícones", "icons": "Ícones",
"none": "Nenhum" "none": "Nenhum",
"thumbnails": "Miniaturas"
} }
}, },
"timeline": { "timeline": {
+141
View File
@@ -0,0 +1,141 @@
// TODO: Change defaults themselves?
// TODO: Camera auto-dep detection
// TODO: CSS
// TODO: Renderprogress
import { deepRemoveDefaults } from './utils/zod.js';
import {
frigateCardConfigSchema,
FrigateCardConfig,
RawFrigateCardConfig,
} from './types';
import { getConfigValue, setConfigValue } from './config-mgmt.js';
import {
CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS,
CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
CONF_LIVE_AUTO_MUTE,
CONF_LIVE_AUTO_PLAY,
CONF_LIVE_CONTROLS_THUMBNAILS_MODE,
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DETAILS,
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS,
CONF_LIVE_CONTROLS_TITLE_MODE,
CONF_LIVE_DRAGGABLE,
CONF_LIVE_LAZY_UNLOAD,
CONF_LIVE_SHOW_IMAGE_DURING_LOAD,
CONF_LIVE_TRANSITION_EFFECT,
CONF_MEDIA_VIEWER_AUTO_MUTE,
CONF_MEDIA_VIEWER_AUTO_PAUSE,
CONF_MEDIA_VIEWER_AUTO_PLAY,
CONF_MEDIA_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE,
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_MODE,
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_DETAILS,
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS,
CONF_MEDIA_VIEWER_CONTROLS_TITLE_MODE,
CONF_MEDIA_VIEWER_DRAGGABLE,
CONF_MEDIA_VIEWER_TRANSITION_EFFECT,
CONF_MENU_BUTTONS_FRIGATE,
CONF_MENU_BUTTONS_MEDIA_PLAYER,
CONF_MENU_BUTTONS_TIMELINE,
CONF_MENU_STYLE,
CONF_TIMELINE_CONTROLS_THUMBNAILS_MODE,
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_DETAILS,
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
CONF_TIMELINE_SHOW_RECORDINGS,
} from './const.js';
// Caution: These values are applied after parsing (since we cannot know the
// performance profile until afterwards), so there is no validation on these
// defaults.
const LOW_PROFILE_DEFAULTS = {
// Disable thumbnail carousels.
[CONF_LIVE_CONTROLS_THUMBNAILS_MODE]: 'none' as const,
[CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_MODE]: 'none' as const,
[CONF_TIMELINE_CONTROLS_THUMBNAILS_MODE]: 'none' as const,
// Do not show recordings on timelines.
[CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS]: false,
[CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS]: false,
[CONF_TIMELINE_SHOW_RECORDINGS]: false,
// Take no automatic media actions.
[CONF_LIVE_AUTO_PLAY]: 'never' as const,
[CONF_LIVE_AUTO_MUTE]: 'never' as const,
[CONF_MEDIA_VIEWER_AUTO_PLAY]: 'never' as const,
[CONF_MEDIA_VIEWER_AUTO_PAUSE]: 'never' as const,
[CONF_MEDIA_VIEWER_AUTO_MUTE]: 'never' as const,
// Always unload resources that are lazily loaded.
[CONF_LIVE_LAZY_UNLOAD]: 'all' as const,
// Media carousels do not drag.
[CONF_LIVE_DRAGGABLE]: false,
[CONF_MEDIA_VIEWER_DRAGGABLE]: false,
// Media carousels have no effects.
[CONF_LIVE_TRANSITION_EFFECT]: 'none' as const,
[CONF_MEDIA_VIEWER_TRANSITION_EFFECT]: 'none' as const,
// Do not show image during load.
[CONF_LIVE_SHOW_IMAGE_DURING_LOAD]: false,
// Media player next/previous are chevrons.
[CONF_MEDIA_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE]: 'chevrons' as const,
[CONF_MEDIA_VIEWER_CONTROLS_TITLE_MODE]: 'none' as const,
[CONF_LIVE_CONTROLS_TITLE_MODE]: 'none' as const,
// Move the menu to outside to remove the need to interact with it with open.
[CONF_MENU_STYLE]: 'outside',
// Hide several buttons that are otherwise visible by default.
[`${CONF_MENU_BUTTONS_FRIGATE}.enabled`]: false,
[`${CONF_MENU_BUTTONS_TIMELINE}.enabled`]: false,
[`${CONF_MENU_BUTTONS_MEDIA_PLAYER}.enabled`]: false,
[`${CONF_MENU_BUTTONS_TIMELINE}.enabled`]: false,
// Disable all options in thumbnails.
[CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL]: false,
[CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL]: false,
[CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS]: false,
[CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL]: false,
[CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL]: false,
[CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DETAILS]: false,
[CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL]: false,
[CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL]: false,
[CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_DETAILS]: false,
[CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL]: false,
[CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL]: false,
[CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_DETAILS]: false,
};
/**
* Set low performance profile mode. Sets flags as defined in
* LOW_PROFILE_DEFAULTS unless they are explicitly overriden in the
* configuration.
* @param inputConfig The raw unparsed input configuration.
* @param parsedConfig The parsed input configuration.
* @returns A changed (in-place) parsed input configuration.
*/
export const setLowPerformanceProfile = (
inputConfig: RawFrigateCardConfig,
parsedConfig: FrigateCardConfig,
): FrigateCardConfig => {
const defaultLessParseResult = deepRemoveDefaults(frigateCardConfigSchema).safeParse(
inputConfig,
);
if (defaultLessParseResult.success) {
const defaultLessConfig = defaultLessParseResult.data;
Object.entries(LOW_PROFILE_DEFAULTS).forEach(([k, v]: [string, unknown]) => {
if (getConfigValue(defaultLessConfig, k) === undefined) {
setConfigValue(parsedConfig, k, v);
}
});
}
return parsedConfig;
};
+10
View File
@@ -1206,6 +1206,14 @@ const liveOverridesSchema = z
.optional(); .optional();
export type LiveOverrides = z.infer<typeof liveOverridesSchema>; export type LiveOverrides = z.infer<typeof liveOverridesSchema>;
const performanceConfigDefault = {
profile: 'high' as const,
}
const performanceConfigSchema = z.object({
profile: z.enum(['low', 'high'])
}).default(performanceConfigDefault);
/** /**
* Main card config. * Main card config.
*/ */
@@ -1221,6 +1229,7 @@ export const frigateCardConfigSchema = z.object({
elements: pictureElementsSchema, elements: pictureElementsSchema,
dimensions: dimensionsConfigSchema, dimensions: dimensionsConfigSchema,
timeline: timelineConfigSchema, timeline: timelineConfigSchema,
performance: performanceConfigSchema,
// Configuration overrides. // Configuration overrides.
overrides: overridesSchema, overrides: overridesSchema,
@@ -1246,6 +1255,7 @@ export const frigateCardConfigDefaults = {
image: imageConfigDefault, image: imageConfigDefault,
timeline: timelineConfigDefault, timeline: timelineConfigDefault,
mini_timeline: timelineCoreConfigDefault, mini_timeline: timelineCoreConfigDefault,
performance: performanceConfigDefault,
}; };
const menuButtonSchema = z.discriminatedUnion('type', [ const menuButtonSchema = z.discriminatedUnion('type', [
+1 -3
View File
@@ -104,7 +104,6 @@ export class DataManager {
protected _maxAgeSeconds: number = DATA_MANAGER_MAX_AGE_SECONDS; protected _maxAgeSeconds: number = DATA_MANAGER_MAX_AGE_SECONDS;
protected _cameras: Map<string, CameraConfig>; protected _cameras: Map<string, CameraConfig>;
protected _mediaType: TimelineMediaType;
// Garbage collect segments at most once an hour. // Garbage collect segments at most once an hour.
protected _throttledSegmentGarbageCollector = throttle( protected _throttledSegmentGarbageCollector = throttle(
@@ -115,9 +114,8 @@ export class DataManager {
{ trailing: true }, { trailing: true },
); );
constructor(cameras: Map<string, CameraConfig>, mediaType: TimelineMediaType) { constructor(cameras: Map<string, CameraConfig>) {
this._cameras = cameras; this._cameras = cameras;
this._mediaType = mediaType;
} }
// Get the last event fetch date. // Get the last event fetch date.