feat: Allow 2-way audio detection to be skipped and timed (#2355)
- For #2313
This commit is contained in:
@@ -85,15 +85,13 @@ export class Camera {
|
||||
): Promise<Capabilities> {
|
||||
const rawCapabilities = await this._getRawCapabilities(options);
|
||||
const config = this.getConfig();
|
||||
const has2WayAudio = await liveProviderSupports2WayAudio(
|
||||
options.hass,
|
||||
config,
|
||||
this._getGo2RTCMetadataEndpoint(),
|
||||
this.getProxyConfig(),
|
||||
);
|
||||
const has2WayAudio = await this._has2WayAudioCapability(options.hass);
|
||||
|
||||
return new Capabilities(
|
||||
{ ...rawCapabilities, '2-way-audio': has2WayAudio },
|
||||
{
|
||||
...rawCapabilities,
|
||||
'2-way-audio': has2WayAudio,
|
||||
},
|
||||
{
|
||||
disable: config.capabilities?.disable,
|
||||
disableExcept: config.capabilities?.disable_except,
|
||||
@@ -101,6 +99,26 @@ export class Camera {
|
||||
);
|
||||
}
|
||||
|
||||
protected async _has2WayAudioCapability(hass: HomeAssistant): Promise<boolean> {
|
||||
if (this._config.capabilities?.disable?.includes('2-way-audio')) {
|
||||
return false;
|
||||
}
|
||||
const disableExcept = this._config.capabilities?.disable_except;
|
||||
if (disableExcept?.length && !disableExcept.includes('2-way-audio')) {
|
||||
return false;
|
||||
}
|
||||
if (this._config.capabilities?.force?.includes('2-way-audio')) {
|
||||
return true;
|
||||
}
|
||||
return await liveProviderSupports2WayAudio(
|
||||
hass,
|
||||
this.getConfig(),
|
||||
this.getConfig().go2rtc.metadata_fetch_timeout_seconds,
|
||||
this._getGo2RTCMetadataEndpoint(),
|
||||
this.getProxyConfig(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw capabilities for this camera. Subclasses should override
|
||||
* and call super._getRawCapabilities() to extend defaults.
|
||||
|
||||
@@ -6,18 +6,14 @@ import { errorToConsole } from '../../../utils/basic';
|
||||
import { CameraProxyConfig } from '../../types';
|
||||
import { Go2RTCStreamInfo, go2RTCStreamInfoSchema } from './types';
|
||||
|
||||
// Allow generous amount of time to fetch metadata (timeout matches that used in
|
||||
// the Frigate frontend for the same call).
|
||||
// See: https://github.com/dermotduffy/advanced-camera-card/issues/2313
|
||||
const GO2RTC_METADATA_TIMEOUT_SECONDS = 10;
|
||||
|
||||
const getGo2RTCStreamMetadata = async (
|
||||
hass: HomeAssistant,
|
||||
endpoint: Endpoint,
|
||||
timeoutSeconds: number,
|
||||
): Promise<Go2RTCStreamInfo | null> => {
|
||||
try {
|
||||
return await homeAssistantSignAndFetch(hass, endpoint, go2RTCStreamInfoSchema, {
|
||||
timeoutSeconds: GO2RTC_METADATA_TIMEOUT_SECONDS,
|
||||
timeoutSeconds,
|
||||
});
|
||||
} catch (e) {
|
||||
errorToConsole(e as Error);
|
||||
@@ -53,6 +49,7 @@ const streamSupports2WayAudio = (streamInfo: Go2RTCStreamInfo | null): boolean =
|
||||
*/
|
||||
export const supports2WayAudio = async (
|
||||
hass: HomeAssistant,
|
||||
metadataFetchTimeoutSeconds: number,
|
||||
go2rtcMetadataEndpoint?: Endpoint | null,
|
||||
proxyConfig?: CameraProxyConfig,
|
||||
): Promise<boolean> => {
|
||||
@@ -67,6 +64,10 @@ export const supports2WayAudio = async (
|
||||
{ context: 'live', openLimit: 1 },
|
||||
);
|
||||
|
||||
const streamInfo = await getGo2RTCStreamMetadata(hass, endpoint);
|
||||
const streamInfo = await getGo2RTCStreamMetadata(
|
||||
hass,
|
||||
endpoint,
|
||||
metadataFetchTimeoutSeconds,
|
||||
);
|
||||
return streamSupports2WayAudio(streamInfo);
|
||||
};
|
||||
|
||||
@@ -31,6 +31,11 @@ const LIVE_PROVIDERS = [
|
||||
] as const;
|
||||
export type LiveProvider = (typeof LIVE_PROVIDERS)[number];
|
||||
|
||||
const go2rtcConfigDefault = {
|
||||
// See: https://github.com/dermotduffy/advanced-camera-card/issues/2313
|
||||
metadata_fetch_timeout_seconds: 2,
|
||||
};
|
||||
|
||||
const go2rtcConfigSchema = z.object({
|
||||
url: z
|
||||
.string()
|
||||
@@ -39,6 +44,11 @@ const go2rtcConfigSchema = z.object({
|
||||
host: z.string().optional(),
|
||||
modes: z.enum(['webrtc', 'mse', 'mp4', 'mjpeg']).array().optional(),
|
||||
stream: z.string().optional(),
|
||||
metadata_fetch_timeout_seconds: z
|
||||
.number()
|
||||
.int()
|
||||
.positive()
|
||||
.default(go2rtcConfigDefault.metadata_fetch_timeout_seconds),
|
||||
});
|
||||
|
||||
const webrtcCardConfigSchema = z
|
||||
@@ -219,6 +229,7 @@ export const cameraConfigSchema = z
|
||||
.object({
|
||||
disable: z.enum(capabilityKeys).array().optional(),
|
||||
disable_except: z.enum(capabilityKeys).array().optional(),
|
||||
force: z.enum(['2-way-audio']).array().optional(),
|
||||
})
|
||||
.optional(),
|
||||
|
||||
@@ -306,7 +317,7 @@ export const cameraConfigSchema = z
|
||||
|
||||
// Live provider options.
|
||||
live_provider: z.enum(LIVE_PROVIDERS).default(cameraConfigDefault.live_provider),
|
||||
go2rtc: go2rtcConfigSchema.optional(),
|
||||
go2rtc: go2rtcConfigSchema.optional().default(go2rtcConfigDefault),
|
||||
image: imageBaseConfigSchema.optional().default(imageConfigDefault),
|
||||
jsmpeg: jsmpegConfigSchema.optional(),
|
||||
webrtc_card: webrtcCardConfigSchema.optional(),
|
||||
|
||||
@@ -20,6 +20,8 @@ export const CONF_CAMERAS_ARRAY_CAPABILITIES_DISABLE =
|
||||
`${CONF_CAMERAS}.#.capabilities.disable` as const;
|
||||
export const CONF_CAMERAS_ARRAY_CAPABILITIES_DISABLE_EXCEPT =
|
||||
`${CONF_CAMERAS}.#.capabilities.disable_except` as const;
|
||||
export const CONF_CAMERAS_ARRAY_CAPABILITIES_FORCE =
|
||||
`${CONF_CAMERAS}.#.capabilities.force` as const;
|
||||
export const CONF_CAMERAS_ARRAY_CAST_METHOD = `${CONF_CAMERAS}.#.cast.method` as const;
|
||||
export const CONF_CAMERAS_ARRAY_CAST_DASHBOARD_DASHBOARD_PATH =
|
||||
`${CONF_CAMERAS}.#.cast.dashboard.dashboard_path` as const;
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
CONF_CAMERAS_ARRAY_CAMERA_ENTITY,
|
||||
CONF_CAMERAS_ARRAY_CAPABILITIES_DISABLE,
|
||||
CONF_CAMERAS_ARRAY_CAPABILITIES_DISABLE_EXCEPT,
|
||||
CONF_CAMERAS_ARRAY_CAPABILITIES_FORCE,
|
||||
CONF_CAMERAS_ARRAY_CAST_DASHBOARD_DASHBOARD_PATH,
|
||||
CONF_CAMERAS_ARRAY_CAST_DASHBOARD_VIEW_PATH,
|
||||
CONF_CAMERAS_ARRAY_CAST_METHOD,
|
||||
@@ -946,6 +947,14 @@ export class AdvancedCameraCardEditor extends LitElement implements LovelaceCard
|
||||
},
|
||||
];
|
||||
|
||||
protected _forceableCapabilities: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{
|
||||
value: '2-way-audio',
|
||||
label: localize('config.cameras.capabilities.capabilities.2-way-audio'),
|
||||
},
|
||||
];
|
||||
|
||||
protected _defaultResetInteractionModes: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{
|
||||
@@ -2703,6 +2712,16 @@ export class AdvancedCameraCardEditor extends LitElement implements LovelaceCard
|
||||
multiple: true,
|
||||
},
|
||||
)}
|
||||
${this._renderOptionSelector(
|
||||
getArrayConfigPath(
|
||||
CONF_CAMERAS_ARRAY_CAPABILITIES_FORCE,
|
||||
cameraIndex,
|
||||
),
|
||||
this._forceableCapabilities,
|
||||
{
|
||||
multiple: true,
|
||||
},
|
||||
)}
|
||||
`,
|
||||
)}
|
||||
${this._putInSubmenu(
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"camera_entity": "Camera Entity",
|
||||
"capabilities": {
|
||||
"capabilities": {
|
||||
"2-way-audio": "2-way Audio",
|
||||
"clips": "Clips",
|
||||
"favorite-events": "Favorite Events",
|
||||
"favorite-recordings": "Favorite Recordings",
|
||||
@@ -43,6 +44,7 @@
|
||||
},
|
||||
"disable": "Disable",
|
||||
"disable_except": "Disable except",
|
||||
"force": "Force",
|
||||
"editor_label": "Camera capabilities"
|
||||
},
|
||||
"cast": {
|
||||
|
||||
@@ -25,6 +25,7 @@ export const getResolvedLiveProvider = (
|
||||
export const liveProviderSupports2WayAudio = async (
|
||||
hass: HomeAssistant,
|
||||
config: CameraConfig,
|
||||
metadataFetchTimeoutSeconds: number,
|
||||
go2rtcMetadataEndpoint?: Endpoint | null,
|
||||
proxyConfig?: CameraProxyConfig,
|
||||
): Promise<boolean> => {
|
||||
@@ -32,5 +33,10 @@ export const liveProviderSupports2WayAudio = async (
|
||||
return false;
|
||||
}
|
||||
|
||||
return gortcSupports2WayAudio(hass, go2rtcMetadataEndpoint, proxyConfig);
|
||||
return gortcSupports2WayAudio(
|
||||
hass,
|
||||
metadataFetchTimeoutSeconds,
|
||||
go2rtcMetadataEndpoint,
|
||||
proxyConfig,
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user