Add new method of casting live cameras based on dashboards.

This commit is contained in:
Dermot Duffy
2023-10-09 22:04:59 -07:00
parent aa9d061331
commit bc71709cc8
10 changed files with 355 additions and 62 deletions
+49 -3
View File
@@ -1,9 +1,11 @@
import { CameraConfig } from '../config/types';
import { MEDIA_PLAYER_SUPPORT_BROWSE_MEDIA } from '../const';
import { ViewMedia } from '../view/media';
import { ViewMediaClassifier } from '../view/media-classifier';
import { localize } from '../localize/localize';
import { errorToConsole } from '../utils/basic';
import { Entity } from '../utils/ha/entity-registry/types';
import { supportsFeature } from '../utils/ha/update';
import { ViewMedia } from '../view/media';
import { ViewMediaClassifier } from '../view/media-classifier';
import { CardMediaPlayerAPI } from './types';
export class MediaPlayerManager {
@@ -76,11 +78,27 @@ export class MediaPlayerManager {
}
public async playLive(mediaPlayer: string, cameraID: string): Promise<void> {
const hass = this._api.getHASSManager().getHASS();
const cameraConfig = this._api
.getCameraManager()
.getStore()
.getCameraConfig(cameraID);
if (!cameraConfig) {
return;
}
if (cameraConfig.cast?.method === 'dashboard') {
await this._playLiveDashboard(mediaPlayer, cameraConfig);
} else {
await this._playLiveStandard(mediaPlayer, cameraID, cameraConfig);
}
}
protected async _playLiveStandard(
mediaPlayer: string,
cameraID: string,
cameraConfig: CameraConfig,
): Promise<void> {
const hass = this._api.getHASSManager().getHASS();
const cameraEntity = cameraConfig?.camera_entity ?? null;
if (!hass || !cameraEntity) {
@@ -102,6 +120,34 @@ export class MediaPlayerManager {
});
}
protected async _playLiveDashboard(
mediaPlayer: string,
cameraConfig: CameraConfig,
): Promise<void> {
const hass = this._api.getHASSManager().getHASS();
if (!hass) {
return;
}
const dashboardConfig = cameraConfig.cast?.dashboard;
if (!dashboardConfig?.dashboard_path || !dashboardConfig?.view_path) {
this._api.getMessageManager().setMessageIfHigherPriority({
type: 'error',
icon: 'mdi:cast',
message: localize('error.no_dashboard_or_view'),
});
return;
}
// When this bug is closed, a query string could be included:
// https://github.com/home-assistant/core/issues/98316
await hass.callService('cast', 'show_lovelace_view', {
entity_id: mediaPlayer,
dashboard_path: dashboardConfig.dashboard_path,
view_path: dashboardConfig.view_path,
});
}
public async playMedia(mediaPlayer: string, media?: ViewMedia | null): Promise<void> {
const hass = this._api.getHASSManager().getHASS();
+39 -2
View File
@@ -23,16 +23,53 @@ export class QueryStringManager {
public executeNonViewRelated = (): void => {
this._executeNonViewRelated(this._calculateIntent());
}
};
public executeViewRelated = (): void => {
this._executeViewRelated(this._calculateIntent());
}
};
public executeAll = (): void => {
const intent = this._calculateIntent();
this._executeViewRelated(intent);
this._executeNonViewRelated(intent);
};
public generateQueryString(action: FrigateCardCustomAction): string | null {
const baseKey =
'frigate-card-action.' + (action.card_id ? `${action.card_id}.` : '');
switch (action.frigate_card_action) {
case 'camera_select':
case 'live_substream_select':
return new URLSearchParams([
[baseKey + action.frigate_card_action, action.camera],
]).toString();
case 'camera_ui':
case 'clip':
case 'clips':
case 'default':
case 'diagnostics':
case 'download':
case 'expand':
case 'image':
case 'live':
case 'menu_toggle':
case 'recording':
case 'recordings':
case 'snapshot':
case 'snapshots':
case 'timeline':
return new URLSearchParams([
[baseKey + action.frigate_card_action, ''],
]).toString();
default:
console.warn(
`Frigate card cannot convert unsupported action to query string:`,
action,
);
}
return null;
}
protected _executeViewRelated(intent: QueryStringViewIntent): void {
+2
View File
@@ -163,6 +163,8 @@ export interface CardMediaPlayerAPI {
getHASSManager(): HASSManager;
getCameraManager(): CameraManager;
getEntityRegistryManager(): EntityRegistryManager;
getMessageManager(): MessageManager;
getQueryStringManager(): QueryStringManager;
}
export interface CardMessageAPI {
+26
View File
@@ -32,6 +32,8 @@ export const FRIGATE_CARD_VIEWS_USER_SPECIFIED = [
'image',
'timeline',
] as const;
export type FrigateCardUserSpecifiedView =
(typeof FRIGATE_CARD_VIEWS_USER_SPECIFIED)[number];
const FRIGATE_CARD_VIEWS = [
...FRIGATE_CARD_VIEWS_USER_SPECIFIED,
@@ -213,6 +215,7 @@ const FRIGATE_CARD_GENERAL_ACTIONS = [
'screenshot',
'unmute',
] as const;
export type FrigateCardGeneralAction = (typeof FRIGATE_CARD_GENERAL_ACTIONS)[number];
const FRIGATE_CARD_ACTIONS = [
...FRIGATE_CARD_VIEWS_USER_SPECIFIED,
@@ -237,6 +240,9 @@ const frigateCardCameraSelectActionSchema = frigateCardCustomActionsBaseSchema.e
frigate_card_action: z.literal('camera_select'),
camera: z.string(),
});
export type FrigateCardCameraSelectAction = z.infer<
typeof frigateCardCameraSelectActionSchema
>;
const frigateCardLiveDependencySelectActionSchema =
frigateCardCustomActionsBaseSchema.extend({
@@ -920,6 +926,24 @@ const liveOverridesSchema = z
.optional();
export type LiveOverrides = z.infer<typeof liveOverridesSchema>;
// *************************************************************************
// Cast Configuration
// *************************************************************************
const castConfigDefault = {
method: 'standard' as const,
};
export const castSchema = z.object({
method: z.enum(['standard', 'dashboard']).default(castConfigDefault.method).optional(),
dashboard: z
.object({
dashboard_path: z.string().optional(),
view_path: z.string().optional(),
})
.optional(),
});
// *************************************************************************
// Camera Configuration
// *************************************************************************
@@ -1035,6 +1059,8 @@ export const cameraConfigSchema = z
image: liveImageConfigSchema.default(cameraConfigDefault.image),
jsmpeg: jsmpegConfigSchema.optional(),
webrtc_card: webrtcCardConfigSchema.optional(),
cast: castSchema.optional(),
})
.default(cameraConfigDefault);
export type CameraConfig = z.infer<typeof cameraConfigSchema>;
+1
View File
@@ -444,6 +444,7 @@
"no_camera_entity_for_triggers": "A camera entity is required in order to autodetect triggers",
"no_camera_id": "Could not determine camera id for the following camera, may need to set 'id' parameter manually",
"no_camera_name": "Could not determine a Frigate camera name for camera (or one of its dependents), please specify either 'camera_entity' or 'camera_name'",
"no_dashboard_or_view": "Both 'dashboard_path' and 'view_path' parameters are required for the 'dashboard' cast method",
"no_live_camera": "The camera_entity parameter must be set and valid for this live provider",
"no_visible_cameras": "No visible cameras found, you must configure at least one non-hidden camera",
"reconnecting": "Reconnecting",
+1
View File
@@ -436,6 +436,7 @@
"no_camera_entity_for_triggers": "È necessaria un'entità telecamera per rilevare automaticamente i trigger",
"no_camera_id": "Impossibile determinare l'ID della telecamera , potrebbe essere necessario impostare manualmente il parametro 'ID'",
"no_camera_name": "Impossibile determinare un nome della telecamera in Frigate, si prega di specificare 'camera_enty' o 'camera_name'",
"no_dashboard_or_view": "",
"no_live_camera": "Il parametro fotocamera_enty deve essere impostato e valido per questo provider live",
"no_visible_cameras": "Nessuna telecamera visibile trovata, è necessario configurare almeno una telecamera non nascosta",
"reconnecting": "Riconnessione",
+1
View File
@@ -443,6 +443,7 @@
"no_camera_entity_for_triggers": "Uma entidade de câmera é necessária para detectar automaticamente os gatilhos",
"no_camera_id": "Não foi possível determinar o ID da câmera para a câmera a seguir, pode ser necessário definir o parâmetro 'id' manualmente",
"no_camera_name": "Não foi possível determinar o nome da câmera da Frigate, especifique 'camera_entity' ou 'camera_name' para a câmera a seguir",
"no_dashboard_or_view": "",
"no_live_camera": "O parâmetro camera_entity deve ser definido e válido para este provedor ativo",
"no_visible_cameras": "Nenhuma câmera visível encontrada, você deve configurar pelo menos uma câmera não oculta",
"reconnecting": "Reconectando",
+1
View File
@@ -429,6 +429,7 @@
"no_camera_entity_for_triggers": "Não existe camera para a acção",
"no_camera_id": "Não foi possível determinar o ID da câmera para a câmera a seguir, pode ser necessário definir o parâmetro 'id' manualmente",
"no_camera_name": "Não foi possível determinar o nome da câmera da Frigate, especifique 'camera_entity' ou 'camera_name' para a câmera a seguir",
"no_dashboard_or_view": "",
"no_live_camera": "O parâmetro camera_entity deve ser definido e válido para este serviço ativo",
"no_visible_cameras": "Sem camaras visiveis",
"reconnecting": "A voltar a ligar",