feat: Add UI optional UI locking when microphone is hot (#2484)
This commit is contained in:
committed by
dermotduffy
parent
9a763db1f3
commit
be7e7d79dc
@@ -70,6 +70,9 @@ export class AdvancedCameraCardLiveCarousel extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public microphoneState?: MicrophoneState;
|
||||
|
||||
@property({ attribute: false })
|
||||
public locked?: boolean;
|
||||
|
||||
@property({ attribute: false })
|
||||
public viewFilterCameraID?: string;
|
||||
|
||||
@@ -247,6 +250,7 @@ export class AdvancedCameraCardLiveCarousel extends LitElement {
|
||||
.zoomSettings=${view?.context?.zoom?.[cameraID]?.requested}
|
||||
.zoom=${!this._isGesturesPTZActive(view, cameraID)}
|
||||
.forceSelected=${isSelectedSlide}
|
||||
.locked=${this.locked}
|
||||
@advanced-camera-card:zoom:change=${(
|
||||
ev: CustomEvent<ZoomSettingsObserved>,
|
||||
) =>
|
||||
@@ -327,6 +331,7 @@ export class AdvancedCameraCardLiveCarousel extends LitElement {
|
||||
.label=${neighbor?.metadata?.title ?? ''}
|
||||
.icon=${neighbor?.metadata?.icon}
|
||||
?disabled=${!neighbor}
|
||||
?locked=${!!this.locked}
|
||||
@click=${(ev) => {
|
||||
this._setViewCameraID(neighbor?.id);
|
||||
stopEventFromActivatingCardWideActions(ev);
|
||||
@@ -360,7 +365,10 @@ export class AdvancedCameraCardLiveCarousel extends LitElement {
|
||||
: view.context?.ptzControls?.enabled;
|
||||
|
||||
const dragEnabled =
|
||||
hasMultipleCameras && this.liveConfig?.draggable && !gesturesPTZActive;
|
||||
hasMultipleCameras &&
|
||||
this.liveConfig?.draggable &&
|
||||
!gesturesPTZActive &&
|
||||
!this.locked;
|
||||
|
||||
return html`
|
||||
<advanced-camera-card-carousel
|
||||
|
||||
@@ -39,6 +39,9 @@ export class AdvancedCameraCardLiveGrid extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public microphoneState?: MicrophoneState;
|
||||
|
||||
@property({ attribute: false })
|
||||
public locked?: boolean;
|
||||
|
||||
@property({ attribute: false, hasChanged: contentsChanged })
|
||||
public triggeredCameraIDs?: Set<string>;
|
||||
|
||||
@@ -63,6 +66,7 @@ export class AdvancedCameraCardLiveGrid extends LitElement {
|
||||
.cardWideConfig=${this.cardWideConfig}
|
||||
.cameraManager=${this.cameraManager}
|
||||
.microphoneState=${this.microphoneState}
|
||||
.locked=${this.locked}
|
||||
?triggered=${triggeredCameraID &&
|
||||
!!this.triggeredCameraIDs?.has(triggeredCameraID)}
|
||||
>
|
||||
|
||||
@@ -43,6 +43,9 @@ export class AdvancedCameraCardLive extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public microphoneState?: MicrophoneState;
|
||||
|
||||
@property({ attribute: false })
|
||||
public locked?: boolean;
|
||||
|
||||
@property({ attribute: false, hasChanged: contentsChanged })
|
||||
public triggeredCameraIDs?: Set<string>;
|
||||
|
||||
@@ -93,6 +96,7 @@ export class AdvancedCameraCardLive extends LitElement {
|
||||
.cardWideConfig=${this.cardWideConfig}
|
||||
.cameraManager=${this.cameraManager}
|
||||
.microphoneState=${this.microphoneState}
|
||||
.locked=${this.locked}
|
||||
.triggeredCameraIDs=${this.triggeredCameraIDs}
|
||||
>
|
||||
</advanced-camera-card-live-grid>
|
||||
|
||||
@@ -66,6 +66,12 @@ export class AdvancedCameraCardLiveProvider extends LitElement implements MediaP
|
||||
@property({ attribute: false })
|
||||
public forceSelected = false;
|
||||
|
||||
// When true the UI lock is active and the native video controls must be
|
||||
// suppressed: those controls expose pause/play/cast/etc. directly on the
|
||||
// media element.
|
||||
@property({ attribute: false })
|
||||
public locked?: boolean;
|
||||
|
||||
private _mediaLoadedInfoSinkController = new MediaLoadedInfoSinkController(this, {
|
||||
getTargetID: () => this.targetID ?? null,
|
||||
});
|
||||
@@ -181,14 +187,17 @@ export class AdvancedCameraCardLiveProvider extends LitElement implements MediaP
|
||||
return result;
|
||||
}
|
||||
|
||||
// Builtin (native) video controls require all three conditions:
|
||||
// Builtin (native) video controls require all four conditions:
|
||||
// - controls.builtin: user config enables native controls.
|
||||
// - zoom: Whether digital zoom/panning is allowed (this will be false when a
|
||||
// 'gesture' type PTZ control is active).
|
||||
// - !_zoomed: the user has not actually digital zoomed in (when zoomed, we
|
||||
// want to hide the controls).
|
||||
// - !locked: the UI lock is not active.
|
||||
private _getEffectiveBuiltinControls(): boolean {
|
||||
return !!this.liveConfig?.controls.builtin && this.zoom && !this._zoomed;
|
||||
return (
|
||||
!!this.liveConfig?.controls.builtin && this.zoom && !this._zoomed && !this.locked
|
||||
);
|
||||
}
|
||||
|
||||
private _renderContainer(template: TemplateResult): TemplateResult {
|
||||
|
||||
+19
-6
@@ -2,12 +2,13 @@ import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { actionHandler } from '../action-handler-directive.js';
|
||||
import type { LockManagerEpoch } from '../card-controller/lock/types';
|
||||
import { MenuController } from '../components-lib/menu-controller.js';
|
||||
import { MenuItem } from '../config/schema/elements/custom/menu/types.js';
|
||||
import { MenuConfig } from '../config/schema/menu.js';
|
||||
import type { MenuItem } from '../config/schema/elements/custom/menu/types.js';
|
||||
import type { MenuConfig } from '../config/schema/menu.js';
|
||||
import { getEntityTitle } from '../ha/get-entity-title.js';
|
||||
import { EntityRegistryManager } from '../ha/registry/entity/types.js';
|
||||
import { HomeAssistant } from '../ha/types.js';
|
||||
import type { EntityRegistryManager } from '../ha/registry/entity/types.js';
|
||||
import type { HomeAssistant } from '../ha/types.js';
|
||||
import menuStyle from '../scss/menu.scss';
|
||||
import { hasAction } from '../utils/action.js';
|
||||
import './icon.js';
|
||||
@@ -24,6 +25,9 @@ export class AdvancedCameraCardMenu extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false })
|
||||
public lockManagerEpoch?: LockManagerEpoch;
|
||||
|
||||
set menuConfig(menuConfig: MenuConfig) {
|
||||
this._controller.setMenuConfig(menuConfig);
|
||||
}
|
||||
@@ -36,6 +40,12 @@ export class AdvancedCameraCardMenu extends LitElement {
|
||||
this._controller.setExpanded(expanded);
|
||||
}
|
||||
|
||||
protected willUpdate(changedProps: Map<string, unknown>): void {
|
||||
if (changedProps.has('lockManagerEpoch')) {
|
||||
this._controller.setLockManagerEpoch(this.lockManagerEpoch);
|
||||
}
|
||||
}
|
||||
|
||||
public toggleMenu(): void {
|
||||
this._controller.toggleExpanded();
|
||||
}
|
||||
@@ -49,7 +59,8 @@ export class AdvancedCameraCardMenu extends LitElement {
|
||||
return html` <advanced-camera-card-submenu-button
|
||||
.hass=${this.hass}
|
||||
.submenu=${button}
|
||||
@action=${(ev) => this._controller.handleAction(ev)}
|
||||
.lockManagerEpoch=${this.lockManagerEpoch}
|
||||
@action=${(ev) => this._controller.handleAction(ev, button)}
|
||||
>
|
||||
</advanced-camera-card-submenu-button>`;
|
||||
} else if (button.type === 'custom:advanced-camera-card-menu-submenu-select') {
|
||||
@@ -57,7 +68,8 @@ export class AdvancedCameraCardMenu extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.submenuSelect=${button}
|
||||
.entityRegistryManager=${this.entityRegistryManager}
|
||||
@action=${(ev) => this._controller.handleAction(ev)}
|
||||
.lockManagerEpoch=${this.lockManagerEpoch}
|
||||
@action=${(ev) => this._controller.handleAction(ev, button)}
|
||||
>
|
||||
</advanced-camera-card-submenu-select-button>`;
|
||||
}
|
||||
@@ -75,6 +87,7 @@ export class AdvancedCameraCardMenu extends LitElement {
|
||||
hasDoubleClick: hasAction(button.double_tap_action),
|
||||
})}
|
||||
.label=${title ?? ''}
|
||||
?disabled=${this._controller.shouldButtonBeInert(button)}
|
||||
@action=${(ev) => this._controller.handleAction(ev, button)}
|
||||
>
|
||||
<advanced-camera-card-icon
|
||||
|
||||
@@ -2,8 +2,9 @@ import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { actionHandler } from '../../action-handler-directive.js';
|
||||
import type { LockManagerEpoch } from '../../card-controller/lock/types';
|
||||
import { getEntityTitle } from '../../ha/get-entity-title.js';
|
||||
import { HomeAssistant } from '../../ha/types.js';
|
||||
import type { HomeAssistant } from '../../ha/types.js';
|
||||
import submenuStyle from '../../scss/submenu.scss';
|
||||
import {
|
||||
hasAction,
|
||||
@@ -11,7 +12,7 @@ import {
|
||||
} from '../../utils/action.js';
|
||||
import { contentsChanged } from '../../utils/basic.js';
|
||||
import '../icon.js';
|
||||
import { SubmenuInteraction, SubmenuItem } from './types.js';
|
||||
import type { SubmenuInteraction, SubmenuItem } from './types.js';
|
||||
|
||||
@customElement('advanced-camera-card-submenu')
|
||||
export class AdvancedCameraCardSubmenu extends LitElement {
|
||||
@@ -21,6 +22,9 @@ export class AdvancedCameraCardSubmenu extends LitElement {
|
||||
@property({ attribute: false, hasChanged: contentsChanged })
|
||||
public items?: SubmenuItem[];
|
||||
|
||||
@property({ attribute: false })
|
||||
public lockManagerEpoch?: LockManagerEpoch;
|
||||
|
||||
private _renderItem(item: SubmenuItem): TemplateResult | void {
|
||||
if (!this.hass) {
|
||||
return;
|
||||
@@ -28,7 +32,10 @@ export class AdvancedCameraCardSubmenu extends LitElement {
|
||||
|
||||
const title = item.title ?? getEntityTitle(this.hass, item.entity);
|
||||
const style = styleMap(item.style || {});
|
||||
const disabled = item.enabled === false;
|
||||
const inert =
|
||||
!!this.lockManagerEpoch?.locked &&
|
||||
this.lockManagerEpoch.manager.areAllActionsBlocked(item);
|
||||
const disabled = item.enabled === false || inert;
|
||||
|
||||
return html`
|
||||
<ha-dropdown-item
|
||||
|
||||
@@ -8,17 +8,19 @@ import {
|
||||
} from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { InternalIcon } from '../../config/schema/common/icon.js';
|
||||
import { MenuSubmenuSelect } from '../../config/schema/elements/custom/menu/submenu-select.js';
|
||||
import { MenuSubmenuItem } from '../../config/schema/elements/custom/menu/submenu.js';
|
||||
import { actionHandler } from '../../action-handler-directive.js';
|
||||
import type { LockManagerEpoch } from '../../card-controller/lock/types';
|
||||
import type { InternalIcon } from '../../config/schema/common/icon.js';
|
||||
import type { MenuSubmenuSelect } from '../../config/schema/elements/custom/menu/submenu-select.js';
|
||||
import type { MenuSubmenuItem } from '../../config/schema/elements/custom/menu/submenu.js';
|
||||
import { computeDomain } from '../../ha/compute-domain.js';
|
||||
import { getEntityStateTranslation } from '../../ha/entity-state-translation.js';
|
||||
import { getEntityTitle } from '../../ha/get-entity-title.js';
|
||||
import { isHassDifferent } from '../../ha/is-hass-different.js';
|
||||
import { EntityRegistryManager } from '../../ha/registry/entity/types.js';
|
||||
import { HomeAssistant } from '../../ha/types.js';
|
||||
import type { EntityRegistryManager } from '../../ha/registry/entity/types.js';
|
||||
import type { HomeAssistant } from '../../ha/types.js';
|
||||
import menuButtonStyle from '../../scss/menu-button.scss';
|
||||
import { createSelectOptionAction } from '../../utils/action.js';
|
||||
import { createSelectOptionAction, hasAction } from '../../utils/action.js';
|
||||
import '../icon.js';
|
||||
import './index.js';
|
||||
|
||||
@@ -33,6 +35,9 @@ export class AdvancedCameraCardSubmenuSelectButton extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public entityRegistryManager?: EntityRegistryManager;
|
||||
|
||||
@property({ attribute: false })
|
||||
public lockManagerEpoch?: LockManagerEpoch;
|
||||
|
||||
@state()
|
||||
private _optionTitles?: Record<string, string>;
|
||||
|
||||
@@ -129,6 +134,7 @@ export class AdvancedCameraCardSubmenuSelectButton extends LitElement {
|
||||
return html` <advanced-camera-card-submenu
|
||||
.hass=${this.hass}
|
||||
.items=${this._generatedSubmenuItems}
|
||||
.lockManagerEpoch=${this.lockManagerEpoch}
|
||||
>
|
||||
<ha-icon-button style="${style}" .label=${title || ''}>
|
||||
<advanced-camera-card-icon
|
||||
@@ -137,6 +143,11 @@ export class AdvancedCameraCardSubmenuSelectButton extends LitElement {
|
||||
title=${title || ''}
|
||||
.hass=${this.hass}
|
||||
.icon=${this._generatedIcon}
|
||||
.actionHandler=${actionHandler({
|
||||
allowPropagation: true,
|
||||
hasHold: hasAction(this.submenuSelect.hold_action),
|
||||
hasDoubleClick: hasAction(this.submenuSelect.double_tap_action),
|
||||
})}
|
||||
></advanced-camera-card-icon>
|
||||
</ha-icon-button>
|
||||
</advanced-camera-card-submenu>`;
|
||||
|
||||
@@ -2,8 +2,9 @@ import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { actionHandler } from '../../action-handler-directive.js';
|
||||
import { MenuSubmenu } from '../../config/schema/elements/custom/menu/submenu.js';
|
||||
import { HomeAssistant } from '../../ha/types.js';
|
||||
import type { LockManagerEpoch } from '../../card-controller/lock/types';
|
||||
import type { MenuSubmenu } from '../../config/schema/elements/custom/menu/submenu.js';
|
||||
import type { HomeAssistant } from '../../ha/types.js';
|
||||
import menuButtonStyle from '../../scss/menu-button.scss';
|
||||
import { hasAction } from '../../utils/action.js';
|
||||
import '../icon.js';
|
||||
@@ -17,6 +18,9 @@ export class AdvancedCameraCardSubmenuButton extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public submenu?: MenuSubmenu;
|
||||
|
||||
@property({ attribute: false })
|
||||
public lockManagerEpoch?: LockManagerEpoch;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.submenu) {
|
||||
return html``;
|
||||
@@ -26,6 +30,7 @@ export class AdvancedCameraCardSubmenuButton extends LitElement {
|
||||
return html` <advanced-camera-card-submenu
|
||||
.hass=${this.hass}
|
||||
.items=${this.submenu?.items}
|
||||
.lockManagerEpoch=${this.lockManagerEpoch}
|
||||
>
|
||||
<ha-icon-button style="${style}" .label=${this.submenu.title || ''}>
|
||||
<advanced-camera-card-icon
|
||||
|
||||
@@ -51,6 +51,9 @@ export class AdvancedCameraCardSurround extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public cardWideConfig?: CardWideConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
public locked?: boolean;
|
||||
|
||||
/**
|
||||
* Determine if a drawer is being used.
|
||||
*/
|
||||
@@ -102,6 +105,7 @@ export class AdvancedCameraCardSurround extends LitElement {
|
||||
.foldersManager=${this.foldersManager}
|
||||
.selected=${view.queryResults?.getSelectedIndex() ?? undefined}
|
||||
.cardWideConfig=${this.cardWideConfig}
|
||||
.locked=${this.locked}
|
||||
>
|
||||
</advanced-camera-card-thumbnail-carousel>`
|
||||
: ''}
|
||||
@@ -123,6 +127,7 @@ export class AdvancedCameraCardSurround extends LitElement {
|
||||
.conditionStateManager=${this.conditionStateManager}
|
||||
.viewItemManager=${this.viewItemManager}
|
||||
.cardWideConfig=${this.cardWideConfig}
|
||||
.locked=${this.locked}
|
||||
>
|
||||
</advanced-camera-card-timeline-core>`
|
||||
: ''}
|
||||
|
||||
@@ -65,6 +65,9 @@ export class AdvancedCameraCardThumbnailCarousel extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public fadeThumbnails = false;
|
||||
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public locked?: boolean;
|
||||
|
||||
private _thumbnails: TemplateResult[] = [];
|
||||
private _builder: UnifiedQueryBuilder | null = null;
|
||||
|
||||
|
||||
@@ -137,6 +137,9 @@ export class AdvancedCameraCardTimelineCore extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public itemClickAction?: TimelineItemClickAction;
|
||||
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public locked?: boolean;
|
||||
|
||||
private _refDatePicker: Ref<AdvancedCameraCardDatePicker> = createRef();
|
||||
private _refTimeline: Ref<HTMLElement> = createRef();
|
||||
private _controller: TimelineController = new TimelineController(this);
|
||||
|
||||
@@ -67,6 +67,9 @@ export class AdvancedCameraCardViews extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public microphoneState?: MicrophoneState;
|
||||
|
||||
@property({ attribute: false })
|
||||
public locked?: boolean;
|
||||
|
||||
@property({ attribute: false, hasChanged: contentsChanged })
|
||||
public triggeredCameraIDs?: Set<string>;
|
||||
|
||||
@@ -163,6 +166,7 @@ export class AdvancedCameraCardViews extends LitElement {
|
||||
.conditionStateManager=${this.conditionStateManager}
|
||||
.viewItemManager=${this.viewItemManager}
|
||||
.cardWideConfig=${this.cardWideConfig}
|
||||
.locked=${this.locked}
|
||||
>
|
||||
${!this.hide && view?.is('image')
|
||||
? html` <advanced-camera-card-image
|
||||
@@ -236,6 +240,7 @@ export class AdvancedCameraCardViews extends LitElement {
|
||||
.cardWideConfig=${this.cardWideConfig}
|
||||
.microphoneManager=${this.microphoneManager}
|
||||
.microphoneState=${this.microphoneState}
|
||||
.locked=${this.locked}
|
||||
.triggeredCameraIDs=${this.triggeredCameraIDs}
|
||||
class="${classMap(liveClasses)}"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user