feat: Allow user generated notifications (#2401)

This commit is contained in:
Dermot Duffy
2026-03-07 20:47:24 -08:00
committed by GitHub
parent 1d81e03b04
commit 24c6b98948
53 changed files with 939 additions and 471 deletions
+2 -2
View File
@@ -16,8 +16,8 @@ export class InfoAction extends AdvancedCameraCardAction<GeneralActionConfig> {
const controller = new MediaDetailsController();
controller.calculate(api.getCameraManager(), item);
api.getOverlayMessageManager().setMessage(
controller.getMessage({
api.getNotificationManager().setNotification(
controller.getNotification({
hass: api.getHASSManager().getHASS() ?? undefined,
viewItemManager: api.getViewItemManager(),
viewManagerEpoch: api.getViewManager().getEpoch(),
@@ -0,0 +1,10 @@
import { NotificationActionConfig } from '../../../config/schema/actions/types';
import { CardActionsAPI } from '../../types';
import { AdvancedCameraCardAction } from './base';
export class NotificationAction extends AdvancedCameraCardAction<NotificationActionConfig> {
public async execute(api: CardActionsAPI): Promise<void> {
await super.execute(api);
api.getNotificationManager().setNotification(this._action.notification);
}
}
+3
View File
@@ -25,6 +25,7 @@ import { MoreInfoAction } from './actions/more-info';
import { MuteAction } from './actions/mute';
import { NavigateAction } from './actions/navigate';
import { NoneAction } from './actions/none';
import { NotificationAction } from './actions/notification';
import { PauseAction } from './actions/pause';
import { PerformActionAction } from './actions/perform-action';
import { PIPAction } from './actions/pip';
@@ -163,6 +164,8 @@ export class ActionFactory {
return new PTZControlsAction(context, action, options?.config);
case 'log':
return new LogAction(context, action, options?.config);
case 'notification':
return new NotificationAction(context, action, options?.config);
case 'status_bar':
return new StatusBarAction(context, action, options?.config);
case 'reload':
+8 -2
View File
@@ -28,6 +28,7 @@ export class ConfigManager {
private _overriddenConfig: AdvancedCameraCardConfig | null = null;
private _rawConfig: RawAdvancedCameraCardConfig | null = null;
private _cardWideConfig: CardWideConfig | null = null;
private _upgradeable = false;
private _overridesManager = new OverridesManager(() => this._processOverrideConfig());
constructor(api: CardConfigAPI) {
@@ -54,17 +55,22 @@ export class ConfigManager {
return this._rawConfig;
}
public isUpgradeable(): boolean {
return this._upgradeable;
}
public setConfig(inputConfig?: RawAdvancedCameraCardConfig): void {
if (!inputConfig) {
throw new Error(localize('error.invalid_configuration'));
}
const parseResult = advancedCameraCardConfigSchema.safeParse(inputConfig);
this._upgradeable = isConfigUpgradeable(inputConfig);
if (!parseResult.success) {
const configUpgradeable = isConfigUpgradeable(inputConfig);
const hint = getParseError(parseResult.error);
let upgradeMessage = '';
if (configUpgradeable) {
if (this._upgradeable) {
upgradeMessage = `${localize('error.upgrade_available')}. `;
}
throw new Error(
+6 -6
View File
@@ -31,7 +31,7 @@ import { MediaLoadedInfoManager } from './media-info-manager';
import { MediaPlayerManager } from './media-player-manager';
import { MessageManager } from './message-manager';
import { MicrophoneManager } from './microphone-manager';
import { OverlayMessageManager } from './overlay-message-manager';
import { NotificationManager } from './notification-manager';
import { PIPManager } from './pip-manager';
import { QueryStringManager } from './query-string-manager';
import { StatusBarItemManager } from './status-bar-item-manager';
@@ -58,7 +58,7 @@ import {
CardMediaPlayerAPI,
CardMessageAPI,
CardMicrophoneAPI,
CardOverlayMessageAPI,
CardNotificationAPI,
CardPIPAPI,
CardQueryStringAPI,
CardStyleAPI,
@@ -90,7 +90,7 @@ export class CardController
CardMediaPlayerAPI,
CardMessageAPI,
CardMicrophoneAPI,
CardOverlayMessageAPI,
CardNotificationAPI,
CardQueryStringAPI,
CardStyleAPI,
CardTriggersAPI,
@@ -125,7 +125,7 @@ export class CardController
private _mediaPlayerManager = new MediaPlayerManager(this);
private _messageManager = new MessageManager(this);
private _microphoneManager = new MicrophoneManager(this);
private _overlayMessageManager = new OverlayMessageManager(this);
private _notificationManager = new NotificationManager(this);
private _pipManager = new PIPManager(this);
private _queryStringManager = new QueryStringManager(this);
private _statusBarItemManager = new StatusBarItemManager(this);
@@ -252,8 +252,8 @@ export class CardController
this._microphoneManager = new MicrophoneManager(this);
}
public getOverlayMessageManager(): OverlayMessageManager {
return this._overlayMessageManager;
public getNotificationManager(): NotificationManager {
return this._notificationManager;
}
public getPIPManager(): PIPManager {
@@ -0,0 +1,33 @@
import { Notification } from '../config/schema/actions/types';
import { CardNotificationAPI } from './types';
export class NotificationManager {
private _notification: Notification | null = null;
private _api: CardNotificationAPI;
constructor(api: CardNotificationAPI) {
this._api = api;
}
public getNotification(): Notification | null {
return this._notification;
}
public hasNotification(): boolean {
return this._notification !== null;
}
// Also used to replace the current notification in-place (e.g. to refresh
// control state after a toggle action).
public setNotification(notification: Notification): void {
this._notification = notification;
this._api.getCardElementManager().update();
}
public reset(): void {
if (this._notification) {
this._notification = null;
this._api.getCardElementManager().update();
}
}
}
@@ -1,31 +0,0 @@
import { OverlayMessage } from '../types';
import { CardOverlayMessageAPI } from './types';
export class OverlayMessageManager {
private _message: OverlayMessage | null = null;
private _api: CardOverlayMessageAPI;
constructor(api: CardOverlayMessageAPI) {
this._api = api;
}
public getMessage(): OverlayMessage | null {
return this._message;
}
public hasMessage(): boolean {
return this._message !== null;
}
public setMessage(message: OverlayMessage): void {
this._message = message;
this._api.getCardElementManager().update();
}
public reset(): void {
if (this._message) {
this._message = null;
this._api.getCardElementManager().update();
}
}
}
@@ -2,7 +2,9 @@ import { isEqual } from 'lodash-es';
import { CameraManager } from '../camera-manager/manager';
import { StatusBarItem } from '../config/schema/actions/types';
import { StatusBarConfig } from '../config/schema/status-bar';
import { localize } from '../localize/localize';
import { MediaLoadedInfo } from '../types';
import { createNotificationAction } from '../utils/action';
import { View } from '../view/view';
import { CardStatusBarAPI } from './types';
@@ -42,6 +44,7 @@ export class StatusBarItemManager {
cameraManager?: CameraManager | null;
view?: View | null;
mediaLoadedInfo?: MediaLoadedInfo | null;
isUpgradeable?: boolean;
}): StatusBarItem[] {
const cameraMetadata = options?.view?.camera
? options?.cameraManager?.getCameraMetadata(options.view.camera)
@@ -124,6 +127,27 @@ export class StatusBarItemManager {
},
]
: []),
...(options?.isUpgradeable
? [
{
type: 'custom:advanced-camera-card-status-bar-icon' as const,
icon: 'mdi:update',
severity: 'medium' as const,
actions: {
tap_action: createNotificationAction({
heading: {
text: localize('notification.upgrade.heading'),
icon: 'mdi:update',
severity: 'medium',
},
text: localize('notification.upgrade.text'),
}),
},
...options?.statusConfig?.items.upgrade,
},
]
: []),
...this._dynamicItems,
];
}
+3 -3
View File
@@ -22,7 +22,7 @@ import type { MediaLoadedInfoManager } from './media-info-manager';
import type { MediaPlayerManager } from './media-player-manager';
import type { MessageManager } from './message-manager';
import type { MicrophoneManager } from './microphone-manager';
import type { OverlayMessageManager } from './overlay-message-manager';
import type { NotificationManager } from './notification-manager';
import type { PIPManager } from './pip-manager';
import type { QueryStringManager } from './query-string-manager';
import type { StatusBarItemManager } from './status-bar-item-manager';
@@ -54,7 +54,7 @@ export interface CardActionsAPI {
getMediaPlayerManager(): MediaPlayerManager;
getMessageManager(): MessageManager;
getMicrophoneManager(): MicrophoneManager;
getOverlayMessageManager(): OverlayMessageManager;
getNotificationManager(): NotificationManager;
getPIPManager(): PIPManager;
getStatusBarItemManager(): StatusBarItemManager;
getTriggersManager(): TriggersManager;
@@ -257,7 +257,7 @@ export interface CardMessageAPI {
getMediaLoadedInfoManager(): MediaLoadedInfoManager;
}
export interface CardOverlayMessageAPI {
export interface CardNotificationAPI {
getCardElementManager(): CardElementManager;
}