feat: Add UI support for PTZ presets (#1920)
- Closes #1889 Whilst a simple change theoretically, the card has such a collection of surfaces that can overlap other surfaces, it's challenging to get this to work right! There's a real chance this will have broken something z-index related (e.g. X overlaps Y when it should not), or (for related reasons) broken curver corners on the card.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { HASSDomEvent } from '@dermotduffy/custom-card-helpers';
|
||||
import { LitElement } from 'lit';
|
||||
import { orderBy } from 'lodash-es';
|
||||
import { dispatchActionExecutionRequest } from '../card-controller/actions/utils/execution-request.js';
|
||||
import { SubmenuInteraction } from '../components/submenu/types.js';
|
||||
import {
|
||||
MENU_PRIORITY_MAX,
|
||||
type ActionType,
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
type MenuConfig,
|
||||
type MenuItem,
|
||||
} from '../config/types.js';
|
||||
import { Interaction } from '../types.js';
|
||||
import {
|
||||
convertActionToCardCustomAction,
|
||||
getActionConfigGivenAction,
|
||||
@@ -86,21 +87,16 @@ export class MenuController {
|
||||
this.setExpanded(!this._expanded);
|
||||
}
|
||||
|
||||
public actionHandler(
|
||||
ev: HASSDomEvent<{ action: string; config?: ActionsConfig }>,
|
||||
config?: ActionsConfig,
|
||||
public handleAction(
|
||||
ev: CustomEvent<Interaction & Partial<SubmenuInteraction>>,
|
||||
buttonConfig?: ActionsConfig,
|
||||
): void {
|
||||
// These interactions should only be handled by the menu, as nothing
|
||||
// upstream has the user-provided configuration.
|
||||
ev.stopPropagation();
|
||||
|
||||
// If the event itself contains a configuration then use that. This is
|
||||
// useful in cases where the registration of the event handler does not have
|
||||
// access to the actual desired configuration (e.g. action events generated
|
||||
// by a submenu).
|
||||
if (ev.detail.config) {
|
||||
config = ev.detail.config;
|
||||
}
|
||||
// If the action is from a submenu, use the attached action config.
|
||||
const config: ActionsConfig | null = buttonConfig ?? ev.detail.item ?? null;
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { HASSDomEvent, HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||
import { CameraManager } from '../../camera-manager/manager';
|
||||
import { dispatchActionExecutionRequest } from '../../card-controller/actions/utils/execution-request';
|
||||
import { PTZAction } from '../../config/ptz';
|
||||
import { Actions, ActionsConfig, PTZControlsConfig } from '../../config/types';
|
||||
import { Interaction } from '../../types';
|
||||
import { createPTZMultiAction, getActionConfigGivenAction } from '../../utils/action';
|
||||
import { PTZActionNameToMultiAction, PTZActionPresence } from './types';
|
||||
import { SubmenuInteraction } from '../../components/submenu/types';
|
||||
import { PTZControllerActions } from './types';
|
||||
|
||||
export class PTZController {
|
||||
private _host: HTMLElement;
|
||||
@@ -47,9 +49,11 @@ export class PTZController {
|
||||
}
|
||||
|
||||
public handleAction(
|
||||
ev: HASSDomEvent<{ action: string }>,
|
||||
config?: ActionsConfig | null,
|
||||
ev: CustomEvent<Interaction & Partial<SubmenuInteraction>>,
|
||||
buttonConfig?: ActionsConfig | null,
|
||||
): void {
|
||||
const config: ActionsConfig | null = buttonConfig ?? ev.detail.item ?? null;
|
||||
|
||||
// Nothing else has the configuration for this action, so don't let it
|
||||
// propagate further.
|
||||
ev.stopPropagation();
|
||||
@@ -64,34 +68,6 @@ export class PTZController {
|
||||
}
|
||||
}
|
||||
|
||||
public hasUsefulAction(): PTZActionPresence {
|
||||
const allUsefulActions = {
|
||||
pt: true,
|
||||
z: true,
|
||||
home: true,
|
||||
};
|
||||
if (!this._cameraID) {
|
||||
// Will use digital PTZ.
|
||||
return allUsefulActions;
|
||||
}
|
||||
const capabilities = this._cameraManager?.getCameraCapabilities(this._cameraID);
|
||||
if (!capabilities || !capabilities.hasPTZCapability()) {
|
||||
// Will use digital PTZ.
|
||||
return allUsefulActions;
|
||||
}
|
||||
|
||||
const ptzCapabilities = capabilities.getPTZCapabilities();
|
||||
return {
|
||||
pt:
|
||||
!!ptzCapabilities?.up ||
|
||||
!!ptzCapabilities?.down ||
|
||||
!!ptzCapabilities?.left ||
|
||||
!!ptzCapabilities?.right,
|
||||
z: !!ptzCapabilities?.zoomIn || !!ptzCapabilities?.zoomOut,
|
||||
home: !!ptzCapabilities?.presets?.length,
|
||||
};
|
||||
}
|
||||
|
||||
public shouldDisplay(): boolean {
|
||||
return this._forceVisibility !== undefined
|
||||
? this._forceVisibility
|
||||
@@ -103,8 +79,15 @@ export class PTZController {
|
||||
: this._config?.mode === 'on';
|
||||
}
|
||||
|
||||
public getPTZActions(): PTZActionNameToMultiAction {
|
||||
const getDefaultActions = (options?: {
|
||||
public getPTZActions(): PTZControllerActions {
|
||||
const cameraCapabilities = this._cameraID
|
||||
? this._cameraManager?.getCameraCapabilities(this._cameraID)
|
||||
: null;
|
||||
const hasRealPTZCapability =
|
||||
cameraCapabilities && cameraCapabilities.hasPTZCapability();
|
||||
const ptzCapabilities = cameraCapabilities?.getPTZCapabilities();
|
||||
|
||||
const getContinuousActions = (options?: {
|
||||
ptzAction?: PTZAction;
|
||||
preset?: string;
|
||||
}): Actions => ({
|
||||
@@ -120,28 +103,61 @@ export class PTZController {
|
||||
}),
|
||||
});
|
||||
|
||||
const actions: PTZActionNameToMultiAction = {};
|
||||
actions.up = getDefaultActions({
|
||||
ptzAction: 'up',
|
||||
const getDiscreteAction = (options?: {
|
||||
ptzAction?: PTZAction;
|
||||
preset?: string;
|
||||
}): Actions => ({
|
||||
tap_action: createPTZMultiAction({
|
||||
ptzAction: options?.ptzAction,
|
||||
ptzPreset: options?.preset,
|
||||
}),
|
||||
});
|
||||
actions.down = getDefaultActions({
|
||||
ptzAction: 'down',
|
||||
});
|
||||
actions.left = getDefaultActions({
|
||||
ptzAction: 'left',
|
||||
});
|
||||
actions.right = getDefaultActions({
|
||||
ptzAction: 'right',
|
||||
});
|
||||
actions.zoom_in = getDefaultActions({
|
||||
ptzAction: 'zoom_in',
|
||||
});
|
||||
actions.zoom_out = getDefaultActions({
|
||||
ptzAction: 'zoom_out',
|
||||
});
|
||||
actions.home = {
|
||||
tap_action: createPTZMultiAction(),
|
||||
};
|
||||
|
||||
const actions: PTZControllerActions = {};
|
||||
if (!hasRealPTZCapability || ptzCapabilities?.up) {
|
||||
actions.up = getContinuousActions({
|
||||
ptzAction: 'up',
|
||||
});
|
||||
}
|
||||
if (!hasRealPTZCapability || ptzCapabilities?.down) {
|
||||
actions.down = getContinuousActions({
|
||||
ptzAction: 'down',
|
||||
});
|
||||
}
|
||||
if (!hasRealPTZCapability || ptzCapabilities?.left) {
|
||||
actions.left = getContinuousActions({
|
||||
ptzAction: 'left',
|
||||
});
|
||||
}
|
||||
if (!hasRealPTZCapability || ptzCapabilities?.right) {
|
||||
actions.right = getContinuousActions({
|
||||
ptzAction: 'right',
|
||||
});
|
||||
}
|
||||
if (!hasRealPTZCapability || ptzCapabilities?.zoomIn) {
|
||||
actions.zoom_in = getContinuousActions({
|
||||
ptzAction: 'zoom_in',
|
||||
});
|
||||
}
|
||||
if (!hasRealPTZCapability || ptzCapabilities?.zoomOut) {
|
||||
actions.zoom_out = getContinuousActions({
|
||||
ptzAction: 'zoom_out',
|
||||
});
|
||||
}
|
||||
if (!hasRealPTZCapability || ptzCapabilities?.presets?.length) {
|
||||
actions.home = getDiscreteAction();
|
||||
}
|
||||
for (const preset of ptzCapabilities?.presets ?? []) {
|
||||
actions.presets ??= [];
|
||||
actions.presets.push({
|
||||
preset: preset,
|
||||
actions: getDiscreteAction({
|
||||
preset: preset,
|
||||
ptzAction: 'preset',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,13 @@ declare module 'view' {
|
||||
}
|
||||
}
|
||||
|
||||
export type PTZActionNameToMultiAction = {
|
||||
[K in PTZControlAction]?: Actions;
|
||||
};
|
||||
|
||||
export interface PTZActionPresence {
|
||||
pt: boolean;
|
||||
z: boolean;
|
||||
home: boolean;
|
||||
interface PTZPresetAction {
|
||||
preset: string;
|
||||
actions: Actions;
|
||||
}
|
||||
|
||||
export type PTZControllerActions = {
|
||||
[K in PTZControlAction]?: Actions;
|
||||
} & {
|
||||
presets?: PTZPresetAction[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user