@@ -55,12 +55,13 @@ live:
|
|||||||
# [...]
|
# [...]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Default | Description |
|
| Option | Default | Description |
|
||||||
| ---------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ---------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `button_size` | `40` | The size of the call control buttons in pixels. Must be >= `20`. |
|
| `button_size` | `40` | The size of the call control buttons in pixels. Must be >= `20`. |
|
||||||
| `lock` | `true` | Whether to lock the rest of the card controls/actions while a call is in progress. Prevents an accidental tap, swipe or action mid-call. Set to `false` to allow interactions regardless of call state. |
|
| `enabled` | `true` | Whether the on-screen call controls are shown during a call. Set to `false` to hide them and drive calls from the [`menu`](./menu.md) instead. When hiding the controls, be sure to also remove `call` from `menu.auto_hide` so the menu stays visible allowing the call to be controlled. See [Driving calls from the menu](../examples.md?id=driving-calls-from-the-menu) for a complete example. |
|
||||||
| `ringtone` | | The audible chime played while an inbound call (e.g. one started by [`view.triggers.actions.trigger: call`](./view.md?id=trigger-action-configuration)) is ringing. Stops as soon as the call is answered or ended. Manual calls never ring. See [`ringtone`](#ringtone). |
|
| `lock` | `true` | Whether to lock the rest of the card controls/actions while a call is in progress. Prevents an accidental tap, swipe or action mid-call. Set to `false` to allow interactions regardless of call state. |
|
||||||
| `unanswered_timeout_seconds` | `60` | The number of seconds an inbound call may ring unanswered before it is automatically ended. The timer is cancelled the moment the call is answered. Set to `0` to disable the timeout. |
|
| `ringtone` | | The audible chime played while an inbound call (e.g. one started by [`view.triggers.actions.trigger: call`](./view.md?id=trigger-action-configuration)) is ringing. Stops as soon as the call is answered or ended. Manual calls never ring. See [`ringtone`](#ringtone). |
|
||||||
|
| `unanswered_timeout_seconds` | `60` | The number of seconds an inbound call may ring unanswered before it is automatically ended. The timer is cancelled the moment the call is answered. Set to `0` to disable the timeout. |
|
||||||
|
|
||||||
> [!NOTE] Browser autoplay restrictions may prevent the ringtone from playing
|
> [!NOTE] Browser autoplay restrictions may prevent the ringtone from playing
|
||||||
> until the page has received a user gesture (e.g. a tap or click). When that
|
> until the page has received a user gesture (e.g. a tap or click). When that
|
||||||
|
|||||||
@@ -563,6 +563,64 @@ profiles:
|
|||||||
> the view-only camera to `live_provider: ha` (HLS / receive-only WebRTC, which
|
> the view-only camera to `live_provider: ha` (HLS / receive-only WebRTC, which
|
||||||
> never offers a backchannel) instead.
|
> never offers a backchannel) instead.
|
||||||
|
|
||||||
|
### Driving calls from the menu
|
||||||
|
|
||||||
|
By default on-screen controls will appear mid-card to handle a call. Setting
|
||||||
|
[`live.controls.call.enabled: false`](configuration/live.md?id=call) hides those
|
||||||
|
controls so the call can be driven via some other mechanism. In this example,
|
||||||
|
the card is configured to allow calls to be driven from the menu instead.
|
||||||
|
|
||||||
|
This wires up the menu equivalents of every overlay control. The menu `call`
|
||||||
|
button starts, ends, and (while ringing) rejects calls, but it cannot _answer_
|
||||||
|
an inbound ring -- so a conditional answer button is added that appears only
|
||||||
|
while ringing.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
type: custom:advanced-camera-card
|
||||||
|
cameras:
|
||||||
|
- camera_entity: camera.front_door
|
||||||
|
live_provider: go2rtc
|
||||||
|
go2rtc:
|
||||||
|
modes:
|
||||||
|
- webrtc
|
||||||
|
profiles:
|
||||||
|
- doorbell
|
||||||
|
live:
|
||||||
|
controls:
|
||||||
|
call:
|
||||||
|
# Hide the on-screen call controls overlay -- the menu drives the call.
|
||||||
|
enabled: false
|
||||||
|
menu:
|
||||||
|
# The menu hides during a call by default; keep it visible so its call
|
||||||
|
# controls stay reachable.
|
||||||
|
auto_hide: []
|
||||||
|
buttons:
|
||||||
|
# Starts a call, and becomes a hang-up button for the duration of a call.
|
||||||
|
call:
|
||||||
|
enabled: true
|
||||||
|
# Mutes/unmutes your outbound microphone during a call.
|
||||||
|
microphone:
|
||||||
|
enabled: true
|
||||||
|
type: toggle
|
||||||
|
# Mutes/unmutes the inbound (caller's) audio during a call.
|
||||||
|
mute:
|
||||||
|
enabled: true
|
||||||
|
elements:
|
||||||
|
# The menu `call` button cannot answer an inbound (ringing) call, so this
|
||||||
|
# answer button is shown only while ringing to provide that control.
|
||||||
|
- type: custom:advanced-camera-card-conditional
|
||||||
|
conditions:
|
||||||
|
- condition: call
|
||||||
|
call: ringing
|
||||||
|
elements:
|
||||||
|
- type: custom:advanced-camera-card-menu-icon
|
||||||
|
icon: mdi:phone
|
||||||
|
title: Answer call
|
||||||
|
tap_action:
|
||||||
|
action: custom:advanced-camera-card-action
|
||||||
|
advanced_camera_card_action: call_answer
|
||||||
|
```
|
||||||
|
|
||||||
## Events from other cameras
|
## Events from other cameras
|
||||||
|
|
||||||
`dependencies.cameras` allows events/recordings for other cameras to be shown
|
`dependencies.cameras` allows events/recordings for other cameras to be shown
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ const getCallSchema = (): HAFormExpandableSchema => ({
|
|||||||
title: localize('config.live.controls.call.editor_label'),
|
title: localize('config.live.controls.call.editor_label'),
|
||||||
icon: 'mdi:phone',
|
icon: 'mdi:phone',
|
||||||
schema: [
|
schema: [
|
||||||
|
{ name: 'enabled', selector: { boolean: {} } },
|
||||||
{ name: 'lock', selector: { boolean: {} } },
|
{ name: 'lock', selector: { boolean: {} } },
|
||||||
{
|
{
|
||||||
name: 'ringtone',
|
name: 'ringtone',
|
||||||
|
|||||||
@@ -435,7 +435,8 @@ export class AdvancedCameraCardLiveCarousel extends LitElement {
|
|||||||
!gesturesPTZActive &&
|
!gesturesPTZActive &&
|
||||||
!this.locked;
|
!this.locked;
|
||||||
|
|
||||||
const isCallActive = this.call?.cameraID === carouselCameraID;
|
const isCallControlsActive =
|
||||||
|
this.liveConfig.controls.call.enabled && this.call?.cameraID === carouselCameraID;
|
||||||
const callMediaPlayerController =
|
const callMediaPlayerController =
|
||||||
this._mediaLoadedInfoSinkController.get()?.mediaPlayerController ?? null;
|
this._mediaLoadedInfoSinkController.get()?.mediaPlayerController ?? null;
|
||||||
|
|
||||||
@@ -470,7 +471,7 @@ export class AdvancedCameraCardLiveCarousel extends LitElement {
|
|||||||
>
|
>
|
||||||
</advanced-camera-card-ptz>
|
</advanced-camera-card-ptz>
|
||||||
<advanced-camera-card-call-controls
|
<advanced-camera-card-call-controls
|
||||||
.active=${isCallActive}
|
.active=${isCallControlsActive}
|
||||||
.answered=${this.call?.answered ?? true}
|
.answered=${this.call?.answered ?? true}
|
||||||
.microphoneState=${this.microphoneState}
|
.microphoneState=${this.microphoneState}
|
||||||
.muted=${callMediaPlayerController?.isMuted()}
|
.muted=${callMediaPlayerController?.isMuted()}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ export type RingtoneConfig = z.infer<typeof ringtoneConfigSchema>;
|
|||||||
|
|
||||||
const callConfigDefault = {
|
const callConfigDefault = {
|
||||||
button_size: 40,
|
button_size: 40,
|
||||||
|
enabled: true,
|
||||||
lock: true,
|
lock: true,
|
||||||
ringtone: { ...ringtoneConfigDefault },
|
ringtone: { ...ringtoneConfigDefault },
|
||||||
unanswered_timeout_seconds: 60,
|
unanswered_timeout_seconds: 60,
|
||||||
@@ -57,6 +58,7 @@ const callConfigDefault = {
|
|||||||
|
|
||||||
const callConfigSchema = z.object({
|
const callConfigSchema = z.object({
|
||||||
button_size: z.number().min(BUTTON_SIZE_MIN).default(callConfigDefault.button_size),
|
button_size: z.number().min(BUTTON_SIZE_MIN).default(callConfigDefault.button_size),
|
||||||
|
enabled: z.boolean().default(callConfigDefault.enabled),
|
||||||
lock: z.boolean().default(callConfigDefault.lock),
|
lock: z.boolean().default(callConfigDefault.lock),
|
||||||
ringtone: ringtoneConfigSchema.default(callConfigDefault.ringtone),
|
ringtone: ringtoneConfigSchema.default(callConfigDefault.ringtone),
|
||||||
|
|
||||||
|
|||||||
@@ -459,6 +459,7 @@
|
|||||||
"answer": "Answer call",
|
"answer": "Answer call",
|
||||||
"button_size": "Call control button size",
|
"button_size": "Call control button size",
|
||||||
"editor_label": "Two-way audio call",
|
"editor_label": "Two-way audio call",
|
||||||
|
"enabled": "Show on-screen call controls",
|
||||||
"end": "End 2-way audio call",
|
"end": "End 2-way audio call",
|
||||||
"lock": "Lock UI during an active call",
|
"lock": "Lock UI during an active call",
|
||||||
"mute_audio": "Mute audio",
|
"mute_audio": "Mute audio",
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ describe('config defaults', () => {
|
|||||||
builtin: true,
|
builtin: true,
|
||||||
call: {
|
call: {
|
||||||
button_size: 40,
|
button_size: 40,
|
||||||
|
enabled: true,
|
||||||
lock: true,
|
lock: true,
|
||||||
ringtone: { type: 'chime', repeat: 0 },
|
ringtone: { type: 'chime', repeat: 0 },
|
||||||
unanswered_timeout_seconds: 60,
|
unanswered_timeout_seconds: 60,
|
||||||
@@ -1848,6 +1849,20 @@ it('should not require title controls to specify all options', () => {
|
|||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should allow the on-screen call controls overlay to be disabled', () => {
|
||||||
|
const config = createConfig({
|
||||||
|
cameras: [{}],
|
||||||
|
live: {
|
||||||
|
controls: {
|
||||||
|
call: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(config.live.controls.call.enabled).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('should strip trailing slashes from go2rtc url', () => {
|
it('should strip trailing slashes from go2rtc url', () => {
|
||||||
const config = createConfig({
|
const config = createConfig({
|
||||||
cameras: [
|
cameras: [
|
||||||
|
|||||||
Reference in New Issue
Block a user