Add editor and migration support.
This commit is contained in:
@@ -432,32 +432,16 @@ Scan mode tracks Home Assistant state *changes* -- when the card is first starte
|
|||||||
| Option | Default | Overridable | Description |
|
| Option | Default | Overridable | Description |
|
||||||
| - | - | - | - |
|
| - | - | - | - |
|
||||||
| `enabled` | `false` | :white_check_mark: | Whether to enable scan mode. |
|
| `enabled` | `false` | :white_check_mark: | Whether to enable scan mode. |
|
||||||
| `interaction_mode` | `inactive` | :white_check_mark: | Whether actions should be taken when the card is being interacted with. If `all`, actions will always be taken regardless. If `inactive` actions will only be taken if the card has *not* had human interaction recently (as defined by `view.timeout_seconds`). If `active` actions will only be taken if the card *has* had human interaction recently. This does not stop triggering itself (i.e. border will still pulse if `trigger_show_status` is true) but rather just prevents the actions being performed.|
|
| `filter_selected_camera` | `false` | :white_check_mark: | If set to `true` will only trigger on the currently selected camera.|
|
||||||
| `trigger_filter_camera` | `all` | :white_check_mark: | If set to `all` the camera will be triggered regardless of which camera is currently selected, if set to `selected` the camera will only trigger if that camera is already selected.|
|
| `show_trigger_status` | `true` | :white_check_mark: | Whether or not the card should show a visual indication that it is triggered (a pulsing border around the card edge). |
|
||||||
| `trigger_show_status` | `true` | :white_check_mark: | Whether or not the card should show a visual indication that it is triggered (a pulsing border around the card edge). |
|
|
||||||
| `trigger_action` | Selects triggered camera in `live` view (see below) | :white_check_mark: | An action or list of actions that are executed when a camera is triggered. May be set to `null` for no action.|
|
|
||||||
| `untrigger_reset` | `true` | :white_check_mark: | Whether or not to reset the view to the default after untriggering. |
|
|
||||||
| `untrigger_seconds` | `0` | :white_check_mark: | The number of seconds to wait after all entities are inactive before untriggering. |
|
| `untrigger_seconds` | `0` | :white_check_mark: | The number of seconds to wait after all entities are inactive before untriggering. |
|
||||||
| `untrigger_action` | Selects default view and camera (see below) | :white_check_mark: | An action or list of actions that are executed when a camera is triggered. May bet to `null` for no action.|
|
| `actions` | | :white_check_mark: | The actions to take when scan mode triggers (see below). |
|
||||||
|
|
||||||
##### View: Scan Mode default actions
|
#### View: Scan Mode Actions configuration
|
||||||
|
|
||||||
The default `trigger_action` is:
|
| `trigger` | `live` | :white_check_mark | When `live` the trigger will select the triggered camera in `live` view, when `none` will take no action. |
|
||||||
|
| `untrigger` | `default` | :white_check_mark | When `default` the untrigger will return to the default view and camera, when `none` will take no action. |
|
||||||
```yaml
|
| `interaction_mode` | `inactive` | :white_check_mark: | Whether actions should be taken when the card is being interacted with. If `all`, actions will always be taken regardless. If `inactive` actions will only be taken if the card has *not* had human interaction recently (as defined by `view.timeout_seconds`). If `active` actions will only be taken if the card *has* had human interaction recently. This does not stop triggering itself (i.e. border will still pulse if `show_trigger_status` is true) but rather just prevents the actions being performed.|
|
||||||
- action: custom:frigate-card-action
|
|
||||||
frigate_card_action: camera_select
|
|
||||||
triggered: true
|
|
||||||
- action: custom:frigate-card-action
|
|
||||||
frigate_card_action: live
|
|
||||||
```
|
|
||||||
|
|
||||||
The default `untrigger_action` is:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- action: custom:frigate-card-action
|
|
||||||
frigate_card_action: default
|
|
||||||
```
|
|
||||||
|
|
||||||
### Menu Options
|
### Menu Options
|
||||||
|
|
||||||
@@ -1935,9 +1919,13 @@ view:
|
|||||||
dark_mode: 'off'
|
dark_mode: 'off'
|
||||||
scan:
|
scan:
|
||||||
enabled: false
|
enabled: false
|
||||||
trigger_show_status: true
|
show_trigger_status: true
|
||||||
untrigger_reset: true
|
filter_selected_camera: false
|
||||||
untrigger_seconds: 0
|
untrigger_seconds: 0
|
||||||
|
actions:
|
||||||
|
interaction_mode: 'inactive' as const,
|
||||||
|
trigger: 'live' as const,
|
||||||
|
untrigger: 'default' as const,
|
||||||
actions:
|
actions:
|
||||||
entity: light.office_main_lights
|
entity: light.office_main_lights
|
||||||
tap_action:
|
tap_action:
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ export class TriggersManager {
|
|||||||
public updateView(oldView?: View | null): void {
|
public updateView(oldView?: View | null): void {
|
||||||
if (oldView?.camera !== this._api.getViewManager().getView()?.camera) {
|
if (oldView?.camera !== this._api.getViewManager().getView()?.camera) {
|
||||||
// If the view changes, a new camera may have been selected, which may
|
// If the view changes, a new camera may have been selected, which may
|
||||||
// mean a trigger is required (in the case that `trigger_filter_camera`
|
// mean a trigger is required (in the case that `filter_selected_camera`
|
||||||
// has been set to `selected`).
|
// is true).
|
||||||
this._evaluateTriggers();
|
this._evaluateTriggers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,12 +91,11 @@ export class TriggersManager {
|
|||||||
for (const cameraID of this._triggeredState.keys()) {
|
for (const cameraID of this._triggeredState.keys()) {
|
||||||
if (
|
if (
|
||||||
!this._triggeredCameras.has(cameraID) &&
|
!this._triggeredCameras.has(cameraID) &&
|
||||||
(scanConfig.trigger_filter_camera === 'all' ||
|
(!scanConfig.filter_selected_camera ||
|
||||||
(scanConfig.trigger_filter_camera === 'selected' &&
|
cameraID === this._api.getViewManager().getView()?.camera)
|
||||||
cameraID === this._api.getViewManager().getView()?.camera))
|
|
||||||
) {
|
) {
|
||||||
this._triggeredCameras.set(cameraID, now);
|
this._triggeredCameras.set(cameraID, now);
|
||||||
this._triggerAction();
|
this._triggerAction(cameraID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,23 +106,26 @@ export class TriggersManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _hasAllowableInteractionState(): boolean {
|
protected _hasAllowableInteractionStateForAction(): boolean {
|
||||||
const scanConfig = this._api.getConfigManager().getConfig()?.view.scan;
|
const scanConfig = this._api.getConfigManager().getConfig()?.view.scan;
|
||||||
const hasInteraction = this._api.getInteractionManager().hasInteraction();
|
const hasInteraction = this._api.getInteractionManager().hasInteraction();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
!!scanConfig &&
|
!!scanConfig &&
|
||||||
(scanConfig.interaction_mode === 'all' ||
|
(scanConfig.actions.interaction_mode === 'all' ||
|
||||||
(scanConfig.interaction_mode === 'active' && hasInteraction) ||
|
(scanConfig.actions.interaction_mode === 'active' && hasInteraction) ||
|
||||||
(scanConfig.interaction_mode === 'inactive' && !hasInteraction))
|
(scanConfig.actions.interaction_mode === 'inactive' && !hasInteraction))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _triggerAction(): void {
|
protected _triggerAction(cameraID: string): void {
|
||||||
const action = this._api.getConfigManager().getConfig()?.view.scan.trigger_action;
|
const action = this._api.getConfigManager().getConfig()?.view.scan.actions.trigger;
|
||||||
|
|
||||||
if (action && this._hasAllowableInteractionState()) {
|
if (action === 'live' && this._hasAllowableInteractionStateForAction()) {
|
||||||
this._api.getActionsManager().executeActions(action);
|
this._api.getViewManager().setViewByParameters({
|
||||||
|
viewName: 'live',
|
||||||
|
cameraID: cameraID,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must update master element to add border pulsing.
|
// Must update master element to add border pulsing.
|
||||||
@@ -131,10 +133,10 @@ export class TriggersManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected _untriggerAction(cameraID: string): void {
|
protected _untriggerAction(cameraID: string): void {
|
||||||
const action = this._api.getConfigManager().getConfig()?.view.scan.untrigger_action;
|
const action = this._api.getConfigManager().getConfig()?.view.scan.actions.untrigger;
|
||||||
|
|
||||||
if (action && this._hasAllowableInteractionState()) {
|
if (action === 'default' && this._hasAllowableInteractionStateForAction()) {
|
||||||
this._api.getActionsManager().executeActions(action);
|
this._api.getViewManager().setViewDefault();
|
||||||
}
|
}
|
||||||
this._triggeredCameras.delete(cameraID);
|
this._triggeredCameras.delete(cameraID);
|
||||||
this._deleteTimer(cameraID);
|
this._deleteTimer(cameraID);
|
||||||
|
|||||||
@@ -197,7 +197,6 @@ export interface CardStyleAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface CardTriggersAPI {
|
export interface CardTriggersAPI {
|
||||||
getActionsManager(): ActionsManager;
|
|
||||||
getCameraManager(): CameraManager;
|
getCameraManager(): CameraManager;
|
||||||
getCardElementManager(): CardElementManager;
|
getCardElementManager(): CardElementManager;
|
||||||
getConfigManager(): ConfigManager;
|
getConfigManager(): ConfigManager;
|
||||||
|
|||||||
+1
-1
@@ -241,7 +241,7 @@ class FrigateCard extends LitElement {
|
|||||||
const cardClasses = {
|
const cardClasses = {
|
||||||
triggered:
|
triggered:
|
||||||
!!this._controller.getTriggersManager().isTriggered() &&
|
!!this._controller.getTriggersManager().isTriggered() &&
|
||||||
!!this._config?.view.scan.trigger_show_status,
|
!!this._config?.view.scan.show_trigger_status,
|
||||||
};
|
};
|
||||||
const mainClasses = {
|
const mainClasses = {
|
||||||
main: true,
|
main: true,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
CONF_MEDIA_GALLERY,
|
CONF_MEDIA_GALLERY,
|
||||||
CONF_MENU_BUTTONS_CAMERA_UI,
|
CONF_MENU_BUTTONS_CAMERA_UI,
|
||||||
CONF_OVERRIDES,
|
CONF_OVERRIDES,
|
||||||
|
CONF_VIEW_SCAN_ACTIONS_UNTRIGGER,
|
||||||
} from './const';
|
} from './const';
|
||||||
import { arrayify } from './utils/basic';
|
import { arrayify } from './utils/basic';
|
||||||
|
|
||||||
@@ -474,4 +475,12 @@ const UPGRADES = [
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
upgradePTZElementsToLive(),
|
upgradePTZElementsToLive(),
|
||||||
|
upgradeMoveToWithOverrides(
|
||||||
|
'view.scan.untrigger_reset',
|
||||||
|
CONF_VIEW_SCAN_ACTIONS_UNTRIGGER,
|
||||||
|
{
|
||||||
|
// Delete the value if it's set to the default.
|
||||||
|
transform: (val) => (val ? 'default' : null),
|
||||||
|
},
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|||||||
+22
-72
@@ -1,42 +1,3 @@
|
|||||||
// TODO: Config migration
|
|
||||||
// TODO: Editor support for scan mode changes
|
|
||||||
// TODO: Consider this:
|
|
||||||
|
|
||||||
/*
|
|
||||||
// NOW ===>
|
|
||||||
view:
|
|
||||||
scan:
|
|
||||||
enabled: true
|
|
||||||
trigger_show_status: true,
|
|
||||||
trigger_action:
|
|
||||||
- action: custom:frigate-card-action
|
|
||||||
frigate_card_action: camera_select
|
|
||||||
triggered: true
|
|
||||||
- action: custom:frigate-card-action
|
|
||||||
frigate_card_action: live
|
|
||||||
untrigger_action:
|
|
||||||
- action: custom:frigate-card-action
|
|
||||||
interaction_mode: inactive
|
|
||||||
trigger_filter_camera: all
|
|
||||||
untrigger_seconds: 0
|
|
||||||
|
|
||||||
view:
|
|
||||||
scan:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
// TO
|
|
||||||
|
|
||||||
view:
|
|
||||||
scan:
|
|
||||||
enabled: true
|
|
||||||
trigger_show_status: true
|
|
||||||
actions:
|
|
||||||
live | live-reset | none
|
|
||||||
interaction: inactive | active | all
|
|
||||||
trigger_filter_camera: all
|
|
||||||
untrigger_seconds: 0
|
|
||||||
automations:
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CallServiceActionConfig,
|
CallServiceActionConfig,
|
||||||
@@ -1160,47 +1121,36 @@ const viewConfigDefault = {
|
|||||||
dark_mode: 'off' as const,
|
dark_mode: 'off' as const,
|
||||||
scan: {
|
scan: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
trigger_show_status: true,
|
show_trigger_status: true,
|
||||||
trigger_action: [
|
filter_selected_camera: false,
|
||||||
{
|
actions: {
|
||||||
action: 'custom:frigate-card-action' as const,
|
interaction_mode: 'inactive' as const,
|
||||||
frigate_card_action: 'camera_select' as const,
|
trigger: 'live' as const,
|
||||||
triggered: true,
|
untrigger: 'default' as const,
|
||||||
},
|
|
||||||
{
|
|
||||||
action: 'custom:frigate-card-action' as const,
|
|
||||||
frigate_card_action: 'live' as const,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
untrigger_action: {
|
|
||||||
action: 'custom:frigate-card-action' as const,
|
|
||||||
frigate_card_action: 'default' as const,
|
|
||||||
},
|
},
|
||||||
interaction_mode: 'inactive' as const,
|
|
||||||
trigger_filter_camera: 'all' as const,
|
|
||||||
untrigger_seconds: 0,
|
untrigger_seconds: 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const scanSchema = z.object({
|
export const scanSchema = z.object({
|
||||||
enabled: z.boolean().default(viewConfigDefault.scan.enabled),
|
enabled: z.boolean().default(viewConfigDefault.scan.enabled),
|
||||||
|
|
||||||
interaction_mode: z
|
filter_selected_camera: z
|
||||||
.enum(['all', 'inactive', 'active'])
|
.boolean()
|
||||||
.default(viewConfigDefault.scan.interaction_mode),
|
.default(viewConfigDefault.scan.filter_selected_camera),
|
||||||
trigger_filter_camera: z
|
show_trigger_status: z.boolean().default(viewConfigDefault.scan.show_trigger_status),
|
||||||
.enum(['all', 'selected'])
|
|
||||||
.default(viewConfigDefault.scan.trigger_filter_camera),
|
|
||||||
trigger_show_status: z.boolean().default(viewConfigDefault.scan.trigger_show_status),
|
|
||||||
trigger_action: actionSchema
|
|
||||||
.or(actionSchema.array())
|
|
||||||
.nullable()
|
|
||||||
.default(viewConfigDefault.scan.trigger_action),
|
|
||||||
|
|
||||||
untrigger_action: actionSchema
|
actions: z
|
||||||
.or(actionSchema.array())
|
.object({
|
||||||
.nullable()
|
interaction_mode: z
|
||||||
.default(viewConfigDefault.scan.untrigger_action),
|
.enum(['all', 'inactive', 'active'])
|
||||||
|
.default(viewConfigDefault.scan.actions.interaction_mode),
|
||||||
|
trigger: z.enum(['live', 'none']).default(viewConfigDefault.scan.actions.trigger),
|
||||||
|
untrigger: z
|
||||||
|
.enum(['default', 'none'])
|
||||||
|
.default(viewConfigDefault.scan.actions.untrigger),
|
||||||
|
})
|
||||||
|
.default(viewConfigDefault.scan.actions),
|
||||||
untrigger_seconds: z.number().default(viewConfigDefault.scan.untrigger_seconds),
|
untrigger_seconds: z.number().default(viewConfigDefault.scan.untrigger_seconds),
|
||||||
});
|
});
|
||||||
export type ScanOptions = z.infer<typeof scanSchema>;
|
export type ScanOptions = z.infer<typeof scanSchema>;
|
||||||
|
|||||||
+11
-2
@@ -77,10 +77,19 @@ export const CONF_VIEW_UPDATE_FORCE = `${CONF_VIEW}.update_force` as const;
|
|||||||
export const CONF_VIEW_UPDATE_SECONDS = `${CONF_VIEW}.update_seconds` as const;
|
export const CONF_VIEW_UPDATE_SECONDS = `${CONF_VIEW}.update_seconds` as const;
|
||||||
export const CONF_VIEW_SCAN = `${CONF_VIEW}.scan` as const;
|
export const CONF_VIEW_SCAN = `${CONF_VIEW}.scan` as const;
|
||||||
export const CONF_VIEW_SCAN_ENABLED = `${CONF_VIEW_SCAN}.enabled` as const;
|
export const CONF_VIEW_SCAN_ENABLED = `${CONF_VIEW_SCAN}.enabled` as const;
|
||||||
export const CONF_VIEW_SCAN_TRIGGER_SHOW_STATUS =
|
export const CONF_VIEW_SCAN_SHOW_TRIGGER_STATUS =
|
||||||
`${CONF_VIEW_SCAN}.trigger_show_status` as const;
|
`${CONF_VIEW_SCAN}.show_trigger_status` as const;
|
||||||
|
export const CONF_VIEW_SCAN_FILTER_SELECTED_CAMERA =
|
||||||
|
`${CONF_VIEW_SCAN}.filter_selected_camera` as const;
|
||||||
export const CONF_VIEW_SCAN_UNTRIGGER_SECONDS =
|
export const CONF_VIEW_SCAN_UNTRIGGER_SECONDS =
|
||||||
`${CONF_VIEW_SCAN}.untrigger_seconds` as const;
|
`${CONF_VIEW_SCAN}.untrigger_seconds` as const;
|
||||||
|
export const CONF_VIEW_SCAN_ACTIONS = `${CONF_VIEW_SCAN}.actions` as const;
|
||||||
|
export const CONF_VIEW_SCAN_ACTIONS_TRIGGER =
|
||||||
|
`${CONF_VIEW_SCAN_ACTIONS}.trigger` as const;
|
||||||
|
export const CONF_VIEW_SCAN_ACTIONS_UNTRIGGER =
|
||||||
|
`${CONF_VIEW_SCAN_ACTIONS}.untrigger` as const;
|
||||||
|
export const CONF_VIEW_SCAN_ACTIONS_INTERACTION_MODE =
|
||||||
|
`${CONF_VIEW_SCAN_ACTIONS}.interaction_mode` as const;
|
||||||
|
|
||||||
export const CONF_MEDIA_GALLERY = 'media_gallery' as const;
|
export const CONF_MEDIA_GALLERY = 'media_gallery' as const;
|
||||||
export const CONF_MEDIA_GALLERY_CONTROLS_FILTER_MODE =
|
export const CONF_MEDIA_GALLERY_CONTROLS_FILTER_MODE =
|
||||||
|
|||||||
+109
-39
@@ -181,8 +181,13 @@ import {
|
|||||||
CONF_VIEW_DARK_MODE,
|
CONF_VIEW_DARK_MODE,
|
||||||
CONF_VIEW_DEFAULT,
|
CONF_VIEW_DEFAULT,
|
||||||
CONF_VIEW_SCAN,
|
CONF_VIEW_SCAN,
|
||||||
|
CONF_VIEW_SCAN_ACTIONS,
|
||||||
|
CONF_VIEW_SCAN_ACTIONS_INTERACTION_MODE,
|
||||||
|
CONF_VIEW_SCAN_ACTIONS_TRIGGER,
|
||||||
|
CONF_VIEW_SCAN_ACTIONS_UNTRIGGER,
|
||||||
CONF_VIEW_SCAN_ENABLED,
|
CONF_VIEW_SCAN_ENABLED,
|
||||||
CONF_VIEW_SCAN_TRIGGER_SHOW_STATUS,
|
CONF_VIEW_SCAN_FILTER_SELECTED_CAMERA,
|
||||||
|
CONF_VIEW_SCAN_SHOW_TRIGGER_STATUS,
|
||||||
CONF_VIEW_SCAN_UNTRIGGER_SECONDS,
|
CONF_VIEW_SCAN_UNTRIGGER_SECONDS,
|
||||||
CONF_VIEW_TIMEOUT_SECONDS,
|
CONF_VIEW_TIMEOUT_SECONDS,
|
||||||
CONF_VIEW_UPDATE_CYCLE_CAMERA,
|
CONF_VIEW_UPDATE_CYCLE_CAMERA,
|
||||||
@@ -237,6 +242,7 @@ const MENU_PERFORMANCE_FEATURES = 'performance.features';
|
|||||||
const MENU_PERFORMANCE_STYLE = 'performance.style';
|
const MENU_PERFORMANCE_STYLE = 'performance.style';
|
||||||
const MENU_TIMELINE_CONTROLS_THUMBNAILS = 'timeline.controls.thumbnails';
|
const MENU_TIMELINE_CONTROLS_THUMBNAILS = 'timeline.controls.thumbnails';
|
||||||
const MENU_VIEW_SCAN = 'scan';
|
const MENU_VIEW_SCAN = 'scan';
|
||||||
|
const MENU_VIEW_SCAN_ACTIONS = 'scan.actions';
|
||||||
|
|
||||||
interface EditorOptionsSet {
|
interface EditorOptionsSet {
|
||||||
icon: string;
|
icon: string;
|
||||||
@@ -620,6 +626,46 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
protected _scanActionsInteractionModes: EditorSelectOption[] = [
|
||||||
|
{ value: '', label: '' },
|
||||||
|
{
|
||||||
|
value: 'all',
|
||||||
|
label: localize('config.view.scan.actions.interaction_modes.all'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'inactive',
|
||||||
|
label: localize('config.view.scan.actions.interaction_modes.inactive'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'active',
|
||||||
|
label: localize('config.view.scan.actions.interaction_modes.active'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
protected _scanActionsTrigger: EditorSelectOption[] = [
|
||||||
|
{ value: '', label: '' },
|
||||||
|
{
|
||||||
|
value: 'live',
|
||||||
|
label: localize('config.view.scan.actions.triggers.live'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'none',
|
||||||
|
label: localize('config.view.scan.actions.triggers.none'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
protected _scanActionsUntrigger: EditorSelectOption[] = [
|
||||||
|
{ value: '', label: '' },
|
||||||
|
{
|
||||||
|
value: 'default',
|
||||||
|
label: localize('config.view.scan.actions.untriggers.default'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'none',
|
||||||
|
label: localize('config.view.scan.actions.untriggers.none'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
public setConfig(config: RawFrigateCardConfig): void {
|
public setConfig(config: RawFrigateCardConfig): void {
|
||||||
// Note: This does not use Zod to parse the configuration, so it may be
|
// Note: This does not use Zod to parse the configuration, so it may be
|
||||||
// partially or completely invalid. It's more useful to have a partially
|
// partially or completely invalid. It's more useful to have a partially
|
||||||
@@ -876,44 +922,68 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected _renderViewScanMenu(): TemplateResult {
|
protected _renderViewScanMenu(): TemplateResult {
|
||||||
const submenuClasses = {
|
return this._putInSubmenu(
|
||||||
submenu: true,
|
MENU_VIEW_SCAN,
|
||||||
selected: !!this._expandedMenus[MENU_VIEW_SCAN],
|
true,
|
||||||
};
|
`config.${CONF_VIEW_SCAN}.editor_label`,
|
||||||
return html`
|
{ name: 'mdi:target-account' },
|
||||||
<div class="${classMap(submenuClasses)}">
|
html`
|
||||||
<div
|
${this._renderSwitch(CONF_VIEW_SCAN_ENABLED, this._defaults.view.scan.enabled, {
|
||||||
class="submenu-header"
|
label: localize(`config.${CONF_VIEW_SCAN_ENABLED}`),
|
||||||
@click=${this._toggleMenu}
|
})}
|
||||||
.domain=${MENU_VIEW_SCAN}
|
${this._renderSwitch(
|
||||||
.key=${true}
|
CONF_VIEW_SCAN_FILTER_SELECTED_CAMERA,
|
||||||
>
|
this._defaults.view.scan.filter_selected_camera,
|
||||||
<ha-icon .icon=${'mdi:target-account'}></ha-icon>
|
{
|
||||||
<span>${localize(`config.${CONF_VIEW_SCAN}.scan_mode`)}</span>
|
label: localize(`config.${CONF_VIEW_SCAN_FILTER_SELECTED_CAMERA}`),
|
||||||
</div>
|
},
|
||||||
${this._expandedMenus[MENU_VIEW_SCAN]
|
)}
|
||||||
? html` <div class="values">
|
${this._renderSwitch(
|
||||||
${this._renderSwitch(
|
CONF_VIEW_SCAN_SHOW_TRIGGER_STATUS,
|
||||||
CONF_VIEW_SCAN_ENABLED,
|
this._defaults.view.scan.show_trigger_status,
|
||||||
this._defaults.view.scan.enabled,
|
{
|
||||||
{
|
label: localize(`config.${CONF_VIEW_SCAN_SHOW_TRIGGER_STATUS}`),
|
||||||
label: localize(`config.${CONF_VIEW_SCAN_ENABLED}`),
|
},
|
||||||
},
|
)}
|
||||||
)}
|
${this._renderNumberInput(CONF_VIEW_SCAN_UNTRIGGER_SECONDS, {
|
||||||
${this._renderSwitch(
|
default: this._defaults.view.scan.untrigger_seconds,
|
||||||
CONF_VIEW_SCAN_TRIGGER_SHOW_STATUS,
|
})}
|
||||||
this._defaults.view.scan.trigger_show_status,
|
${this._renderOptionSelector(
|
||||||
{
|
CONF_VIEW_SCAN_ACTIONS_TRIGGER,
|
||||||
label: localize(`config.${CONF_VIEW_SCAN_TRIGGER_SHOW_STATUS}`),
|
this._scanActionsTrigger,
|
||||||
},
|
{
|
||||||
)}
|
label: localize('config.view.scan.actions.trigger'),
|
||||||
${this._renderNumberInput(CONF_VIEW_SCAN_UNTRIGGER_SECONDS, {
|
},
|
||||||
default: this._defaults.view.scan.untrigger_seconds,
|
)}
|
||||||
})}
|
${this._putInSubmenu(
|
||||||
</div>`
|
MENU_VIEW_SCAN_ACTIONS,
|
||||||
: ''}
|
true,
|
||||||
</div>
|
`config.${CONF_VIEW_SCAN_ACTIONS}.editor_label`,
|
||||||
`;
|
{ name: 'mdi:cogs' },
|
||||||
|
html` ${this._renderOptionSelector(
|
||||||
|
CONF_VIEW_SCAN_ACTIONS_TRIGGER,
|
||||||
|
this._scanActionsTrigger,
|
||||||
|
{
|
||||||
|
label: localize('config.view.scan.actions.trigger'),
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
${this._renderOptionSelector(
|
||||||
|
CONF_VIEW_SCAN_ACTIONS_UNTRIGGER,
|
||||||
|
this._scanActionsUntrigger,
|
||||||
|
{
|
||||||
|
label: localize('config.view.scan.actions.untrigger'),
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
${this._renderOptionSelector(
|
||||||
|
CONF_VIEW_SCAN_ACTIONS_INTERACTION_MODE,
|
||||||
|
this._scanActionsInteractionModes,
|
||||||
|
{
|
||||||
|
label: localize('config.view.scan.actions.interaction_mode'),
|
||||||
|
},
|
||||||
|
)}`,
|
||||||
|
)}
|
||||||
|
`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -385,10 +385,29 @@
|
|||||||
},
|
},
|
||||||
"default": "Default view",
|
"default": "Default view",
|
||||||
"scan": {
|
"scan": {
|
||||||
|
"actions": {
|
||||||
|
"editor_label": "Scan mode actions",
|
||||||
|
"interaction_mode": "How to handle actions when the card has human interaction",
|
||||||
|
"interaction_modes": {
|
||||||
|
"active": "Only trigger actions when card has human interaction",
|
||||||
|
"all": "Trigger actions regardless of human interaction",
|
||||||
|
"inactive": "Only trigger actions when card has no human interaction"
|
||||||
|
},
|
||||||
|
"trigger": "Trigger action",
|
||||||
|
"triggers": {
|
||||||
|
"live": "Change to live",
|
||||||
|
"none": "Take no action"
|
||||||
|
},
|
||||||
|
"untrigger": "Untrigger action",
|
||||||
|
"untriggers": {
|
||||||
|
"default": "Change to default view/camera",
|
||||||
|
"none": "Take no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"editor_label": "Scan mode",
|
||||||
|
"filter_selected_camera": "Only trigger on selected camera",
|
||||||
"enabled": "Scan mode enabled",
|
"enabled": "Scan mode enabled",
|
||||||
"scan_mode": "Scan mode",
|
"show_trigger_status": "Show pulsing border when triggered",
|
||||||
"trigger_show_status": "Show pulsing border when triggered",
|
|
||||||
"untrigger_reset": "Reset the view to default after untrigger",
|
|
||||||
"untrigger_seconds": "Seconds after inactive state change to untrigger"
|
"untrigger_seconds": "Seconds after inactive state change to untrigger"
|
||||||
},
|
},
|
||||||
"timeout_seconds": "Reset to default view X seconds after user action (0=never)",
|
"timeout_seconds": "Reset to default view X seconds after user action (0=never)",
|
||||||
|
|||||||
@@ -381,10 +381,29 @@
|
|||||||
},
|
},
|
||||||
"default": "Visualizzazione predefinita",
|
"default": "Visualizzazione predefinita",
|
||||||
"scan": {
|
"scan": {
|
||||||
|
"actions": {
|
||||||
|
"editor_label": "",
|
||||||
|
"interaction_mode": "",
|
||||||
|
"interaction_modes": {
|
||||||
|
"active": "",
|
||||||
|
"all": "",
|
||||||
|
"inactive": ""
|
||||||
|
},
|
||||||
|
"trigger": "",
|
||||||
|
"triggers": {
|
||||||
|
"live": "",
|
||||||
|
"none": ""
|
||||||
|
},
|
||||||
|
"untrigger": "",
|
||||||
|
"untriggers": {
|
||||||
|
"default": "",
|
||||||
|
"none": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"editor_label": "Modalità di scansione",
|
||||||
"enabled": "Modalità di scansione abilitata",
|
"enabled": "Modalità di scansione abilitata",
|
||||||
"scan_mode": "Modalità di scansione",
|
"filter_selected_camera": "",
|
||||||
"trigger_show_status": "Mostra bordo pulsante quando attivato",
|
"show_trigger_status": "Mostra bordo pulsante quando attivato",
|
||||||
"untrigger_reset": "Reset the view to default after untrigger",
|
|
||||||
"untrigger_seconds": "Reimposta la vista ai valori predefiniti dopo aver annullato l'attivazione"
|
"untrigger_seconds": "Reimposta la vista ai valori predefiniti dopo aver annullato l'attivazione"
|
||||||
},
|
},
|
||||||
"timeout_seconds": "Ripristina la vista predefinita x secondi dopo l'azione dell'utente (0 = mai)",
|
"timeout_seconds": "Ripristina la vista predefinita x secondi dopo l'azione dell'utente (0 = mai)",
|
||||||
|
|||||||
@@ -384,10 +384,29 @@
|
|||||||
},
|
},
|
||||||
"default": "Visualização padrão",
|
"default": "Visualização padrão",
|
||||||
"scan": {
|
"scan": {
|
||||||
|
"actions": {
|
||||||
|
"editor_label": "",
|
||||||
|
"interaction_mode": "",
|
||||||
|
"interaction_modes": {
|
||||||
|
"active": "",
|
||||||
|
"all": "",
|
||||||
|
"inactive": ""
|
||||||
|
},
|
||||||
|
"trigger": "",
|
||||||
|
"triggers": {
|
||||||
|
"live": "",
|
||||||
|
"none": ""
|
||||||
|
},
|
||||||
|
"untrigger": "",
|
||||||
|
"untriggers": {
|
||||||
|
"default": "",
|
||||||
|
"none": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"editor_label": "Modo scan",
|
||||||
"enabled": "Modo scan ativado",
|
"enabled": "Modo scan ativado",
|
||||||
"scan_mode": "Modo scan",
|
"filter_selected_camera": "",
|
||||||
"trigger_show_status": "Pulsar borda quando acionado",
|
"show_trigger_status": "Pulsar borda quando acionado",
|
||||||
"untrigger_reset": "Redefinir a visualização para o padrão após desacionar",
|
|
||||||
"untrigger_seconds": "Segundos após a mudar para o estado inativo para desacionar"
|
"untrigger_seconds": "Segundos após a mudar para o estado inativo para desacionar"
|
||||||
},
|
},
|
||||||
"timeout_seconds": "Redefinir para a visualização padrão X segundos após a ação do usuário (0 = nunca)",
|
"timeout_seconds": "Redefinir para a visualização padrão X segundos após a ação do usuário (0 = nunca)",
|
||||||
|
|||||||
@@ -374,10 +374,29 @@
|
|||||||
},
|
},
|
||||||
"default": "Visualização padrão",
|
"default": "Visualização padrão",
|
||||||
"scan": {
|
"scan": {
|
||||||
|
"actions": {
|
||||||
|
"editor_label": "",
|
||||||
|
"interaction_mode": "",
|
||||||
|
"interaction_modes": {
|
||||||
|
"active": "",
|
||||||
|
"all": "",
|
||||||
|
"inactive": ""
|
||||||
|
},
|
||||||
|
"trigger": "",
|
||||||
|
"triggers": {
|
||||||
|
"live": "",
|
||||||
|
"none": ""
|
||||||
|
},
|
||||||
|
"untrigger": "",
|
||||||
|
"untriggers": {
|
||||||
|
"default": "",
|
||||||
|
"none": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"editor_label": "Modo scan",
|
||||||
"enabled": "Modo scan ativado",
|
"enabled": "Modo scan ativado",
|
||||||
"scan_mode": "Modo scan",
|
"filter_selected_camera": "",
|
||||||
"trigger_show_status": "Exibir estado do gatilho",
|
"show_trigger_status": "Exibir estado do gatilho",
|
||||||
"untrigger_reset": "Redefinir a visualização para o padrão após desacionar",
|
|
||||||
"untrigger_seconds": "Segundos após a mudar para o estado inativo para desacionar"
|
"untrigger_seconds": "Segundos após a mudar para o estado inativo para desacionar"
|
||||||
},
|
},
|
||||||
"timeout_seconds": "Redefinir para a visualização padrão X segundos após a ação do usuário (0 = nunca)",
|
"timeout_seconds": "Redefinir para a visualização padrão X segundos após a ação do usuário (0 = nunca)",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { HassEntities } from 'home-assistant-js-websocket';
|
|||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import { CardController } from '../../src/card-controller/controller';
|
import { CardController } from '../../src/card-controller/controller';
|
||||||
import { TriggersManager } from '../../src/card-controller/triggers-manager';
|
import { TriggersManager } from '../../src/card-controller/triggers-manager';
|
||||||
import { ScanOptions } from '../../src/config/types';
|
import { ScanOptions, scanSchema } from '../../src/config/types';
|
||||||
import {
|
import {
|
||||||
createCameraConfig,
|
createCameraConfig,
|
||||||
createCameraManager,
|
createCameraManager,
|
||||||
@@ -18,22 +18,11 @@ import {
|
|||||||
const baseScanConfig: Partial<ScanOptions> = {
|
const baseScanConfig: Partial<ScanOptions> = {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
untrigger_seconds: 10,
|
untrigger_seconds: 10,
|
||||||
interaction_mode: 'inactive' as const,
|
filter_selected_camera: false,
|
||||||
trigger_filter_camera: 'all' as const,
|
actions: {
|
||||||
trigger_action: [
|
trigger: 'live' as const,
|
||||||
{
|
untrigger: 'default' as const,
|
||||||
action: 'fire-dom-event' as const,
|
interaction_mode: 'inactive' as const,
|
||||||
frigate_card_action: 'camera_select' as const,
|
|
||||||
triggered: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
action: 'fire-dom-event' as const,
|
|
||||||
frigate_card_action: 'live' as const,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
untrigger_action: {
|
|
||||||
action: 'fire-dom-event' as const,
|
|
||||||
frigate_card_action: 'default' as const,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -48,7 +37,7 @@ const createTriggerAPI = (options?: {
|
|||||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||||
createConfig({
|
createConfig({
|
||||||
view: {
|
view: {
|
||||||
scan: options?.config ?? baseScanConfig,
|
scan: options?.config ? scanSchema.parse(options.config) : baseScanConfig,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -134,17 +123,10 @@ describe('TriggersManager', () => {
|
|||||||
manager.updateTriggerHAState(createHASS(hassInactiveState));
|
manager.updateTriggerHAState(createHASS(hassInactiveState));
|
||||||
|
|
||||||
expect(manager.isTriggered()).toBeTruthy();
|
expect(manager.isTriggered()).toBeTruthy();
|
||||||
expect(api.getActionsManager().executeActions).toBeCalledWith([
|
expect(api.getViewManager().setViewByParameters).toBeCalledWith({
|
||||||
{
|
viewName: 'live' as const,
|
||||||
action: 'fire-dom-event' as const,
|
cameraID: 'camera_1' as const,
|
||||||
frigate_card_action: 'camera_select' as const,
|
});
|
||||||
triggered: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
action: 'fire-dom-event' as const,
|
|
||||||
frigate_card_action: 'live' as const,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(
|
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(
|
||||||
createHASS(hassInactiveState),
|
createHASS(hassInactiveState),
|
||||||
@@ -163,10 +145,7 @@ describe('TriggersManager', () => {
|
|||||||
|
|
||||||
expect(manager.isTriggered()).toBeFalsy();
|
expect(manager.isTriggered()).toBeFalsy();
|
||||||
|
|
||||||
expect(api.getActionsManager().executeActions).toBeCalledWith({
|
expect(api.getViewManager().setViewDefault).toBeCalled();
|
||||||
action: 'fire-dom-event' as const,
|
|
||||||
frigate_card_action: 'default' as const,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should trigger when entity state is active on startup', () => {
|
it('should trigger when entity state is active on startup', () => {
|
||||||
@@ -191,7 +170,7 @@ describe('TriggersManager', () => {
|
|||||||
manager.updateTriggerHAState(createHASS(hassInactiveState));
|
manager.updateTriggerHAState(createHASS(hassInactiveState));
|
||||||
expect(manager.isTriggered()).toBeTruthy();
|
expect(manager.isTriggered()).toBeTruthy();
|
||||||
|
|
||||||
expect(api.getActionsManager().executeActions).not.toBeCalled();
|
expect(api.getViewManager().setViewByParameters).not.toBeCalled();
|
||||||
|
|
||||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(
|
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(
|
||||||
createHASS(hassInactiveState),
|
createHASS(hassInactiveState),
|
||||||
@@ -203,7 +182,39 @@ describe('TriggersManager', () => {
|
|||||||
|
|
||||||
expect(manager.isTriggered()).toBeFalsy();
|
expect(manager.isTriggered()).toBeFalsy();
|
||||||
|
|
||||||
expect(api.getActionsManager().executeActions).not.toBeCalled();
|
expect(api.getViewManager().setViewDefault).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should take no actions when actions are set to none', () => {
|
||||||
|
const start = new Date('2023-10-01T17:14');
|
||||||
|
const api = createTriggerAPI({
|
||||||
|
hassStates: hassActiveState,
|
||||||
|
config: {
|
||||||
|
enabled: true,
|
||||||
|
actions: {
|
||||||
|
interaction_mode: 'all',
|
||||||
|
trigger: 'none',
|
||||||
|
untrigger: 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const manager = new TriggersManager(api);
|
||||||
|
manager.updateTriggerHAState(createHASS(hassInactiveState));
|
||||||
|
expect(manager.isTriggered()).toBeTruthy();
|
||||||
|
|
||||||
|
expect(api.getViewManager().setViewByParameters).not.toBeCalled();
|
||||||
|
|
||||||
|
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(
|
||||||
|
createHASS(hassInactiveState),
|
||||||
|
);
|
||||||
|
manager.updateTriggerHAState(createHASS(hassActiveState));
|
||||||
|
|
||||||
|
vi.setSystemTime(add(start, { seconds: 10 }));
|
||||||
|
vi.runOnlyPendingTimers();
|
||||||
|
|
||||||
|
expect(manager.isTriggered()).toBeFalsy();
|
||||||
|
|
||||||
|
expect(api.getViewManager().setViewDefault).not.toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should take actions with human interactions when interaction mode is active', () => {
|
it('should take actions with human interactions when interaction mode is active', () => {
|
||||||
@@ -214,14 +225,21 @@ describe('TriggersManager', () => {
|
|||||||
interaction: true,
|
interaction: true,
|
||||||
config: {
|
config: {
|
||||||
...baseScanConfig,
|
...baseScanConfig,
|
||||||
interaction_mode: 'active',
|
actions: {
|
||||||
|
trigger: 'live' as const,
|
||||||
|
untrigger: 'default' as const,
|
||||||
|
interaction_mode: 'active',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const manager = new TriggersManager(api);
|
const manager = new TriggersManager(api);
|
||||||
manager.updateTriggerHAState(createHASS(hassInactiveState));
|
manager.updateTriggerHAState(createHASS(hassInactiveState));
|
||||||
expect(manager.isTriggered()).toBeTruthy();
|
expect(manager.isTriggered()).toBeTruthy();
|
||||||
|
|
||||||
expect(api.getActionsManager().executeActions).toBeCalledTimes(1);
|
expect(api.getViewManager().setViewByParameters).toBeCalledWith({
|
||||||
|
viewName: 'live' as const,
|
||||||
|
cameraID: 'camera_1' as const,
|
||||||
|
});
|
||||||
|
|
||||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(
|
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(
|
||||||
createHASS(hassInactiveState),
|
createHASS(hassInactiveState),
|
||||||
@@ -233,7 +251,7 @@ describe('TriggersManager', () => {
|
|||||||
|
|
||||||
expect(manager.isTriggered()).toBeFalsy();
|
expect(manager.isTriggered()).toBeFalsy();
|
||||||
|
|
||||||
expect(api.getActionsManager().executeActions).toBeCalledTimes(2);
|
expect(api.getViewManager().setViewDefault).toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should report multiple triggered cameras', () => {
|
it('should report multiple triggered cameras', () => {
|
||||||
@@ -303,7 +321,7 @@ describe('TriggersManager', () => {
|
|||||||
config: {
|
config: {
|
||||||
...baseScanConfig,
|
...baseScanConfig,
|
||||||
// Filter triggers to selected camera only.
|
// Filter triggers to selected camera only.
|
||||||
trigger_filter_camera: 'selected' as const,
|
filter_selected_camera: true,
|
||||||
},
|
},
|
||||||
hassStates: hassActiveState,
|
hassStates: hassActiveState,
|
||||||
});
|
});
|
||||||
@@ -339,7 +357,7 @@ describe('TriggersManager', () => {
|
|||||||
config: {
|
config: {
|
||||||
...baseScanConfig,
|
...baseScanConfig,
|
||||||
// Filter triggers to selected camera only.
|
// Filter triggers to selected camera only.
|
||||||
trigger_filter_camera: 'selected' as const,
|
filter_selected_camera: true,
|
||||||
},
|
},
|
||||||
hassStates: hassActiveState,
|
hassStates: hassActiveState,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1166,6 +1166,51 @@ describe('should handle version specific upgrades', () => {
|
|||||||
type: 'custom:frigate-card',
|
type: 'custom:frigate-card',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should move and transform untrigger_reset', () => {
|
||||||
|
it('when true', () => {
|
||||||
|
const config = {
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
view: {
|
||||||
|
scan: {
|
||||||
|
untrigger_reset: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
view: {
|
||||||
|
scan: {
|
||||||
|
actions: {
|
||||||
|
untrigger: 'default',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when false', () => {
|
||||||
|
const config = {
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
view: {
|
||||||
|
scan: {
|
||||||
|
untrigger_reset: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
type: 'custom:frigate-card',
|
||||||
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
|
view: {
|
||||||
|
scan: {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -279,25 +279,14 @@ describe('config defaults', () => {
|
|||||||
default: 'live',
|
default: 'live',
|
||||||
scan: {
|
scan: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
interaction_mode: 'inactive',
|
show_trigger_status: true,
|
||||||
trigger_show_status: true,
|
|
||||||
untrigger_seconds: 0,
|
untrigger_seconds: 0,
|
||||||
trigger_action: [
|
actions: {
|
||||||
{
|
trigger: 'live',
|
||||||
action: 'fire-dom-event',
|
untrigger: 'default',
|
||||||
frigate_card_action: 'camera_select',
|
interaction_mode: 'inactive',
|
||||||
triggered: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
action: 'fire-dom-event',
|
|
||||||
frigate_card_action: 'live',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
trigger_filter_camera: 'all',
|
|
||||||
untrigger_action: {
|
|
||||||
action: 'fire-dom-event',
|
|
||||||
frigate_card_action: 'default',
|
|
||||||
},
|
},
|
||||||
|
filter_selected_camera: false,
|
||||||
},
|
},
|
||||||
timeout_seconds: 300,
|
timeout_seconds: 300,
|
||||||
update_cycle_camera: false,
|
update_cycle_camera: false,
|
||||||
|
|||||||
+4
-4
@@ -10,10 +10,10 @@ export default defineConfig({
|
|||||||
// Thresholds will automatically be updated as coverage improves to avoid
|
// Thresholds will automatically be updated as coverage improves to avoid
|
||||||
// back-sliding.
|
// back-sliding.
|
||||||
thresholdAutoUpdate: true,
|
thresholdAutoUpdate: true,
|
||||||
statements: 71.74,
|
statements: 71.82,
|
||||||
branches: 60.62,
|
branches: 60.67,
|
||||||
functions: 72.84,
|
functions: 72.86,
|
||||||
lines: 71.63,
|
lines: 71.71,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user