Rename service_data to data to maintain HA compatibility.
This commit is contained in:
+1
-1
@@ -16,6 +16,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cycjimmy/jsmpeg-player": "^6.0.4",
|
"@cycjimmy/jsmpeg-player": "^6.0.4",
|
||||||
|
"@dermotduffy/custom-card-helpers": "^1.9.0",
|
||||||
"@dermotduffy/panzoom": "^4.5.1",
|
"@dermotduffy/panzoom": "^4.5.1",
|
||||||
"@egjs/hammerjs": "^2.0.17",
|
"@egjs/hammerjs": "^2.0.17",
|
||||||
"@graphiteds/core": "^1.9.11",
|
"@graphiteds/core": "^1.9.11",
|
||||||
@@ -24,7 +25,6 @@
|
|||||||
"@types/bluebird": "^3.5.36",
|
"@types/bluebird": "^3.5.36",
|
||||||
"component-emitter": "^1.3.0",
|
"component-emitter": "^1.3.0",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"custom-card-helpers": "^1.9.0",
|
|
||||||
"date-fns": "^2.29.2",
|
"date-fns": "^2.29.2",
|
||||||
"date-fns-tz": "^1.3.7",
|
"date-fns-tz": "^1.3.7",
|
||||||
"embla-carousel": "8.0.0-rc14",
|
"embla-carousel": "8.0.0-rc14",
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { fireEvent } from 'custom-card-helpers';
|
import { fireEvent } from '@dermotduffy/custom-card-helpers';
|
||||||
import type {
|
import type {
|
||||||
ActionHandlerDetail,
|
ActionHandlerDetail,
|
||||||
ActionHandlerOptions,
|
ActionHandlerOptions,
|
||||||
} from 'custom-card-helpers/dist/types.d.js';
|
} from '@dermotduffy/custom-card-helpers';
|
||||||
import { noChange } from 'lit';
|
import { noChange } from 'lit';
|
||||||
import {
|
import {
|
||||||
AttributePart,
|
AttributePart,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { CameraConfig } from '../../config/types';
|
import { CameraConfig } from '../../config/types';
|
||||||
import { localize } from '../../localize/localize';
|
import { localize } from '../../localize/localize';
|
||||||
import { ExtendedHomeAssistant } from '../../types';
|
import { ExtendedHomeAssistant } from '../../types';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { CameraConfig } from '../config/types';
|
import { CameraConfig } from '../config/types';
|
||||||
import { localize } from '../localize/localize';
|
import { localize } from '../localize/localize';
|
||||||
import { BrowseMediaManager } from '../utils/ha/browse-media/browse-media-manager';
|
import { BrowseMediaManager } from '../utils/ha/browse-media/browse-media-manager';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { CameraConfig } from '../config/types';
|
import { CameraConfig } from '../config/types';
|
||||||
import { ExtendedHomeAssistant } from '../types';
|
import { ExtendedHomeAssistant } from '../types';
|
||||||
import { EntityRegistryManager } from '../utils/ha/entity-registry';
|
import { EntityRegistryManager } from '../utils/ha/entity-registry';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import add from 'date-fns/add';
|
import add from 'date-fns/add';
|
||||||
import endOfHour from 'date-fns/endOfHour';
|
import endOfHour from 'date-fns/endOfHour';
|
||||||
import format from 'date-fns/format';
|
import format from 'date-fns/format';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { localize } from '../../localize/localize';
|
import { localize } from '../../localize/localize';
|
||||||
import { FrigateCardError } from '../../types';
|
import { FrigateCardError } from '../../types';
|
||||||
import { homeAssistantWSRequest } from '../../utils/ha';
|
import { homeAssistantWSRequest } from '../../utils/ha';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
|
||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { CameraConfig } from '../../config/types';
|
import { CameraConfig } from '../../config/types';
|
||||||
import { ExtendedHomeAssistant } from '../../types';
|
import { ExtendedHomeAssistant } from '../../types';
|
||||||
import { getEntityIcon, getEntityTitle } from '../../utils/ha';
|
import { getEntityIcon, getEntityTitle } from '../../utils/ha';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import add from 'date-fns/add';
|
import add from 'date-fns/add';
|
||||||
import cloneDeep from 'lodash-es/cloneDeep';
|
import cloneDeep from 'lodash-es/cloneDeep';
|
||||||
import merge from 'lodash-es/merge.js';
|
import merge from 'lodash-es/merge.js';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import add from 'date-fns/add';
|
import add from 'date-fns/add';
|
||||||
import endOfDay from 'date-fns/endOfDay';
|
import endOfDay from 'date-fns/endOfDay';
|
||||||
import parse from 'date-fns/parse';
|
import parse from 'date-fns/parse';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { LovelaceCardEditor } from 'custom-card-helpers';
|
import { LovelaceCardEditor } from '@dermotduffy/custom-card-helpers';
|
||||||
import { ReactiveController } from 'lit';
|
import { ReactiveController } from 'lit';
|
||||||
import { CameraManager } from '../camera-manager/manager';
|
import { CameraManager } from '../camera-manager/manager';
|
||||||
import { FrigateCardConfig } from '../config/types';
|
import { FrigateCardConfig } from '../config/types';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import orderBy from 'lodash-es/orderBy';
|
import orderBy from 'lodash-es/orderBy';
|
||||||
import { getHassDifferences, isTriggeredState } from '../utils/ha';
|
import { getHassDifferences, isTriggeredState } from '../utils/ha';
|
||||||
import { Timer } from '../utils/timer';
|
import { Timer } from '../utils/timer';
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant, LovelaceCardEditor } from 'custom-card-helpers';
|
import { HomeAssistant, LovelaceCardEditor } from '@dermotduffy/custom-card-helpers';
|
||||||
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
||||||
import { customElement } from 'lit/decorators.js';
|
import { customElement } from 'lit/decorators.js';
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { StyleInfo } from 'lit/directives/style-map';
|
import { StyleInfo } from 'lit/directives/style-map';
|
||||||
import { CameraManager } from '../camera-manager/manager';
|
import { CameraManager } from '../camera-manager/manager';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { CSSResultGroup, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
import { CSSResultGroup, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
||||||
import { customElement, property, state } from 'lit/decorators.js';
|
import { customElement, property, state } from 'lit/decorators.js';
|
||||||
import { RawFrigateCardConfig } from '../config/types';
|
import { RawFrigateCardConfig } from '../config/types';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HASSDomEvent, HomeAssistant } from 'custom-card-helpers';
|
import { HASSDomEvent, HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import {
|
import {
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
LitElement,
|
LitElement,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { HassEntity } from 'home-assistant-js-websocket';
|
import { HassEntity } from 'home-assistant-js-websocket';
|
||||||
import {
|
import {
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
||||||
import { customElement, property } from 'lit/decorators.js';
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
import { createRef, Ref, ref } from 'lit/directives/ref.js';
|
import { createRef, Ref, ref } from 'lit/directives/ref.js';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
||||||
import { customElement, property } from 'lit/decorators.js';
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
import { createRef, ref, Ref } from 'lit/directives/ref.js';
|
import { createRef, ref, Ref } from 'lit/directives/ref.js';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Task } from '@lit-labs/task';
|
import { Task } from '@lit-labs/task';
|
||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
||||||
import { customElement, property } from 'lit/decorators.js';
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
import { CameraEndpoints } from '../../camera-manager/types.js';
|
import { CameraEndpoints } from '../../camera-manager/types.js';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ScopedRegistryHost } from '@lit-labs/scoped-registry-mixin';
|
import { ScopedRegistryHost } from '@lit-labs/scoped-registry-mixin';
|
||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import endOfDay from 'date-fns/endOfDay';
|
import endOfDay from 'date-fns/endOfDay';
|
||||||
import endOfMonth from 'date-fns/endOfMonth';
|
import endOfMonth from 'date-fns/endOfMonth';
|
||||||
import endOfYesterday from 'date-fns/endOfYesterday';
|
import endOfYesterday from 'date-fns/endOfYesterday';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HASSDomEvent, HomeAssistant } from 'custom-card-helpers';
|
import { HASSDomEvent, HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import {
|
import {
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
LitElement,
|
LitElement,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
||||||
import { customElement, property, state } from 'lit/decorators.js';
|
import { customElement, property, state } from 'lit/decorators.js';
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import {
|
import {
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
html,
|
html,
|
||||||
@@ -230,7 +230,7 @@ export class FrigateCardSubmenuSelect extends LitElement {
|
|||||||
service: entityID.startsWith('select.')
|
service: entityID.startsWith('select.')
|
||||||
? 'select.select_option'
|
? 'select.select_option'
|
||||||
: 'input_select.select_option',
|
: 'input_select.select_option',
|
||||||
service_data: {
|
data: {
|
||||||
entity_id: entityID,
|
entity_id: entityID,
|
||||||
option: option,
|
option: option,
|
||||||
},
|
},
|
||||||
|
|||||||
+123
-316
@@ -2,40 +2,24 @@ import cloneDeep from 'lodash-es/cloneDeep';
|
|||||||
import get from 'lodash-es/get';
|
import get from 'lodash-es/get';
|
||||||
import isEqual from 'lodash-es/isEqual';
|
import isEqual from 'lodash-es/isEqual';
|
||||||
import set from 'lodash-es/set';
|
import set from 'lodash-es/set';
|
||||||
import {
|
import unset from 'lodash-es/unset';
|
||||||
BUTTON_SIZE_MIN,
|
import { RawFrigateCardConfig } from './config/types';
|
||||||
RawFrigateCardConfig,
|
|
||||||
THUMBNAIL_WIDTH_MAX,
|
|
||||||
THUMBNAIL_WIDTH_MIN,
|
|
||||||
} from './config/types';
|
|
||||||
import {
|
import {
|
||||||
CONF_CAMERAS,
|
CONF_CAMERAS,
|
||||||
CONF_CAMERAS_GLOBAL_IMAGE,
|
CONF_CAMERAS_GLOBAL_IMAGE,
|
||||||
CONF_CAMERAS_GLOBAL_JSMPEG,
|
CONF_CAMERAS_GLOBAL_JSMPEG,
|
||||||
CONF_CAMERAS_GLOBAL_WEBRTC_CARD,
|
CONF_CAMERAS_GLOBAL_WEBRTC_CARD,
|
||||||
CONF_ELEMENTS,
|
CONF_ELEMENTS,
|
||||||
CONF_LIVE_AUTO_UNMUTE,
|
|
||||||
CONF_LIVE_CONTROLS_NEXT_PREVIOUS_SIZE,
|
|
||||||
CONF_LIVE_CONTROLS_THUMBNAILS_SIZE,
|
|
||||||
CONF_LIVE_LAZY_UNLOAD,
|
|
||||||
CONF_MEDIA_GALLERY,
|
CONF_MEDIA_GALLERY,
|
||||||
CONF_MEDIA_VIEWER,
|
|
||||||
CONF_MENU_BUTTONS_CAMERAS,
|
|
||||||
CONF_MENU_BUTTONS_CAMERA_UI,
|
CONF_MENU_BUTTONS_CAMERA_UI,
|
||||||
CONF_MENU_BUTTONS_CLIPS,
|
|
||||||
CONF_MENU_BUTTONS_DOWNLOAD,
|
|
||||||
CONF_MENU_BUTTONS_FRIGATE,
|
|
||||||
CONF_MENU_BUTTONS_FULLSCREEN,
|
|
||||||
CONF_MENU_BUTTONS_IMAGE,
|
|
||||||
CONF_MENU_BUTTONS_LIVE,
|
|
||||||
CONF_MENU_BUTTONS_SNAPSHOTS,
|
|
||||||
CONF_MENU_BUTTON_SIZE,
|
|
||||||
CONF_MENU_POSITION,
|
|
||||||
CONF_MENU_STYLE,
|
|
||||||
CONF_OVERRIDES,
|
CONF_OVERRIDES,
|
||||||
} from './const';
|
} from './const';
|
||||||
import { arrayify } from './utils/basic';
|
import { arrayify } from './utils/basic';
|
||||||
|
|
||||||
|
// *************************************************************************
|
||||||
|
// General Config Management Functions
|
||||||
|
// *************************************************************************
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a configuration value.
|
* Set a configuration value.
|
||||||
* @param obj The configuration.
|
* @param obj The configuration.
|
||||||
@@ -71,19 +55,33 @@ export const getConfigValue = (
|
|||||||
* @param obj The configuration.
|
* @param obj The configuration.
|
||||||
* @param key The key to the property to delete.
|
* @param key The key to the property to delete.
|
||||||
*/
|
*/
|
||||||
export const deleteConfigValue = (obj: RawFrigateCardConfig, key: string): void => {
|
export const deleteConfigValue = (obj: RawFrigateCardConfig, path: string): void => {
|
||||||
let id = key;
|
unset(obj, path);
|
||||||
let targetObj: unknown = obj;
|
|
||||||
if (key && key.split && key.includes('.')) {
|
|
||||||
const keys = key.split('.');
|
|
||||||
id = keys[keys.length - 1];
|
|
||||||
targetObj = getConfigValue(obj, keys.slice(0, -1).join('.'));
|
|
||||||
}
|
|
||||||
if (targetObj && typeof targetObj === 'object') {
|
|
||||||
delete targetObj[id];
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy a configuration.
|
||||||
|
* @param obj Configuration to copy.
|
||||||
|
* @returns A new deeply-copied configuration.
|
||||||
|
*/
|
||||||
|
export const copyConfig = <T>(obj: T): T => {
|
||||||
|
return cloneDeep(obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an array path, return a true path.
|
||||||
|
* @param path The array path (should have a '#').
|
||||||
|
* @param index The numeric array index to use.
|
||||||
|
* @returns The true config path.
|
||||||
|
*/
|
||||||
|
export const getArrayConfigPath = (path: string, index: number): string => {
|
||||||
|
return path.replace('#', `[${index.toString()}]`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// *************************************************************************
|
||||||
|
// Upgrade Related Functions
|
||||||
|
// *************************************************************************
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upgrade a configuration.
|
* Upgrade a configuration.
|
||||||
* @param obj The configuration to upgrade.
|
* @param obj The configuration to upgrade.
|
||||||
@@ -106,67 +104,6 @@ export const isConfigUpgradeable = function (obj: RawFrigateCardConfig): boolean
|
|||||||
return upgradeConfig(copyConfig(obj));
|
return upgradeConfig(copyConfig(obj));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy a configuration.
|
|
||||||
* @param obj Configuration to copy.
|
|
||||||
* @returns A new deeply-copied configuration.
|
|
||||||
*/
|
|
||||||
export const copyConfig = <T>(obj: T): T => {
|
|
||||||
return cloneDeep(obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a value from 'XXpx' to XX (as a number).
|
|
||||||
* @param value Incoming value.
|
|
||||||
* @returns A number, null if the property should be deleted or undefined if it
|
|
||||||
* should be ignored.
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request a property be deleted.
|
|
||||||
* @param _value Inbound value (not required).
|
|
||||||
* @returns `null` to request the property be deleted.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const deleteProperty = function (_value: unknown): number | null | undefined {
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move a property from one location to another.
|
* Move a property from one location to another.
|
||||||
* @param obj The configuration object in which the property resides.
|
* @param obj The configuration object in which the property resides.
|
||||||
@@ -175,7 +112,7 @@ const deleteProperty = function (_value: unknown): number | null | undefined {
|
|||||||
* @param transform An optional transform for the value.
|
* @param transform An optional transform for the value.
|
||||||
* @returns `true` if the configuration was modified.
|
* @returns `true` if the configuration was modified.
|
||||||
*/
|
*/
|
||||||
const moveConfigValue = (
|
export const moveConfigValue = (
|
||||||
obj: RawFrigateCardConfig,
|
obj: RawFrigateCardConfig,
|
||||||
oldPath: string,
|
oldPath: string,
|
||||||
newPath: string,
|
newPath: string,
|
||||||
@@ -209,16 +146,6 @@ const moveConfigValue = (
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Given an array path, return a true path.
|
|
||||||
* @param path The array path (should have a '#').
|
|
||||||
* @param index The numeric array index to use.
|
|
||||||
* @returns The true config path.
|
|
||||||
*/
|
|
||||||
export const getArrayConfigPath = (path: string, index: number): string => {
|
|
||||||
return path.replace('#', `[${index.toString()}]`);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upgrade by moving a property from one location to another.
|
* Upgrade by moving a property from one location to another.
|
||||||
* @param oldPath The old property path.
|
* @param oldPath The old property path.
|
||||||
@@ -226,7 +153,7 @@ export const getArrayConfigPath = (path: string, index: number): string => {
|
|||||||
* @param transform An optional transform for the value.
|
* @param transform An optional transform for the value.
|
||||||
* @returns `true` if the configuration was modified.
|
* @returns `true` if the configuration was modified.
|
||||||
*/
|
*/
|
||||||
const upgradeMoveTo = function (
|
export const upgradeMoveTo = function (
|
||||||
oldPath: string,
|
oldPath: string,
|
||||||
newPath: string,
|
newPath: string,
|
||||||
options?: {
|
options?: {
|
||||||
@@ -247,7 +174,7 @@ const upgradeMoveTo = function (
|
|||||||
* @param transform An optional transform for the value.
|
* @param transform An optional transform for the value.
|
||||||
* @returns A function that returns `true` if the configuration was modified.
|
* @returns A function that returns `true` if the configuration was modified.
|
||||||
*/
|
*/
|
||||||
const upgradeMoveToWithOverrides = function (
|
export const upgradeMoveToWithOverrides = function (
|
||||||
oldPath: string,
|
oldPath: string,
|
||||||
newPath: string,
|
newPath: string,
|
||||||
options?: {
|
options?: {
|
||||||
@@ -258,7 +185,7 @@ const upgradeMoveToWithOverrides = function (
|
|||||||
return function (obj: RawFrigateCardConfig): boolean {
|
return function (obj: RawFrigateCardConfig): boolean {
|
||||||
let modified = upgradeMoveTo(oldPath, newPath, options)(obj);
|
let modified = upgradeMoveTo(oldPath, newPath, options)(obj);
|
||||||
modified =
|
modified =
|
||||||
upgradeArrayValue(
|
upgradeArrayOfObjects(
|
||||||
CONF_OVERRIDES,
|
CONF_OVERRIDES,
|
||||||
upgradeMoveTo(oldPath, newPath, options),
|
upgradeMoveTo(oldPath, newPath, options),
|
||||||
(obj) => obj.overrides as RawFrigateCardConfig | undefined,
|
(obj) => obj.overrides as RawFrigateCardConfig | undefined,
|
||||||
@@ -273,26 +200,13 @@ const upgradeMoveToWithOverrides = function (
|
|||||||
* @param transform An optional transform for the value.
|
* @param transform An optional transform for the value.
|
||||||
* @returns A function that returns `true` if the configuration was modified.
|
* @returns A function that returns `true` if the configuration was modified.
|
||||||
*/
|
*/
|
||||||
const upgradeWithOverrides = function (
|
export const upgradeWithOverrides = function (
|
||||||
path: string,
|
path: string,
|
||||||
transform: (valueIn: unknown) => unknown,
|
transform: (valueIn: unknown) => unknown,
|
||||||
): (obj: RawFrigateCardConfig) => boolean {
|
): (obj: RawFrigateCardConfig) => boolean {
|
||||||
return upgradeMoveToWithOverrides(path, path, { transform: transform });
|
return upgradeMoveToWithOverrides(path, path, { transform: transform });
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Upgrade a property in place without 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 upgrade = function (
|
|
||||||
path: string,
|
|
||||||
transform: (valueIn: unknown) => unknown,
|
|
||||||
): (obj: RawFrigateCardConfig) => boolean {
|
|
||||||
return upgradeMoveTo(path, path, { transform: transform });
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a path to an array, apply an upgrade to each object in the array.
|
* Given a path to an array, apply an upgrade to each object in the array.
|
||||||
* @param arrayPath The path to the array to upgrade.
|
* @param arrayPath The path to the array to upgrade.
|
||||||
@@ -301,7 +215,7 @@ const upgrade = function (
|
|||||||
* returns the object to modify within it.
|
* returns the object to modify within it.
|
||||||
* @returns A function that returns `true` if the configuration was modified.
|
* @returns A function that returns `true` if the configuration was modified.
|
||||||
*/
|
*/
|
||||||
const upgradeArrayValue = function (
|
export const upgradeArrayOfObjects = function (
|
||||||
arrayPath: string,
|
arrayPath: string,
|
||||||
upgrade: (obj: RawFrigateCardConfig) => boolean,
|
upgrade: (obj: RawFrigateCardConfig) => boolean,
|
||||||
getObject?: (obj: RawFrigateCardConfig) => RawFrigateCardConfig | undefined,
|
getObject?: (obj: RawFrigateCardConfig) => RawFrigateCardConfig | undefined,
|
||||||
@@ -321,127 +235,13 @@ const upgradeArrayValue = function (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Upgrade from a menu-mode to a style & position.
|
|
||||||
* @returns An upgrade function.
|
|
||||||
*/
|
|
||||||
const upgradeMenuModeToStyleAndPosition = (): ((
|
|
||||||
obj: RawFrigateCardConfig,
|
|
||||||
) => boolean) => {
|
|
||||||
return function (obj: RawFrigateCardConfig): boolean {
|
|
||||||
let modified = false;
|
|
||||||
|
|
||||||
// Change the 'start' of the mode into a style.
|
|
||||||
modified =
|
|
||||||
upgradeMoveToWithOverrides('menu.mode', CONF_MENU_STYLE, {
|
|
||||||
transform: (mode: unknown): string | undefined => {
|
|
||||||
if (typeof mode === 'string') {
|
|
||||||
const result = mode.match(/^(hover|hidden|overlay|above|below|none)/);
|
|
||||||
if (result) {
|
|
||||||
switch (result[1]) {
|
|
||||||
case 'hover':
|
|
||||||
case 'hidden':
|
|
||||||
case 'overlay':
|
|
||||||
case 'none':
|
|
||||||
return result[1];
|
|
||||||
case 'above':
|
|
||||||
case 'below':
|
|
||||||
return 'outside';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
},
|
|
||||||
keepOriginal: true,
|
|
||||||
})(obj) || modified;
|
|
||||||
|
|
||||||
// Change the 'end' of the mode into a position.
|
|
||||||
modified =
|
|
||||||
upgradeMoveToWithOverrides('menu.mode', CONF_MENU_POSITION, {
|
|
||||||
transform: (mode: unknown): string | undefined => {
|
|
||||||
if (typeof mode === 'string') {
|
|
||||||
const result = mode.match(/(above|below|left|right|top|bottom)$/);
|
|
||||||
if (result) {
|
|
||||||
switch (result[1]) {
|
|
||||||
case 'left':
|
|
||||||
case 'right':
|
|
||||||
case 'top':
|
|
||||||
case 'bottom':
|
|
||||||
return result[1];
|
|
||||||
case 'above':
|
|
||||||
return 'top';
|
|
||||||
case 'below':
|
|
||||||
return 'bottom';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
},
|
|
||||||
keepOriginal: true,
|
|
||||||
})(obj) || modified;
|
|
||||||
|
|
||||||
// Delete the old `menu.mode` .
|
|
||||||
return upgradeWithOverrides('menu.mode', deleteProperty)(obj) || modified;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform a menu button from a boolean to a priority.
|
|
||||||
* @param value The boolean true/false for show/hide the switch.
|
|
||||||
* @returns A priority value.
|
|
||||||
*/
|
|
||||||
const menuButtonBooleanToObject = function (
|
|
||||||
value: unknown,
|
|
||||||
): { enabled: boolean } | null | undefined {
|
|
||||||
if (typeof value === 'object') {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
// If it's not a boolean remove it.
|
|
||||||
if (typeof value !== 'boolean') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return { enabled: value };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upgrade from a show_controls key to individual favorite/timeline keys.
|
|
||||||
* @returns An upgrade function.
|
|
||||||
*/
|
|
||||||
const upgradeThumbnailShowControlsToIndividualControls = (
|
|
||||||
thumbnailsBasePath: string,
|
|
||||||
): ((obj: RawFrigateCardConfig) => boolean) => {
|
|
||||||
const thumbnailsShowControlsPath = `${thumbnailsBasePath}.show_controls`;
|
|
||||||
|
|
||||||
return function (obj: RawFrigateCardConfig): boolean {
|
|
||||||
let modified = false;
|
|
||||||
modified =
|
|
||||||
upgradeMoveToWithOverrides(
|
|
||||||
thumbnailsShowControlsPath,
|
|
||||||
`${thumbnailsBasePath}.show_favorite_control`,
|
|
||||||
{ keepOriginal: true },
|
|
||||||
)(obj) || modified;
|
|
||||||
|
|
||||||
modified =
|
|
||||||
upgradeMoveToWithOverrides(
|
|
||||||
thumbnailsShowControlsPath,
|
|
||||||
`${thumbnailsBasePath}.show_timeline_control`,
|
|
||||||
{ keepOriginal: true },
|
|
||||||
)(obj) || modified;
|
|
||||||
|
|
||||||
// Delete the old `show_controls`.
|
|
||||||
return (
|
|
||||||
upgradeWithOverrides(thumbnailsShowControlsPath, deleteProperty)(obj) || modified
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively upgrade an object.
|
* Recursively upgrade an object.
|
||||||
* @param transform A transform applied to each object recursively.
|
* @param transform A transform applied to each object recursively.
|
||||||
* @param getObject A function to get the object to be upgraded.
|
* @param getObject A function to get the object to be upgraded.
|
||||||
* @returns An upgrade function.
|
* @returns An upgrade function.
|
||||||
*/
|
*/
|
||||||
const recursiveUpgradeObject = (
|
export const upgradeObjectRecursively = (
|
||||||
transform: (data: RawFrigateCardConfig) => boolean,
|
transform: (data: RawFrigateCardConfig) => boolean,
|
||||||
getObject?: (data: RawFrigateCardConfig) => RawFrigateCardConfig | undefined | null,
|
getObject?: (data: RawFrigateCardConfig) => RawFrigateCardConfig | undefined | null,
|
||||||
): ((data: RawFrigateCardConfig) => boolean) => {
|
): ((data: RawFrigateCardConfig) => boolean) => {
|
||||||
@@ -453,17 +253,13 @@ const recursiveUpgradeObject = (
|
|||||||
result = transform(object) || result;
|
result = transform(object) || result;
|
||||||
}
|
}
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
data
|
data.forEach((item: RawFrigateCardConfig) => {
|
||||||
.filter((item) => typeof item === 'object')
|
result = recurse(item) || result;
|
||||||
.forEach((item: RawFrigateCardConfig) => {
|
});
|
||||||
result = recurse(item) || result;
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
Object.keys(data)
|
Object.keys(data).forEach((key) => {
|
||||||
.filter((key) => typeof data[key] === 'object')
|
result = recurse(data[key] as RawFrigateCardConfig) || result;
|
||||||
.forEach((key) => {
|
});
|
||||||
result = recurse(data[key] as RawFrigateCardConfig) || result;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -471,12 +267,51 @@ const recursiveUpgradeObject = (
|
|||||||
return recurse;
|
return recurse;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// *************************************************************************
|
||||||
|
// Upgrade Related Functions: Generic Transforms
|
||||||
|
// *************************************************************************
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a transform that will cap a numeric value.
|
||||||
|
* @param value The value.
|
||||||
|
* @returns A number or null.
|
||||||
|
*/
|
||||||
|
export 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 !== undefined ? Math.max(min, transformed as number) : transformed;
|
||||||
|
transformed = max !== undefined ? Math.min(max, transformed as number) : transformed;
|
||||||
|
return transformed;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a property be deleted.
|
||||||
|
* @param _value Inbound value (not required).
|
||||||
|
* @returns `null` to request the property be deleted.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
export const deleteTransform = function (_value: unknown): number | null | undefined {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// *************************************************************************
|
||||||
|
// Upgrade Related Functions: Specific Transforms
|
||||||
|
// *************************************************************************
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform mediaLoaded -> media_loaded
|
* Transform mediaLoaded -> media_loaded
|
||||||
* @param data Input data.
|
* @param data Input data.
|
||||||
* @returns `true` if the configuration was modified.
|
* @returns `true` if the configuration was modified.
|
||||||
*/
|
*/
|
||||||
const transformConditionMediaLoaded = (data: unknown): boolean => {
|
const conditionMediaLoadedTransform = (data: unknown): boolean => {
|
||||||
if (typeof data === 'object' && data && data['mediaLoaded'] !== undefined) {
|
if (typeof data === 'object' && data && data['mediaLoaded'] !== undefined) {
|
||||||
data['media_loaded'] = data['mediaLoaded'];
|
data['media_loaded'] = data['mediaLoaded'];
|
||||||
delete data['mediaLoaded'];
|
delete data['mediaLoaded'];
|
||||||
@@ -485,12 +320,34 @@ const transformConditionMediaLoaded = (data: unknown): boolean => {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform service_data -> data
|
||||||
|
* See: https://github.com/dermotduffy/frigate-hass-card/issues/1103
|
||||||
|
* @param data Input data.
|
||||||
|
* @returns `true` if the configuration was modified.
|
||||||
|
*/
|
||||||
|
const serviceDataToDataTransform = (data: unknown): boolean => {
|
||||||
|
if (
|
||||||
|
typeof data === 'object' &&
|
||||||
|
data &&
|
||||||
|
data['action'] === 'call-service' &&
|
||||||
|
data['service'] !== undefined &&
|
||||||
|
data['service_data'] !== undefined &&
|
||||||
|
data['data'] === undefined
|
||||||
|
) {
|
||||||
|
data['data'] = data['service_data'];
|
||||||
|
delete data['service_data'];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform action frigate_ui -> camera_ui
|
* Transform action frigate_ui -> camera_ui
|
||||||
* @param data Input data.
|
* @param data Input data.
|
||||||
* @returns `true` if the configuration was modified.
|
* @returns `true` if the configuration was modified.
|
||||||
*/
|
*/
|
||||||
const transformFrigateUIAction = (data: unknown): boolean => {
|
const frigateUIActionTransform = (data: unknown): boolean => {
|
||||||
if (
|
if (
|
||||||
typeof data === 'object' &&
|
typeof data === 'object' &&
|
||||||
data &&
|
data &&
|
||||||
@@ -504,101 +361,51 @@ const transformFrigateUIAction = (data: unknown): boolean => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const UPGRADES = [
|
const UPGRADES = [
|
||||||
// v3.0.0 -> v4.0.0-rc.1
|
|
||||||
upgradeWithOverrides(
|
|
||||||
CONF_LIVE_CONTROLS_THUMBNAILS_SIZE,
|
|
||||||
createRangedTransform(toPixelsOrDelete, THUMBNAIL_WIDTH_MIN, THUMBNAIL_WIDTH_MAX),
|
|
||||||
),
|
|
||||||
upgradeWithOverrides(
|
|
||||||
'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(
|
|
||||||
'event_viewer.controls.next_previous.size',
|
|
||||||
createRangedTransform(toPixelsOrDelete, BUTTON_SIZE_MIN),
|
|
||||||
),
|
|
||||||
upgradeWithOverrides(
|
|
||||||
CONF_MENU_BUTTON_SIZE,
|
|
||||||
createRangedTransform(toPixelsOrDelete, BUTTON_SIZE_MIN),
|
|
||||||
),
|
|
||||||
upgradeWithOverrides('event_gallery.min_columns', deleteProperty),
|
|
||||||
upgradeMenuModeToStyleAndPosition(),
|
|
||||||
upgradeWithOverrides(CONF_MENU_BUTTONS_FRIGATE, menuButtonBooleanToObject),
|
|
||||||
upgradeWithOverrides(CONF_MENU_BUTTONS_CAMERAS, menuButtonBooleanToObject),
|
|
||||||
upgradeWithOverrides(CONF_MENU_BUTTONS_LIVE, menuButtonBooleanToObject),
|
|
||||||
upgradeWithOverrides(CONF_MENU_BUTTONS_CLIPS, menuButtonBooleanToObject),
|
|
||||||
upgradeWithOverrides(CONF_MENU_BUTTONS_SNAPSHOTS, menuButtonBooleanToObject),
|
|
||||||
upgradeWithOverrides(CONF_MENU_BUTTONS_IMAGE, menuButtonBooleanToObject),
|
|
||||||
upgradeWithOverrides(CONF_MENU_BUTTONS_DOWNLOAD, menuButtonBooleanToObject),
|
|
||||||
upgradeWithOverrides('menu.buttons.frigate_ui', menuButtonBooleanToObject),
|
|
||||||
upgradeWithOverrides(CONF_MENU_BUTTONS_FULLSCREEN, menuButtonBooleanToObject),
|
|
||||||
upgrade(CONF_LIVE_LAZY_UNLOAD, (val) =>
|
|
||||||
typeof val === 'boolean' ? (val ? 'all' : 'never') : undefined,
|
|
||||||
),
|
|
||||||
upgrade(CONF_LIVE_AUTO_UNMUTE, (val) =>
|
|
||||||
typeof val === 'boolean' ? (val ? 'all' : 'never') : undefined,
|
|
||||||
),
|
|
||||||
upgrade('event_viewer.auto_play', (val) =>
|
|
||||||
typeof val === 'boolean' ? (val ? 'all' : 'never') : undefined,
|
|
||||||
),
|
|
||||||
upgrade('event_viewer.auto_unmute', (val) =>
|
|
||||||
typeof val === 'boolean' ? (val ? 'all' : 'never') : undefined,
|
|
||||||
),
|
|
||||||
upgradeMoveToWithOverrides('event_viewer', CONF_MEDIA_VIEWER),
|
|
||||||
upgradeArrayValue(CONF_CAMERAS, upgradeMoveTo('camera_name', 'frigate.camera_name')),
|
|
||||||
upgradeArrayValue(CONF_CAMERAS, upgradeMoveTo('client_id', 'frigate.client_id')),
|
|
||||||
upgradeArrayValue(CONF_CAMERAS, upgradeMoveTo('label', 'frigate.label')),
|
|
||||||
upgradeArrayValue(CONF_CAMERAS, upgradeMoveTo('frigate_url', 'frigate.url')),
|
|
||||||
upgradeArrayValue(CONF_CAMERAS, upgradeMoveTo('zone', 'frigate.zone')),
|
|
||||||
|
|
||||||
// v4.0.0-rc.1 -> v4.0.0-rc.3
|
|
||||||
upgradeThumbnailShowControlsToIndividualControls('event_gallery.controls.thumbnails'),
|
|
||||||
upgradeThumbnailShowControlsToIndividualControls('media_viewer.controls.thumbnails'),
|
|
||||||
upgradeThumbnailShowControlsToIndividualControls('live.controls.thumbnails'),
|
|
||||||
upgradeThumbnailShowControlsToIndividualControls('timeline.controls.thumbnails'),
|
|
||||||
|
|
||||||
// v4.0.0 -> v4.1.0
|
// v4.0.0 -> v4.1.0
|
||||||
upgradeArrayValue(
|
upgradeArrayOfObjects(
|
||||||
CONF_OVERRIDES,
|
CONF_OVERRIDES,
|
||||||
transformConditionMediaLoaded,
|
conditionMediaLoadedTransform,
|
||||||
(data) => data.conditions as RawFrigateCardConfig | undefined,
|
(data) => data.conditions as RawFrigateCardConfig | undefined,
|
||||||
),
|
),
|
||||||
(data: unknown): boolean => {
|
(data: unknown): boolean => {
|
||||||
return recursiveUpgradeObject(
|
return upgradeObjectRecursively(
|
||||||
transformConditionMediaLoaded,
|
conditionMediaLoadedTransform,
|
||||||
(data) => data.conditions as RawFrigateCardConfig | undefined,
|
(data) => data.conditions as RawFrigateCardConfig | undefined,
|
||||||
)(typeof data === 'object' && data ? data[CONF_ELEMENTS] : {});
|
)(typeof data === 'object' && data ? data[CONF_ELEMENTS] : {});
|
||||||
},
|
},
|
||||||
upgradeMoveToWithOverrides('event_gallery', CONF_MEDIA_GALLERY),
|
upgradeMoveToWithOverrides('event_gallery', CONF_MEDIA_GALLERY),
|
||||||
upgradeMoveToWithOverrides('menu.buttons.frigate_ui', CONF_MENU_BUTTONS_CAMERA_UI),
|
upgradeMoveToWithOverrides('menu.buttons.frigate_ui', CONF_MENU_BUTTONS_CAMERA_UI),
|
||||||
(data: unknown): boolean => {
|
(data: unknown): boolean => {
|
||||||
return recursiveUpgradeObject(transformFrigateUIAction)(
|
return upgradeObjectRecursively(frigateUIActionTransform)(
|
||||||
typeof data === 'object' && data ? <RawFrigateCardConfig>data : {},
|
typeof data === 'object' && data ? <RawFrigateCardConfig>data : {},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
upgradeArrayValue(
|
upgradeArrayOfObjects(
|
||||||
CONF_CAMERAS,
|
CONF_CAMERAS,
|
||||||
upgradeWithOverrides('live_provider', (val) =>
|
upgradeWithOverrides('live_provider', (val) =>
|
||||||
val === 'frigate-jsmpeg' ? 'jsmpeg' : val,
|
val === 'frigate-jsmpeg' ? 'jsmpeg' : val,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
upgradeMoveToWithOverrides('live.image', CONF_CAMERAS_GLOBAL_IMAGE),
|
|
||||||
upgradeMoveToWithOverrides('live.jsmpeg', CONF_CAMERAS_GLOBAL_JSMPEG),
|
upgradeMoveToWithOverrides('live.jsmpeg', CONF_CAMERAS_GLOBAL_JSMPEG),
|
||||||
|
upgradeMoveToWithOverrides('live.image', CONF_CAMERAS_GLOBAL_IMAGE),
|
||||||
upgradeMoveToWithOverrides('live.webrtc_card', CONF_CAMERAS_GLOBAL_WEBRTC_CARD),
|
upgradeMoveToWithOverrides('live.webrtc_card', CONF_CAMERAS_GLOBAL_WEBRTC_CARD),
|
||||||
upgradeArrayValue(
|
upgradeArrayOfObjects(
|
||||||
CONF_CAMERAS,
|
CONF_CAMERAS,
|
||||||
upgradeMoveToWithOverrides('frigate.zone', 'frigate.zones', {
|
upgradeMoveToWithOverrides('frigate.zone', 'frigate.zones', {
|
||||||
transform: (zone) => arrayify(zone),
|
transform: (zone) => arrayify(zone),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
upgradeArrayValue(
|
upgradeArrayOfObjects(
|
||||||
CONF_CAMERAS,
|
CONF_CAMERAS,
|
||||||
upgradeMoveToWithOverrides('frigate.label', 'frigate.labels', {
|
upgradeMoveToWithOverrides('frigate.label', 'frigate.labels', {
|
||||||
transform: (label) => arrayify(label),
|
transform: (label) => arrayify(label),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// v5.2.0 -> v6.0.0
|
||||||
|
(data: unknown): boolean => {
|
||||||
|
return upgradeObjectRecursively(serviceDataToDataTransform)(
|
||||||
|
typeof data === 'object' && data ? <RawFrigateCardConfig>data : {},
|
||||||
|
);
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
+3
-3
@@ -6,7 +6,7 @@ import {
|
|||||||
NoActionConfig,
|
NoActionConfig,
|
||||||
ToggleActionConfig,
|
ToggleActionConfig,
|
||||||
UrlActionConfig,
|
UrlActionConfig,
|
||||||
} from 'custom-card-helpers';
|
} from '@dermotduffy/custom-card-helpers';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { MEDIA_CHUNK_SIZE_DEFAULT, MEDIA_CHUNK_SIZE_MAX } from '../const.js';
|
import { MEDIA_CHUNK_SIZE_DEFAULT, MEDIA_CHUNK_SIZE_MAX } from '../const.js';
|
||||||
import { deepRemoveDefaults } from '../utils/zod.js';
|
import { deepRemoveDefaults } from '../utils/zod.js';
|
||||||
@@ -133,7 +133,7 @@ const callServiceActionSchema = schemaForType<
|
|||||||
actionBaseSchema.extend({
|
actionBaseSchema.extend({
|
||||||
action: z.literal('call-service'),
|
action: z.literal('call-service'),
|
||||||
service: z.string(),
|
service: z.string(),
|
||||||
service_data: z.object({}).passthrough().optional(),
|
data: z.object({}).passthrough().optional(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -498,7 +498,7 @@ export const frigateCardPTZSchema = z.preprocess(
|
|||||||
tap_action: {
|
tap_action: {
|
||||||
action: 'call-service',
|
action: 'call-service',
|
||||||
service: data['service'],
|
service: data['service'],
|
||||||
service_data: data[`data_${name}`],
|
data: data[`data_${name}`],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
delete out[`data_${name}`];
|
delete out[`data_${name}`];
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import { fireEvent, HomeAssistant, LovelaceCardEditor } from 'custom-card-helpers';
|
import { fireEvent, HomeAssistant, LovelaceCardEditor } from '@dermotduffy/custom-card-helpers';
|
||||||
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
||||||
import { customElement, property, state } from 'lit/decorators.js';
|
import { customElement, property, state } from 'lit/decorators.js';
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import * as en from './languages/en.json';
|
import * as en from './languages/en.json';
|
||||||
|
|
||||||
const DEFAULT_LANG = 'en' as const;
|
const DEFAULT_LANG = 'en' as const;
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant, LovelaceCardConfig, Themes } from 'custom-card-helpers';
|
import { HomeAssistant, LovelaceCardConfig, Themes } from '@dermotduffy/custom-card-helpers';
|
||||||
import { StyleInfo } from 'lit/directives/style-map.js';
|
import { StyleInfo } from 'lit/directives/style-map.js';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ import {
|
|||||||
handleActionConfig,
|
handleActionConfig,
|
||||||
hasAction,
|
hasAction,
|
||||||
HomeAssistant,
|
HomeAssistant,
|
||||||
} from 'custom-card-helpers';
|
} from '@dermotduffy/custom-card-helpers';
|
||||||
import {
|
import {
|
||||||
Actions,
|
Actions,
|
||||||
ActionType,
|
ActionType,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import pkg from '../../package.json';
|
import pkg from '../../package.json';
|
||||||
import { RawFrigateCardConfig } from '../config/types';
|
import { RawFrigateCardConfig } from '../config/types';
|
||||||
import { getLanguage } from '../localize/localize';
|
import { getLanguage } from '../localize/localize';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import add from 'date-fns/add';
|
import add from 'date-fns/add';
|
||||||
import { homeAssistantWSRequest } from '..';
|
import { homeAssistantWSRequest } from '..';
|
||||||
import { MemoryRequestCache } from '../../../camera-manager/cache';
|
import { MemoryRequestCache } from '../../../camera-manager/cache';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { homeAssistantWSRequest } from '.';
|
import { homeAssistantWSRequest } from '.';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { homeAssistantWSRequest } from '..';
|
import { homeAssistantWSRequest } from '..';
|
||||||
import { EntityCache } from './cache';
|
import { EntityCache } from './cache';
|
||||||
import { Entity, EntityList, entitySchema, entityListSchema } from './types.js';
|
import { Entity, EntityList, entitySchema, entityListSchema } from './types.js';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { computeDomain, HomeAssistant } from 'custom-card-helpers';
|
import { computeDomain, HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { HassEntity } from 'home-assistant-js-websocket';
|
import { HassEntity } from 'home-assistant-js-websocket';
|
||||||
import { Entity } from './entity-registry/types';
|
import { Entity } from './entity-registry/types';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { computeDomain, computeStateDomain, HomeAssistant } from 'custom-card-helpers';
|
import { computeDomain, computeStateDomain, HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { HassEntity, MessageBase } from 'home-assistant-js-websocket';
|
import { HassEntity, MessageBase } from 'home-assistant-js-websocket';
|
||||||
import { StyleInfo } from 'lit/directives/style-map.js';
|
import { StyleInfo } from 'lit/directives/style-map.js';
|
||||||
import { ZodSchema } from 'zod';
|
import { ZodSchema } from 'zod';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import QuickLRU from 'quick-lru';
|
import QuickLRU from 'quick-lru';
|
||||||
import { homeAssistantWSRequest } from '.';
|
import { homeAssistantWSRequest } from '.';
|
||||||
import { ResolvedMedia, resolvedMediaSchema } from '../../types.js';
|
import { ResolvedMedia, resolvedMediaSchema } from '../../types.js';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { binarySensorIcon } from './binary-sensor-icon';
|
|||||||
import { coverIcon } from './cover-icon';
|
import { coverIcon } from './cover-icon';
|
||||||
import { sensorIcon } from './sensor-icon';
|
import { sensorIcon } from './sensor-icon';
|
||||||
|
|
||||||
export const DEFAULT_DOMAIN_ICON = 'mdi:bookmark';
|
const DEFAULT_DOMAIN_ICON = 'mdi:bookmark';
|
||||||
|
|
||||||
const FIXED_DOMAIN_ICONS = {
|
const FIXED_DOMAIN_ICONS = {
|
||||||
alert: 'mdi:alert',
|
alert: 'mdi:alert',
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { UNIT_C, UNIT_F } from 'custom-card-helpers';
|
import { UNIT_C, UNIT_F } from '@dermotduffy/custom-card-helpers';
|
||||||
import { HassEntity } from 'home-assistant-js-websocket';
|
import { HassEntity } from 'home-assistant-js-websocket';
|
||||||
|
|
||||||
const FIXED_DEVICE_CLASS_ICONS = {
|
const FIXED_DEVICE_CLASS_ICONS = {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Task } from '@lit-labs/task';
|
import { Task } from '@lit-labs/task';
|
||||||
import { ReactiveControllerHost } from '@lit/reactive-element';
|
import { ReactiveControllerHost } from '@lit/reactive-element';
|
||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
|
|
||||||
// See: https://github.com/sindresorhus/is-absolute-url
|
// See: https://github.com/sindresorhus/is-absolute-url
|
||||||
// Scheme: https://tools.ietf.org/html/rfc3986#section-3.1
|
// Scheme: https://tools.ietf.org/html/rfc3986#section-3.1
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import isEqual from 'lodash-es/isEqual';
|
import isEqual from 'lodash-es/isEqual';
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import { mock } from 'vitest-mock-extended';
|
import { mock } from 'vitest-mock-extended';
|
||||||
|
|||||||
@@ -0,0 +1,742 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import {
|
||||||
|
copyConfig,
|
||||||
|
createRangedTransform,
|
||||||
|
deleteConfigValue,
|
||||||
|
deleteTransform,
|
||||||
|
getArrayConfigPath,
|
||||||
|
getConfigValue,
|
||||||
|
isConfigUpgradeable,
|
||||||
|
moveConfigValue,
|
||||||
|
setConfigValue,
|
||||||
|
upgradeArrayOfObjects,
|
||||||
|
upgradeConfig,
|
||||||
|
upgradeMoveTo,
|
||||||
|
upgradeMoveToWithOverrides,
|
||||||
|
upgradeObjectRecursively,
|
||||||
|
upgradeWithOverrides,
|
||||||
|
} from '../src/config-mgmt';
|
||||||
|
import { RawFrigateCardConfig } from '../src/config/types';
|
||||||
|
|
||||||
|
describe('general functions', () => {
|
||||||
|
it('should set value', () => {
|
||||||
|
const target = {};
|
||||||
|
setConfigValue(target, 'a', 10);
|
||||||
|
expect(target).toEqual({
|
||||||
|
a: 10,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should get value', () => {
|
||||||
|
it('present', () => {
|
||||||
|
expect(getConfigValue({ b: 11 }, 'b')).toEqual(11);
|
||||||
|
});
|
||||||
|
it('absent', () => {
|
||||||
|
expect(getConfigValue({ b: 11 }, 'c')).toBeUndefined();
|
||||||
|
});
|
||||||
|
it('absent with default', () => {
|
||||||
|
expect(getConfigValue({ b: 11 }, 'c', 12)).toBe(12);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should unset value', () => {
|
||||||
|
it('nested', () => {
|
||||||
|
const target = {
|
||||||
|
moo: {
|
||||||
|
foo: {
|
||||||
|
a: 10,
|
||||||
|
},
|
||||||
|
bar: {
|
||||||
|
b: 11,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
deleteConfigValue(target, 'moo.foo');
|
||||||
|
expect(target).toEqual({ moo: { bar: { b: 11 } } });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('top-level', () => {
|
||||||
|
const target = {
|
||||||
|
a: 10,
|
||||||
|
b: 11,
|
||||||
|
};
|
||||||
|
deleteConfigValue(target, 'a');
|
||||||
|
expect(target).toEqual({ b: 11 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should copy config', () => {
|
||||||
|
const target = {
|
||||||
|
a: {
|
||||||
|
b: {
|
||||||
|
c: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const copy = copyConfig(target);
|
||||||
|
|
||||||
|
expect(copy).toEqual(target);
|
||||||
|
expect(copy).not.toBe(target);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get array config path', () => {
|
||||||
|
expect(getArrayConfigPath('a.#.b', 10)).toBe('a.[10].b');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('upgrade functions', () => {
|
||||||
|
it('should determine if config is upgradeable', () => {
|
||||||
|
expect(
|
||||||
|
// Upgrade example: rename of service_data to data.
|
||||||
|
isConfigUpgradeable({
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: 'icon',
|
||||||
|
icon: 'mdi:cow',
|
||||||
|
style: {
|
||||||
|
right: '20px',
|
||||||
|
top: '20px',
|
||||||
|
color: 'white',
|
||||||
|
},
|
||||||
|
tap_action: {
|
||||||
|
action: 'call-service',
|
||||||
|
service: 'notify.persistent_notification',
|
||||||
|
service_data: {
|
||||||
|
message: 'Hello 1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should create ranged transform', () => {
|
||||||
|
describe('with numbers', () => {
|
||||||
|
it('inside range', () => {
|
||||||
|
expect(createRangedTransform((val) => val, 10, 20)(11)).toBe(11);
|
||||||
|
});
|
||||||
|
it('outside range', () => {
|
||||||
|
expect(createRangedTransform((val) => val, 10, 20)(1)).toBe(10);
|
||||||
|
});
|
||||||
|
it('with a range', () => {
|
||||||
|
expect(createRangedTransform((val) => val)(100)).toBe(100);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('with non-number', () => {
|
||||||
|
expect(createRangedTransform((_val) => 'foo')(1)).toBe('foo');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null from delete property transform', () => {
|
||||||
|
expect(deleteTransform(10)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should move config value', () => {
|
||||||
|
it('simple', () => {
|
||||||
|
const config = {
|
||||||
|
foo: {
|
||||||
|
c: 10,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(moveConfigValue(config, 'foo', 'bar')).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
bar: {
|
||||||
|
c: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('in place', () => {
|
||||||
|
it('non-transformed', () => {
|
||||||
|
const config = {
|
||||||
|
foo: {
|
||||||
|
c: 10,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(moveConfigValue(config, 'foo', 'foo')).toBeFalsy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
foo: {
|
||||||
|
c: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('transformed', () => {
|
||||||
|
const config = {
|
||||||
|
foo: {
|
||||||
|
c: 10,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
moveConfigValue(config, 'foo.c', 'foo.c', { transform: (val) => String(val) }),
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
foo: {
|
||||||
|
c: '10',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with transform result', () => {
|
||||||
|
it('move', () => {
|
||||||
|
const config = {
|
||||||
|
c: 10,
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
moveConfigValue(config, 'c', 'd', { transform: (val) => String(val) }),
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(config).toEqual({ d: '10' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keep original', () => {
|
||||||
|
const config = {
|
||||||
|
c: 10,
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
moveConfigValue(config, 'c', 'd', {
|
||||||
|
transform: (val) => String(val),
|
||||||
|
keepOriginal: true,
|
||||||
|
}),
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(config).toEqual({ c: 10, d: '10' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with transform null result', () => {
|
||||||
|
it('remove', () => {
|
||||||
|
const config = {
|
||||||
|
c: 10,
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
moveConfigValue(config, 'c', 'd', { transform: (_val) => null }),
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(config).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keep', () => {
|
||||||
|
const config = {
|
||||||
|
c: 10,
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
moveConfigValue(config, 'c', 'd', {
|
||||||
|
transform: (_val) => null,
|
||||||
|
keepOriginal: true,
|
||||||
|
}),
|
||||||
|
).toBeFalsy();
|
||||||
|
expect(config).toEqual({ c: 10 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('with transform undefined result', () => {
|
||||||
|
const config = {
|
||||||
|
c: 10,
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
moveConfigValue(config, 'c', 'd', { transform: (_val) => undefined }),
|
||||||
|
).toBeFalsy();
|
||||||
|
expect(config).toEqual({ c: 10 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should upgrade with a move', () => {
|
||||||
|
const config = {
|
||||||
|
c: 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(upgradeMoveTo('c', 'd')(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({ d: 10 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should upgrade config and overrides with a move', () => {
|
||||||
|
const config = {
|
||||||
|
c: 10,
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
overrides: {
|
||||||
|
c: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(upgradeMoveToWithOverrides('c', 'd')(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({ d: 10, overrides: [{ overrides: { d: 10 } }] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should upgrade config and overrides in-place', () => {
|
||||||
|
const config = {
|
||||||
|
c: 10,
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
overrides: {
|
||||||
|
c: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(upgradeWithOverrides('c', (val) => String(val))(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({ c: '10', overrides: [{ overrides: { c: '10' } }] });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should upgrade array', () => {
|
||||||
|
it('in case of non-array', () => {
|
||||||
|
const config = { c: 10 };
|
||||||
|
expect(upgradeArrayOfObjects('c', (_val) => false)(config)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('in case of non-object items', () => {
|
||||||
|
const config = { c: [10, 11] };
|
||||||
|
expect(upgradeArrayOfObjects('c', (_val) => false)(config)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('in case of array', () => {
|
||||||
|
const config = { c: [{ d: 10 }, { d: 11 }] };
|
||||||
|
expect(
|
||||||
|
upgradeArrayOfObjects('c', (val) => {
|
||||||
|
val['e'] = 12;
|
||||||
|
return true;
|
||||||
|
})(config),
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
c: [
|
||||||
|
{
|
||||||
|
d: 10,
|
||||||
|
e: 12,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
d: 11,
|
||||||
|
e: 12,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should recursively upgrade', () => {
|
||||||
|
it('ignoring simple objects', () => {
|
||||||
|
const config = { c: 10, d: 10 };
|
||||||
|
expect(upgradeObjectRecursively((_val) => false)(config)).toBeFalsy();
|
||||||
|
expect(config).toEqual({ c: 10, d: 10 });
|
||||||
|
});
|
||||||
|
it('iterating into arrays', () => {
|
||||||
|
const config = { values: [{ c: 10 }, { d: 10 }, 'random'] };
|
||||||
|
expect(
|
||||||
|
upgradeObjectRecursively((val) => {
|
||||||
|
if (!Array.isArray(val)) {
|
||||||
|
val['e'] = 11;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})(config),
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
|
expect(config).toEqual({
|
||||||
|
e: 11,
|
||||||
|
values: [{ c: 10, e: 11 }, { d: 10, e: 11 }, 'random'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have upgrades with bad input data', () => {
|
||||||
|
expect(upgradeConfig(3 as unknown as RawFrigateCardConfig)).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should handle version specific upgrades', () => {
|
||||||
|
describe('v4.1.0', () => {
|
||||||
|
describe('should rename mediaLoaded to media_loaded', () => {
|
||||||
|
it('elements', () => {
|
||||||
|
const config = {
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
conditions: {
|
||||||
|
mediaLoaded: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
conditions: 'not an object',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
conditions: {
|
||||||
|
media_loaded: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
conditions: 'not an object',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('overrides', () => {
|
||||||
|
const config = {
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
conditions: {
|
||||||
|
mediaLoaded: true,
|
||||||
|
},
|
||||||
|
overrides: {
|
||||||
|
view: {
|
||||||
|
default: 'clips',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
conditions: {
|
||||||
|
media_loaded: true,
|
||||||
|
},
|
||||||
|
overrides: {
|
||||||
|
view: {
|
||||||
|
default: 'clips',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should rename event_gallery to media_gallery', () => {
|
||||||
|
const config = {
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
event_gallery: {
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
media_gallery: {
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should rename menu.buttons.frigate_ui to menu.buttons.camera_ui', () => {
|
||||||
|
const config = {
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
menu: {
|
||||||
|
buttons: {
|
||||||
|
frigate_ui: {
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
conditions: {},
|
||||||
|
overrides: {
|
||||||
|
menu: {
|
||||||
|
buttons: {
|
||||||
|
frigate_ui: {
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
menu: {
|
||||||
|
buttons: {
|
||||||
|
camera_ui: {
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
conditions: {},
|
||||||
|
overrides: {
|
||||||
|
menu: {
|
||||||
|
buttons: {
|
||||||
|
camera_ui: {
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should rename frigate ui actions', () => {
|
||||||
|
const config = {
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: 'icon',
|
||||||
|
icon: 'mdi:cow',
|
||||||
|
tap_action: {
|
||||||
|
action: 'custom:frigate-card-action',
|
||||||
|
frigate_card_action: 'frigate_ui',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
view: {
|
||||||
|
actions: {
|
||||||
|
double_tap_action: {
|
||||||
|
action: 'custom:frigate-card-action',
|
||||||
|
frigate_card_action: 'frigate_ui',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: 'icon',
|
||||||
|
icon: 'mdi:cow',
|
||||||
|
tap_action: {
|
||||||
|
action: 'custom:frigate-card-action',
|
||||||
|
frigate_card_action: 'camera_ui',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
view: {
|
||||||
|
actions: {
|
||||||
|
double_tap_action: {
|
||||||
|
action: 'custom:frigate-card-action',
|
||||||
|
frigate_card_action: 'camera_ui',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should rename frigate-jsmpeg provider to jsmpeg', () => {
|
||||||
|
it('in cameras', () => {
|
||||||
|
const config = {
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ live_provider: 'ha' }, { live_provider: 'frigate-jsmpeg' }],
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ live_provider: 'ha' }, { live_provider: 'jsmpeg' }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should move live object into cameras_global', () => {
|
||||||
|
it.each([['image' as const], ['jsmpeg' as const], ['webrtc_card' as const]])(
|
||||||
|
'%s',
|
||||||
|
(objName: string) => {
|
||||||
|
const config = {
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
live: {
|
||||||
|
[objName]: {
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras_global: {
|
||||||
|
[objName]: {
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
live: {},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should convert to array and rename', () => {
|
||||||
|
it.each([['zone' as const], ['label' as const]])(`%s`, (objName: string) => {
|
||||||
|
const config = {
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [
|
||||||
|
{ live_provider: 'ha', frigate: { [objName]: 'foo' } },
|
||||||
|
{ live_provider: 'jsmpeg' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [
|
||||||
|
{ live_provider: 'ha', frigate: { [objName + 's']: ['foo'] } },
|
||||||
|
{ live_provider: 'jsmpeg' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert and rename frigate.label to frigate.labels', () => {
|
||||||
|
const config = {
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [
|
||||||
|
{ live_provider: 'ha', frigate: { zone: 'foo' } },
|
||||||
|
{ live_provider: 'jsmpeg' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [
|
||||||
|
{ live_provider: 'ha', frigate: { zones: ['foo'] } },
|
||||||
|
{ live_provider: 'jsmpeg' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('v5.2.0', () => {
|
||||||
|
describe('should rename service_data to data', () => {
|
||||||
|
it('positive case', () => {
|
||||||
|
const config = {
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: 'icon',
|
||||||
|
icon: 'mdi:cow',
|
||||||
|
style: {
|
||||||
|
right: '20px',
|
||||||
|
top: '20px',
|
||||||
|
color: 'white',
|
||||||
|
},
|
||||||
|
tap_action: {
|
||||||
|
action: 'call-service',
|
||||||
|
service: 'notify.persistent_notification',
|
||||||
|
service_data: {
|
||||||
|
message: 'Hello 1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'service-button',
|
||||||
|
title: 'title',
|
||||||
|
service: 'service',
|
||||||
|
service_data: {
|
||||||
|
message: "It's a trick",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
view: {
|
||||||
|
actions: {
|
||||||
|
double_tap_action: {
|
||||||
|
action: 'call-service',
|
||||||
|
service: 'notify.persistent_notification',
|
||||||
|
service_data: {
|
||||||
|
message: 'Hello 2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hold_action: {
|
||||||
|
action: 'call-service',
|
||||||
|
service: 'notify.persistent_notification',
|
||||||
|
data: {
|
||||||
|
message: 'Hello 3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: 'icon',
|
||||||
|
icon: 'mdi:cow',
|
||||||
|
style: {
|
||||||
|
right: '20px',
|
||||||
|
top: '20px',
|
||||||
|
color: 'white',
|
||||||
|
},
|
||||||
|
tap_action: {
|
||||||
|
action: 'call-service',
|
||||||
|
service: 'notify.persistent_notification',
|
||||||
|
data: {
|
||||||
|
message: 'Hello 1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'service-button',
|
||||||
|
title: 'title',
|
||||||
|
service: 'service',
|
||||||
|
// Trick: This *is* still called service_data in HA, so should not
|
||||||
|
// be modified.
|
||||||
|
service_data: {
|
||||||
|
message: "It's a trick",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
view: {
|
||||||
|
actions: {
|
||||||
|
double_tap_action: {
|
||||||
|
action: 'call-service',
|
||||||
|
service: 'notify.persistent_notification',
|
||||||
|
data: {
|
||||||
|
message: 'Hello 2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hold_action: {
|
||||||
|
action: 'call-service',
|
||||||
|
service: 'notify.persistent_notification',
|
||||||
|
data: {
|
||||||
|
message: 'Hello 3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('negative case', () => {
|
||||||
|
const config = {
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
view: {
|
||||||
|
default: 'live',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeFalsy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
view: {
|
||||||
|
default: 'live',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -335,7 +335,7 @@ describe('should convert webrtc card PTZ to Frigate card PTZ', () => {
|
|||||||
tap_action: {
|
tap_action: {
|
||||||
action: 'call-service',
|
action: 'call-service',
|
||||||
service: 'foo',
|
service: 'foo',
|
||||||
service_data: {
|
data: {
|
||||||
tap_action: {
|
tap_action: {
|
||||||
action: 'none',
|
action: 'none',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { handleActionConfig, hasAction } from 'custom-card-helpers';
|
import { handleActionConfig, hasAction } from '@dermotduffy/custom-card-helpers';
|
||||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||||
import { mock } from 'vitest-mock-extended';
|
import { mock } from 'vitest-mock-extended';
|
||||||
import { actionSchema } from '../../src/config/types';
|
import { actionSchema } from '../../src/config/types';
|
||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
} from '../../src/utils/action';
|
} from '../../src/utils/action';
|
||||||
import { createHASS } from '../test-utils';
|
import { createHASS } from '../test-utils';
|
||||||
|
|
||||||
vi.mock('custom-card-helpers');
|
vi.mock('@dermotduffy/custom-card-helpers');
|
||||||
|
|
||||||
describe('convertActionToFrigateCardCustomAction', () => {
|
describe('convertActionToFrigateCardCustomAction', () => {
|
||||||
it('should skip null action', () => {
|
it('should skip null action', () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import {
|
import {
|
||||||
getEntityIcon,
|
getEntityIcon,
|
||||||
|
|||||||
@@ -603,6 +603,21 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@dermotduffy/custom-card-helpers@npm:^1.9.0":
|
||||||
|
version: 1.9.0
|
||||||
|
resolution: "@dermotduffy/custom-card-helpers@npm:1.9.0"
|
||||||
|
dependencies:
|
||||||
|
"@formatjs/intl-utils": ^3.8.4
|
||||||
|
home-assistant-js-websocket: ^6.0.1
|
||||||
|
intl-messageformat: ^9.11.1
|
||||||
|
lit: ^2.1.1
|
||||||
|
rollup: ^2.63.0
|
||||||
|
superstruct: ^0.15.3
|
||||||
|
typescript: ^4.5.4
|
||||||
|
checksum: 356b6ee436a9c07ce09f8ce435d39eae0448f3d9b4eaef1d32bd6ce3565d5a84c57624b3a28506d265bf55c86bb444e8211054bc9cdd0eb6cf08e1a5ca1e7e4d
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@dermotduffy/panzoom@npm:^4.5.1":
|
"@dermotduffy/panzoom@npm:^4.5.1":
|
||||||
version: 4.5.1
|
version: 4.5.1
|
||||||
resolution: "@dermotduffy/panzoom@npm:4.5.1"
|
resolution: "@dermotduffy/panzoom@npm:4.5.1"
|
||||||
@@ -2379,21 +2394,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"custom-card-helpers@npm:^1.9.0":
|
|
||||||
version: 1.9.0
|
|
||||||
resolution: "custom-card-helpers@npm:1.9.0"
|
|
||||||
dependencies:
|
|
||||||
"@formatjs/intl-utils": ^3.8.4
|
|
||||||
home-assistant-js-websocket: ^6.0.1
|
|
||||||
intl-messageformat: ^9.11.1
|
|
||||||
lit: ^2.1.1
|
|
||||||
rollup: ^2.63.0
|
|
||||||
superstruct: ^0.15.3
|
|
||||||
typescript: ^4.5.4
|
|
||||||
checksum: 5cefdad2487c1d9e6a190e1837637817c8f7ac86c970c2117fac9b57493f2b235d99af301a7bafcf0a53d1c42b7e75b2accda63de6381607d4beb6d175aa07f4
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"data-urls@npm:^4.0.0":
|
"data-urls@npm:^4.0.0":
|
||||||
version: 4.0.0
|
version: 4.0.0
|
||||||
resolution: "data-urls@npm:4.0.0"
|
resolution: "data-urls@npm:4.0.0"
|
||||||
@@ -3324,6 +3324,7 @@ __metadata:
|
|||||||
"@babel/plugin-proposal-class-properties": ^7.18.6
|
"@babel/plugin-proposal-class-properties": ^7.18.6
|
||||||
"@babel/plugin-proposal-decorators": ^7.19.0
|
"@babel/plugin-proposal-decorators": ^7.19.0
|
||||||
"@cycjimmy/jsmpeg-player": ^6.0.4
|
"@cycjimmy/jsmpeg-player": ^6.0.4
|
||||||
|
"@dermotduffy/custom-card-helpers": ^1.9.0
|
||||||
"@dermotduffy/panzoom": ^4.5.1
|
"@dermotduffy/panzoom": ^4.5.1
|
||||||
"@egjs/hammerjs": ^2.0.17
|
"@egjs/hammerjs": ^2.0.17
|
||||||
"@graphiteds/core": ^1.9.11
|
"@graphiteds/core": ^1.9.11
|
||||||
@@ -3343,7 +3344,6 @@ __metadata:
|
|||||||
"@vitest/coverage-istanbul": ^0.34.3
|
"@vitest/coverage-istanbul": ^0.34.3
|
||||||
component-emitter: ^1.3.0
|
component-emitter: ^1.3.0
|
||||||
crypto: ^1.0.1
|
crypto: ^1.0.1
|
||||||
custom-card-helpers: ^1.9.0
|
|
||||||
date-fns: ^2.29.2
|
date-fns: ^2.29.2
|
||||||
date-fns-tz: ^1.3.7
|
date-fns-tz: ^1.3.7
|
||||||
embla-carousel: 8.0.0-rc14
|
embla-carousel: 8.0.0-rc14
|
||||||
|
|||||||
Reference in New Issue
Block a user