Automated upgrade of non-px units.
This commit is contained in:
@@ -170,7 +170,7 @@ menu:
|
||||
| Option | Default | Overridable | Description |
|
||||
| - | - | - | - |
|
||||
| `mode` | `hidden-top` | :white_check_mark: | The menu mode to show by default. See [menu modes](#menu-modes) below.|
|
||||
| `button_size` | 40 | :white_check_mark: | The size of the menu buttons in pixels.|
|
||||
| `button_size` | 40 | :white_check_mark: | The size of the menu buttons in pixels. Must be >= `20`.|
|
||||
| `buttons` | | :white_check_mark: | Whether to show or hide built-in buttons. See below. |
|
||||
|
||||
#### Menu Options: Buttons
|
||||
@@ -258,7 +258,7 @@ live:
|
||||
| Option | Default | Overridable | Description |
|
||||
| - | - | - | - |
|
||||
| `mode` | `none` | :white_check_mark: | Whether to show the thumbnail carousel `below` the media, `above` the media, in a drawer to the `left` or `right` of the media or to hide it entirely (`none`).|
|
||||
| `size` | 100 | :white_check_mark: | The size of the thumbnails in the thumbnail carousel in pixels.|
|
||||
| `size` | 100 | :white_check_mark: | The size of the thumbnails in the thumbnail carousel in pixels. Must be >= `75` and <= `175`. |
|
||||
| `show_details` | `false` | :white_check_mark: | Whether to show event details (e.g. duration, start time, object detected, etc) alongside the thumbnail.|
|
||||
| `show_controls` | `true` | :white_check_mark: | Whether to show event controls (e.g. timeline icon, favorite icon) alongside the thumbnail.|
|
||||
| `media` | `clips` | :white_check_mark: | Whether to show `clips` or `snapshots` in the thumbnail carousel in the `live` view.|
|
||||
@@ -276,7 +276,7 @@ live:
|
||||
| Option | Default | Overridable | Description |
|
||||
| - | - | - | - |
|
||||
| `style` | `chevrons` | :white_check_mark: | When viewing live cameras, what kind of controls to show to move to the previous/next camera. Acceptable values: `chevrons`, `icons`, `none` . |
|
||||
| `size` | 48 | :white_check_mark: | The size of the next/previous controls in pixels.|
|
||||
| `size` | 48 | :white_check_mark: | The size of the next/previous controls in pixels. Must be >= `20`. |
|
||||
|
||||
<a name="live-controls-title"></a>
|
||||
|
||||
@@ -328,7 +328,7 @@ event_viewer:
|
||||
| Option | Default | Overridable | Description |
|
||||
| - | - | - | - |
|
||||
| `style` | `thumbnails` | :heavy_multiplication_x: | When viewing media, what kind of controls to show to move to the previous/next media item. Acceptable values: `thumbnails`, `chevrons`, `none` . |
|
||||
| `size` | 48 | :heavy_multiplication_x: | The size of the next/previous controls in pixels.|
|
||||
| `size` | 48 | :heavy_multiplication_x: | The size of the next/previous controls in pixels. Must be >= `20`.|
|
||||
|
||||
#### Event Viewer Controls: Thumbnails
|
||||
|
||||
@@ -343,7 +343,7 @@ event_viewer:
|
||||
| Option | Default | Overridable | Description |
|
||||
| - | - | - | - |
|
||||
| `mode` | `none` | :heavy_multiplication_x: | Whether to show the thumbnail carousel `below` the media, `above` the media, in a drawer to the `left` or `right` of the media or to hide it entirely (`none`).|
|
||||
| `size` | 100 | :heavy_multiplication_x: | The size of the thumbnails in the thumbnail carousel pixels.|
|
||||
| `size` | 100 | :heavy_multiplication_x: | The size of the thumbnails in the thumbnail carousel pixels. Must be >= `75` and <= `175`.|
|
||||
| `show_details` | `false` | :heavy_multiplication_x: | Whether to show event details (e.g. duration, start time, object detected, etc) alongside the thumbnail.|
|
||||
| `show_controls` | `true` | :heavy_multiplication_x: | Whether to show event controls (e.g. timeline icon, favorite icon) alongside the thumbnail.|
|
||||
|
||||
@@ -374,7 +374,7 @@ event_gallery:
|
||||
|
||||
| Option | Default | Overridable | Description |
|
||||
| - | - | - | - |
|
||||
| `size` | 100 | :heavy_multiplication_x: | The size of the thumbnails in the event gallery in pixels.|
|
||||
| `size` | 100 | :heavy_multiplication_x: | The size of the thumbnails in the event gallery in pixels. Must be >= `75` and <= `175`.|
|
||||
| `show_details` | `false` | :heavy_multiplication_x: | Whether to show event details (e.g. duration, start time, object detected, etc) alongside the thumbnail.|
|
||||
| `show_controls` | `true` | :heavy_multiplication_x: | Whether to show event controls (e.g. timeline icon, favorite icon) alongside the thumbnail.|
|
||||
| `actions` | | :heavy_multiplication_x: | Actions to use for all views that use the `event_gallery` (e.g. `clips`, `snapshots`). See [actions](#actions) below.|
|
||||
@@ -425,7 +425,7 @@ timeline:
|
||||
| Option | Default | Overridable | Description |
|
||||
| - | - | - | - |
|
||||
| `mode` | `none` | :heavy_multiplication_x: | Whether to show the thumbnail carousel `below` the media, `above` the media, in a drawer to the `left` or `right` of the media or to hide it entirely (`none`).|
|
||||
| `size` | 100 | :heavy_multiplication_x: | The size of the thumbnails in the thumbnail carousel in pixels.|
|
||||
| `size` | 100 | :heavy_multiplication_x: | The size of the thumbnails in the thumbnail carousel in pixels. Must be >= `75` and <= `175`.|
|
||||
| `show_details` | `false` | :heavy_multiplication_x: | Whether to show event details (e.g. duration, start time, object detected, etc) alongside the thumbnail.|
|
||||
| `show_controls` | `true` | :heavy_multiplication_x: | Whether to show event controls (e.g. timeline icon, favorite icon) alongside the thumbnail.|
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// TODO: Add details & controls & size support
|
||||
// TODO: automatic upgrade for thumbnail sizes in px and menu buttons in px
|
||||
// TODO: automatic remove of min_columns
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
+98
-37
@@ -1,4 +1,4 @@
|
||||
import { get, set } from 'lodash-es';
|
||||
import { get, isEqual, set } from 'lodash-es';
|
||||
import {
|
||||
CONF_CAMERAS,
|
||||
CONF_CAMERAS_ARRAY_CAMERA_ENTITY,
|
||||
@@ -11,7 +11,10 @@ import {
|
||||
CONF_EVENT_VIEWER_AUTO_PLAY,
|
||||
CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_SIZE,
|
||||
CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE,
|
||||
CONF_EVENT_VIEWER_CONTROLS_THUMBNAILS_SIZE,
|
||||
CONF_IMAGE_URL,
|
||||
CONF_LIVE_CONTROLS_NEXT_PREVIOUS_SIZE,
|
||||
CONF_LIVE_CONTROLS_THUMBNAILS_SIZE,
|
||||
CONF_LIVE_PRELOAD,
|
||||
CONF_LIVE_WEBRTC_CARD,
|
||||
CONF_MENU,
|
||||
@@ -22,7 +25,13 @@ import {
|
||||
CONF_VIEW_TIMEOUT_SECONDS,
|
||||
CONF_VIEW_UPDATE_ENTITIES,
|
||||
} from './const';
|
||||
import { RawFrigateCardConfig, RawFrigateCardConfigArray } from './types';
|
||||
import {
|
||||
BUTTON_SIZE_MIN,
|
||||
RawFrigateCardConfig,
|
||||
RawFrigateCardConfigArray,
|
||||
THUMBNAIL_WIDTH_MAX,
|
||||
THUMBNAIL_WIDTH_MIN,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* Set a configuration value.
|
||||
@@ -131,7 +140,7 @@ export const copyConfig = function (obj: RawFrigateCardConfig): RawFrigateCardCo
|
||||
* @param value The value.
|
||||
* @returns `true` is the value is not an object.
|
||||
*/
|
||||
const isNotObject = function (value: unknown) {
|
||||
const isNotObject = function (value: unknown): unknown | undefined {
|
||||
return typeof value !== 'object' ? value : undefined;
|
||||
};
|
||||
|
||||
@@ -140,10 +149,46 @@ const isNotObject = function (value: unknown) {
|
||||
* @param value The value.
|
||||
* @returns A number or undefined.
|
||||
*/
|
||||
const toNumberOrIgnore = function (value: unknown) {
|
||||
const toNumberOrIgnore = function (value: unknown): number | undefined {
|
||||
return isNaN(value as number) ? undefined : Number(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a transform that will cap a numeric value.
|
||||
* @param value The value.
|
||||
* @returns A number or null.
|
||||
*/
|
||||
const createRangedTransform = function (
|
||||
transform: (value: unknown) => unknown,
|
||||
min?: number,
|
||||
max?: number,
|
||||
): (valueIn: unknown) => unknown {
|
||||
return (value: unknown): unknown => {
|
||||
let transformed = transform(value);
|
||||
if (typeof transformed !== 'number') {
|
||||
return transformed;
|
||||
}
|
||||
transformed = min ? Math.max(min, transformed as number) : transformed;
|
||||
transformed = max ? Math.min(max, transformed as number) : transformed;
|
||||
return transformed;
|
||||
};
|
||||
};
|
||||
|
||||
const toPixelsOrDelete = function (value: unknown): number | null | undefined {
|
||||
// Ignore the value if it's a number.
|
||||
if (typeof value === 'number') {
|
||||
return undefined;
|
||||
}
|
||||
// Delete the value if it's not a string.
|
||||
if (typeof value !== 'string') {
|
||||
return null;
|
||||
}
|
||||
// Remove 'px' and return the number, unless it's an invalid number -- then
|
||||
// delete it.
|
||||
value = value.replace(/px$/i, '');
|
||||
return isNaN(value as number) ? null : Number(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Move a property from one location to another.
|
||||
* @param obj The configuration object in which the property resides.
|
||||
@@ -158,13 +203,21 @@ export const moveConfigValue = (
|
||||
newPath: string,
|
||||
transform?: (valueIn: unknown) => unknown,
|
||||
): boolean => {
|
||||
let value = getConfigValue(obj, oldPath);
|
||||
if (transform) {
|
||||
value = transform(value);
|
||||
const inValue = getConfigValue(obj, oldPath);
|
||||
if (inValue === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (typeof value !== 'undefined') {
|
||||
const outValue = transform ? transform(inValue) : inValue;
|
||||
if (oldPath === newPath && isEqual(inValue, outValue)) {
|
||||
return false;
|
||||
}
|
||||
if (outValue === null) {
|
||||
deleteConfigValue(obj, oldPath);
|
||||
setConfigValue(obj, newPath, value);
|
||||
return true;
|
||||
}
|
||||
if (outValue !== undefined) {
|
||||
deleteConfigValue(obj, oldPath);
|
||||
setConfigValue(obj, newPath, outValue);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -197,33 +250,6 @@ const upgradeMoveTo = function (
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Upgrade a property by changing it if it is present.
|
||||
* @param path The property path.
|
||||
* @param transform A callback that transforms the old value to the new value,
|
||||
* if undefined is returned the property is removed.
|
||||
* @returns `true` if the configuration was modified.
|
||||
*/
|
||||
const upgradeChangeIfPresent = function (
|
||||
path: string,
|
||||
transform: (valueIn: unknown) => unknown,
|
||||
): (obj: RawFrigateCardConfig) => boolean {
|
||||
return function (obj: RawFrigateCardConfig): boolean {
|
||||
const oldValue = getConfigValue(obj, path);
|
||||
if (oldValue !== undefined) {
|
||||
const newValue = transform(oldValue);
|
||||
if (newValue === undefined) {
|
||||
deleteConfigValue(obj, path);
|
||||
return true;
|
||||
} else if (newValue !== oldValue) {
|
||||
setConfigValue(obj, path, newValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Upgrade by moving a property from one location to another, and moving a
|
||||
* property specified in a top-level overrides object.
|
||||
@@ -249,6 +275,19 @@ const upgradeMoveToWithOverrides = function (
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Upgrade a property in place with overrides.
|
||||
* @param path The old property path.
|
||||
* @param transform An optional transform for the value.
|
||||
* @returns A function that returns `true` if the configuration was modified.
|
||||
*/
|
||||
const upgradeWithOverrides = function (
|
||||
path: string,
|
||||
transform?: (valueIn: unknown) => unknown,
|
||||
): (obj: RawFrigateCardConfig) => boolean {
|
||||
return upgradeMoveToWithOverrides(path, path, transform);
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a path to an array, apply an upgrade to each object in the array.
|
||||
* @param arrayPath The path to the array to upgrade.
|
||||
@@ -377,11 +416,33 @@ const UPGRADES = [
|
||||
// v3.0.0-rc.1 -> v3.0.0-rc.2
|
||||
upgradeArrayValue(
|
||||
CONF_CAMERAS,
|
||||
upgradeChangeIfPresent('live_provider', (val) =>
|
||||
upgradeWithOverrides('live_provider', (val) =>
|
||||
val === 'frigate' ? 'ha' : val === 'webrtc' ? 'webrtc-card' : val,
|
||||
),
|
||||
),
|
||||
upgradeArrayValue(CONF_CAMERAS, upgradeMoveTo('webrtc', 'webrtc_card')),
|
||||
upgradeMoveToWithOverrides('live.webrtc', CONF_LIVE_WEBRTC_CARD),
|
||||
upgradeMoveToWithOverrides('image.src', CONF_IMAGE_URL),
|
||||
|
||||
// v3.0.0 -> v4.0.0-rc.1
|
||||
upgradeWithOverrides(
|
||||
CONF_LIVE_CONTROLS_THUMBNAILS_SIZE,
|
||||
createRangedTransform(toPixelsOrDelete, THUMBNAIL_WIDTH_MIN, THUMBNAIL_WIDTH_MAX),
|
||||
),
|
||||
upgradeWithOverrides(
|
||||
CONF_EVENT_VIEWER_CONTROLS_THUMBNAILS_SIZE,
|
||||
createRangedTransform(toPixelsOrDelete, THUMBNAIL_WIDTH_MIN, THUMBNAIL_WIDTH_MAX),
|
||||
),
|
||||
upgradeWithOverrides(
|
||||
CONF_LIVE_CONTROLS_NEXT_PREVIOUS_SIZE,
|
||||
createRangedTransform(toPixelsOrDelete, BUTTON_SIZE_MIN),
|
||||
),
|
||||
upgradeWithOverrides(
|
||||
CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_SIZE,
|
||||
createRangedTransform(toPixelsOrDelete, BUTTON_SIZE_MIN),
|
||||
),
|
||||
upgradeWithOverrides(
|
||||
CONF_MENU_BUTTON_SIZE,
|
||||
createRangedTransform(toPixelsOrDelete, BUTTON_SIZE_MIN),
|
||||
),
|
||||
];
|
||||
|
||||
+10
-3
@@ -5,6 +5,7 @@ import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { HomeAssistant, LovelaceCardEditor, fireEvent } from 'custom-card-helpers';
|
||||
import { localize } from './localize/localize.js';
|
||||
import {
|
||||
BUTTON_SIZE_MIN,
|
||||
RawFrigateCardConfig,
|
||||
RawFrigateCardConfigArray,
|
||||
THUMBNAIL_WIDTH_MAX,
|
||||
@@ -847,7 +848,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
? html`
|
||||
<div class="values">
|
||||
${this._renderOptionSelector(CONF_MENU_MODE, this._menuModes)}
|
||||
${this._renderNumberInput(CONF_MENU_BUTTON_SIZE)}
|
||||
${this._renderNumberInput(CONF_MENU_BUTTON_SIZE, BUTTON_SIZE_MIN)}
|
||||
${this._renderSwitch(
|
||||
CONF_MENU_BUTTONS_FRIGATE,
|
||||
defaults.menu.buttons.frigate,
|
||||
@@ -904,7 +905,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
CONF_LIVE_CONTROLS_NEXT_PREVIOUS_STYLE,
|
||||
this._liveNextPreviousControlStyles,
|
||||
)}
|
||||
${this._renderNumberInput(CONF_LIVE_CONTROLS_NEXT_PREVIOUS_SIZE)}
|
||||
${this._renderNumberInput(
|
||||
CONF_LIVE_CONTROLS_NEXT_PREVIOUS_SIZE,
|
||||
BUTTON_SIZE_MIN,
|
||||
)}
|
||||
${this._renderOptionSelector(
|
||||
CONF_LIVE_CONTROLS_THUMBNAILS_MODE,
|
||||
this._thumbnailModes,
|
||||
@@ -983,7 +987,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE,
|
||||
this._eventViewerNextPreviousControlStyles,
|
||||
)}
|
||||
${this._renderNumberInput(CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_SIZE)}
|
||||
${this._renderNumberInput(
|
||||
CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_SIZE,
|
||||
BUTTON_SIZE_MIN,
|
||||
)}
|
||||
${this._renderOptionSelector(
|
||||
CONF_EVENT_VIEWER_CONTROLS_THUMBNAILS_MODE,
|
||||
this._thumbnailModes,
|
||||
|
||||
+6
-3
@@ -14,7 +14,10 @@ import { z } from 'zod';
|
||||
|
||||
import { deepRemoveDefaults } from './zod-util';
|
||||
|
||||
// The maximum width thumbnail Frigate returns
|
||||
// The min allowed size of buttons.
|
||||
export const BUTTON_SIZE_MIN = 20;
|
||||
|
||||
// The min/max width thumbnail (Frigate returns a maximum of 175px).
|
||||
export const THUMBNAIL_WIDTH_MAX = 175;
|
||||
export const THUMBNAIL_WIDTH_MIN = 75;
|
||||
|
||||
@@ -455,7 +458,7 @@ export type ThumbnailsControlConfig = z.infer<typeof thumbnailsControlSchema>;
|
||||
|
||||
const nextPreviousControlConfigSchema = z.object({
|
||||
style: z.enum(['none', 'chevrons', 'icons', 'thumbnails']),
|
||||
size: z.number().min(1),
|
||||
size: z.number().min(BUTTON_SIZE_MIN),
|
||||
});
|
||||
export type NextPreviousControlConfig = z.infer<typeof nextPreviousControlConfigSchema>;
|
||||
|
||||
@@ -638,7 +641,7 @@ const menuConfigSchema = z
|
||||
fullscreen: z.boolean().default(menuConfigDefault.buttons.fullscreen),
|
||||
})
|
||||
.default(menuConfigDefault.buttons),
|
||||
button_size: z.number().min(1).default(menuConfigDefault.button_size),
|
||||
button_size: z.number().min(BUTTON_SIZE_MIN).default(menuConfigDefault.button_size),
|
||||
})
|
||||
.default(menuConfigDefault);
|
||||
export type MenuConfig = z.infer<typeof menuConfigSchema>;
|
||||
|
||||
Reference in New Issue
Block a user