feat: Add 'doorbell' configuration profile (#2511)
This commit is contained in:
committed by
dermotduffy
parent
ef9c9c8d5c
commit
406176eebc
@@ -16,11 +16,12 @@ profiles:
|
|||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Profiles are applied top to bottom. If multiple profiles change a configuration default, then the last one "wins"
|
> Profiles are applied top to bottom. If multiple profiles change a configuration default, then the last one "wins"
|
||||||
|
|
||||||
| Profile name | Purpose |
|
| Profile name | Purpose |
|
||||||
| ----------------- | ---------------------------------------------- |
|
| ----------------- | ------------------------------------------------------------ |
|
||||||
| `casting` | Configure the card to be casted. |
|
| `casting` | Configure the card to be casted. |
|
||||||
| `low-performance` | Configure the card for lower end devices. |
|
| `doorbell` | Configure the card to ring like a phone on a doorbell press. |
|
||||||
| `scrubbing` | Configure the card to allow "video scrubbing". |
|
| `low-performance` | Configure the card for lower end devices. |
|
||||||
|
| `scrubbing` | Configure the card to allow "video scrubbing". |
|
||||||
|
|
||||||
## `casting`
|
## `casting`
|
||||||
|
|
||||||
@@ -32,6 +33,27 @@ profiles:
|
|||||||
- casting
|
- casting
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `doorbell`
|
||||||
|
|
||||||
|
Turns the card into a phone-style ringer that answers a [two-way audio
|
||||||
|
call](../usage/2-way-audio.md) when somebody presses a doorbell. Intended for a
|
||||||
|
wall-mounted tablet sitting on a dashboard with a doorbell camera.
|
||||||
|
|
||||||
|
Minimal configuration — just point it at a doorbell-capable camera:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
type: custom:advanced-camera-card
|
||||||
|
cameras:
|
||||||
|
- camera_entity: camera.front_door
|
||||||
|
profiles:
|
||||||
|
- doorbell
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> For inbound audio to actually work the camera still needs a live provider that supports two-way audio (e.g. `go2rtc` with `webrtc` mode). The profile does not change `live_provider` since that's setup-specific.
|
||||||
|
|
||||||
|
See the [source code](https://github.com/dermotduffy/advanced-camera-card/blob/main/src/config/profiles/doorbell.ts) for an exhaustive list of options set by this profile.
|
||||||
|
|
||||||
## `low-performance`
|
## `low-performance`
|
||||||
|
|
||||||
For low end devices, the `low-performance` profile will adjust card defaults to attempt to increase performance.
|
For low end devices, the `low-performance` profile will adjust card defaults to attempt to increase performance.
|
||||||
@@ -56,6 +78,7 @@ See the [source code](https://github.com/dermotduffy/advanced-camera-card/blob/m
|
|||||||
```yaml
|
```yaml
|
||||||
profiles:
|
profiles:
|
||||||
- casting
|
- casting
|
||||||
|
- doorbell
|
||||||
- low-performance
|
- low-performance
|
||||||
- scrubbing
|
- scrubbing
|
||||||
```
|
```
|
||||||
|
|||||||
+7
-27
@@ -361,46 +361,26 @@ elements:
|
|||||||
|
|
||||||
### Inbound call on doorbell press
|
### Inbound call on doorbell press
|
||||||
|
|
||||||
This example uses [`view.triggers.actions.trigger: call`](configuration/view.md?id=triggers) to turn a dashboard into a phone-like ringer when somebody presses the doorbell. The intended deployment is a wall-mounted tablet sitting on the dashboard.
|
The [`doorbell` profile](configuration/profiles.md?id=doorbell) turns a dashboard into a phone-like ringer when somebody presses the doorbell, by setting [`view.triggers.actions.trigger: call`](configuration/view.md?id=triggers) and auto-discovering [HA `event.*` entities](https://www.home-assistant.io/integrations/event/#device-class) with `device_class: doorbell` on the camera's device (Ring, UniFi Protect, Nest, DoorBird, Reolink, etc.). The intended deployment is a wall-mounted tablet sitting on the dashboard.
|
||||||
|
|
||||||
Setting `triggers.doorbell: true` opts the camera into auto-discovery of [HA
|
|
||||||
`event.*`
|
|
||||||
entities](https://www.home-assistant.io/integrations/event/#device-class) with
|
|
||||||
`device_class: doorbell` on the camera's device — the officially supported way
|
|
||||||
modern integrations (Ring, UniFi Protect, Nest, DoorBird, Reolink, etc.) expose
|
|
||||||
a doorbell press -- no need to list the entity explicitly under
|
|
||||||
`triggers.entities`. If your doorbell exposes a `binary_sensor.*` or `switch.*`
|
|
||||||
instead, list it under `triggers.entities` manually.
|
|
||||||
|
|
||||||
A doorbell press is instantaneous, so the card synthesises a ring window from [`view.triggers.signal_hold_seconds`](configuration/view.md?id=triggers) (default `30`s) — long enough for a typical phone-style answer window. `untrigger_delay_seconds` then lingers past that, same as for any stateful trigger.
|
A doorbell press is instantaneous, so the card synthesises a ring window from [`view.triggers.signal_hold_seconds`](configuration/view.md?id=triggers) (default `30`s) — long enough for a typical phone-style answer window. `untrigger_delay_seconds` then lingers past that, same as for any stateful trigger.
|
||||||
|
|
||||||
`triggers.motion`, `triggers.occupancy`, and `triggers.events` are off by default — only the explicit doorbell press triggers the call, so casual motion won't make the card ring.
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
type: custom:advanced-camera-card
|
type: custom:advanced-camera-card
|
||||||
cameras:
|
cameras:
|
||||||
- camera_entity: camera.front_door
|
- camera_entity: camera.front_door
|
||||||
|
# `go2rtc` + `webrtc` is needed for two-way audio.
|
||||||
live_provider: go2rtc
|
live_provider: go2rtc
|
||||||
go2rtc:
|
go2rtc:
|
||||||
modes:
|
modes:
|
||||||
- webrtc
|
- webrtc
|
||||||
triggers:
|
profiles:
|
||||||
doorbell: true
|
- doorbell
|
||||||
view:
|
|
||||||
default: live
|
|
||||||
triggers:
|
|
||||||
show_trigger_status: true
|
|
||||||
actions:
|
|
||||||
# Call when triggered.
|
|
||||||
trigger: call
|
|
||||||
# When the trigger naturally ends (after signal_hold_seconds): end
|
|
||||||
# the call if it's still ringing. An answered call survives this and
|
|
||||||
# must be ended manually.
|
|
||||||
untrigger: call
|
|
||||||
# Ring even when somebody is actively using the card.
|
|
||||||
interaction_mode: all
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> If your doorbell exposes a `binary_sensor.*` or `switch.*` instead of an `event.*` entity, list it under [`triggers.entities`](configuration/cameras/README.md?id=triggers) on the camera manually. Auto-discovery only covers `event.*` based doorbell entities.
|
||||||
|
|
||||||
## 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
|
||||||
|
|||||||
@@ -229,7 +229,9 @@ export class CameraManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hasAutoTriggers = (config: CameraConfig): boolean => {
|
const hasAutoTriggers = (config: CameraConfig): boolean => {
|
||||||
return config.triggers.motion || config.triggers.occupancy;
|
return (
|
||||||
|
config.triggers.motion || config.triggers.occupancy || config.triggers.doorbell
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -30,9 +30,6 @@ export const CASTING_PROFILE = {
|
|||||||
[CONF_DIMENSIONS_ASPECT_RATIO_MODE]: 'static',
|
[CONF_DIMENSIONS_ASPECT_RATIO_MODE]: 'static',
|
||||||
[CONF_DIMENSIONS_ASPECT_RATIO]: '16:9',
|
[CONF_DIMENSIONS_ASPECT_RATIO]: '16:9',
|
||||||
|
|
||||||
// These values are defaults anyway unless another profile (e.g.
|
|
||||||
// low-performance) is also selected, but at pretty important to a good
|
|
||||||
// experience so are reset here.
|
|
||||||
[CONF_CAMERAS_GLOBAL_IMAGE_REFRESH_SECONDS]: 1,
|
[CONF_CAMERAS_GLOBAL_IMAGE_REFRESH_SECONDS]: 1,
|
||||||
[CONF_LIVE_SHOW_IMAGE_DURING_LOAD]: true,
|
[CONF_LIVE_SHOW_IMAGE_DURING_LOAD]: true,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import {
|
||||||
|
CONF_CAMERAS_GLOBAL_TRIGGERS_DOORBELL,
|
||||||
|
CONF_VIEW_TRIGGERS_ACTIONS_INTERACTION_MODE,
|
||||||
|
CONF_VIEW_TRIGGERS_ACTIONS_TRIGGER,
|
||||||
|
CONF_VIEW_TRIGGERS_ACTIONS_UNTRIGGER,
|
||||||
|
CONF_VIEW_TRIGGERS_SHOW_TRIGGER_STATUS,
|
||||||
|
} from '../../const.js';
|
||||||
|
|
||||||
|
export const DOORBELL_PROFILE = {
|
||||||
|
// Auto-discover HA `event.*` entities with `device_class: doorbell` on each
|
||||||
|
// camera's device (Ring, UniFi Protect, Nest, DoorBird, Reolink, ...).
|
||||||
|
[CONF_CAMERAS_GLOBAL_TRIGGERS_DOORBELL]: true,
|
||||||
|
|
||||||
|
// Pulse the camera border while the doorbell is ringing.
|
||||||
|
[CONF_VIEW_TRIGGERS_SHOW_TRIGGER_STATUS]: true,
|
||||||
|
|
||||||
|
// Triggers cause calls to start and end.
|
||||||
|
[CONF_VIEW_TRIGGERS_ACTIONS_TRIGGER]: 'call' as const,
|
||||||
|
[CONF_VIEW_TRIGGERS_ACTIONS_UNTRIGGER]: 'call' as const,
|
||||||
|
|
||||||
|
// Ring even while the user is actively touching the dashboard.
|
||||||
|
[CONF_VIEW_TRIGGERS_ACTIONS_INTERACTION_MODE]: 'all' as const,
|
||||||
|
};
|
||||||
@@ -1,31 +1,24 @@
|
|||||||
import {
|
import {
|
||||||
CONF_CAMERAS_GLOBAL_IMAGE_REFRESH_SECONDS,
|
CONF_CAMERAS_GLOBAL_IMAGE_REFRESH_SECONDS,
|
||||||
CONF_CAMERAS_GLOBAL_LIVE_PROVIDER,
|
CONF_CAMERAS_GLOBAL_LIVE_PROVIDER,
|
||||||
CONF_CAMERAS_GLOBAL_TRIGGERS_OCCUPANCY,
|
|
||||||
CONF_LIVE_AUTO_MUTE,
|
CONF_LIVE_AUTO_MUTE,
|
||||||
CONF_LIVE_CONTROLS_THUMBNAILS_MODE,
|
CONF_LIVE_CONTROLS_THUMBNAILS_MODE,
|
||||||
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DETAILS,
|
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DETAILS,
|
||||||
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DOWNLOAD_CONTROL,
|
|
||||||
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
|
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
|
||||||
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
|
|
||||||
CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
||||||
CONF_LIVE_DRAGGABLE,
|
CONF_LIVE_DRAGGABLE,
|
||||||
CONF_LIVE_LAZY_UNLOAD,
|
CONF_LIVE_LAZY_UNLOAD,
|
||||||
CONF_LIVE_SHOW_IMAGE_DURING_LOAD,
|
CONF_LIVE_SHOW_IMAGE_DURING_LOAD,
|
||||||
CONF_LIVE_TRANSITION_EFFECT,
|
CONF_LIVE_TRANSITION_EFFECT,
|
||||||
CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS,
|
CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS,
|
||||||
CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_DOWNLOAD_CONTROL,
|
|
||||||
CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
|
CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
|
||||||
CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
|
|
||||||
CONF_MEDIA_VIEWER_AUTO_MUTE,
|
CONF_MEDIA_VIEWER_AUTO_MUTE,
|
||||||
CONF_MEDIA_VIEWER_AUTO_PAUSE,
|
CONF_MEDIA_VIEWER_AUTO_PAUSE,
|
||||||
CONF_MEDIA_VIEWER_AUTO_PLAY,
|
CONF_MEDIA_VIEWER_AUTO_PLAY,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE,
|
CONF_MEDIA_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_MODE,
|
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_MODE,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_DETAILS,
|
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_DETAILS,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_DOWNLOAD_CONTROL,
|
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
|
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
|
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
||||||
CONF_MEDIA_VIEWER_DRAGGABLE,
|
CONF_MEDIA_VIEWER_DRAGGABLE,
|
||||||
CONF_MEDIA_VIEWER_SNAPSHOT_CLICK_PLAYS_CLIP,
|
CONF_MEDIA_VIEWER_SNAPSHOT_CLICK_PLAYS_CLIP,
|
||||||
@@ -44,9 +37,7 @@ import {
|
|||||||
CONF_STATUS_BAR_STYLE,
|
CONF_STATUS_BAR_STYLE,
|
||||||
CONF_TIMELINE_CONTROLS_THUMBNAILS_MODE,
|
CONF_TIMELINE_CONTROLS_THUMBNAILS_MODE,
|
||||||
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_DETAILS,
|
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_DETAILS,
|
||||||
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_DOWNLOAD_CONTROL,
|
|
||||||
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
|
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
|
||||||
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
|
|
||||||
CONF_TIMELINE_SHOW_RECORDINGS,
|
CONF_TIMELINE_SHOW_RECORDINGS,
|
||||||
CONF_VIEW_TRIGGERS_ACTIONS_TRIGGER,
|
CONF_VIEW_TRIGGERS_ACTIONS_TRIGGER,
|
||||||
} from '../../const.js';
|
} from '../../const.js';
|
||||||
@@ -94,28 +85,19 @@ export const LOW_PERFORMANCE_PROFILE = {
|
|||||||
// Hide several buttons that are otherwise visible by default.
|
// Hide several buttons that are otherwise visible by default.
|
||||||
[`${CONF_MENU_BUTTONS_IRIS}.enabled`]: false,
|
[`${CONF_MENU_BUTTONS_IRIS}.enabled`]: false,
|
||||||
[`${CONF_MENU_BUTTONS_TIMELINE}.enabled`]: false,
|
[`${CONF_MENU_BUTTONS_TIMELINE}.enabled`]: false,
|
||||||
[`${CONF_MENU_BUTTONS_TIMELINE}.enabled`]: false,
|
|
||||||
|
|
||||||
// If the media player button is present media player entity fetches are
|
// If the media player button is present media player entity fetches are
|
||||||
// required on initialization.
|
// required on initialization.
|
||||||
[`${CONF_MENU_BUTTONS_MEDIA_PLAYER}.enabled`]: false,
|
[`${CONF_MENU_BUTTONS_MEDIA_PLAYER}.enabled`]: false,
|
||||||
|
|
||||||
// Disable all options in thumbnails.
|
// Disable thumbnail controls that are otherwise on by default.
|
||||||
[CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_DOWNLOAD_CONTROL]: false,
|
|
||||||
[CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL]: false,
|
[CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL]: false,
|
||||||
[CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL]: false,
|
|
||||||
[CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS]: false,
|
[CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS]: false,
|
||||||
[CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DOWNLOAD_CONTROL]: false,
|
|
||||||
[CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL]: false,
|
[CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL]: false,
|
||||||
[CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL]: false,
|
|
||||||
[CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DETAILS]: false,
|
[CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DETAILS]: false,
|
||||||
[CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_DOWNLOAD_CONTROL]: false,
|
|
||||||
[CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL]: false,
|
[CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL]: false,
|
||||||
[CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL]: false,
|
|
||||||
[CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_DETAILS]: false,
|
[CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_DETAILS]: false,
|
||||||
[CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_DOWNLOAD_CONTROL]: false,
|
|
||||||
[CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL]: false,
|
[CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL]: false,
|
||||||
[CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL]: false,
|
|
||||||
[CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_DETAILS]: false,
|
[CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_DETAILS]: false,
|
||||||
|
|
||||||
// Disable all optional performance related features.
|
// Disable all optional performance related features.
|
||||||
@@ -133,7 +115,6 @@ export const LOW_PERFORMANCE_PROFILE = {
|
|||||||
// Clicking on a snapshot should not play a clip.
|
// Clicking on a snapshot should not play a clip.
|
||||||
[CONF_MEDIA_VIEWER_SNAPSHOT_CLICK_PLAYS_CLIP]: false,
|
[CONF_MEDIA_VIEWER_SNAPSHOT_CLICK_PLAYS_CLIP]: false,
|
||||||
|
|
||||||
[CONF_CAMERAS_GLOBAL_TRIGGERS_OCCUPANCY]: false,
|
|
||||||
[CONF_CAMERAS_GLOBAL_LIVE_PROVIDER]: 'image',
|
[CONF_CAMERAS_GLOBAL_LIVE_PROVIDER]: 'image',
|
||||||
|
|
||||||
// Refresh the live camera image every 10 seconds (same as stock Home
|
// Refresh the live camera image every 10 seconds (same as stock Home
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import { ProfileType } from '../schema/profiles.js';
|
|||||||
import { advancedCameraCardConfigSchema } from '../schema/types.js';
|
import { advancedCameraCardConfigSchema } from '../schema/types.js';
|
||||||
import { RawAdvancedCameraCardConfig } from '../types.js';
|
import { RawAdvancedCameraCardConfig } from '../types.js';
|
||||||
import { CASTING_PROFILE } from './casting.js';
|
import { CASTING_PROFILE } from './casting.js';
|
||||||
|
import { DOORBELL_PROFILE } from './doorbell.js';
|
||||||
import { LOW_PERFORMANCE_PROFILE } from './low-performance.js';
|
import { LOW_PERFORMANCE_PROFILE } from './low-performance.js';
|
||||||
import { SCRUBBING_PROFILE } from './scrubbing.js';
|
import { SCRUBBING_PROFILE } from './scrubbing.js';
|
||||||
|
|
||||||
const PROFILES = {
|
const PROFILES = {
|
||||||
casting: CASTING_PROFILE,
|
casting: CASTING_PROFILE,
|
||||||
|
doorbell: DOORBELL_PROFILE,
|
||||||
'low-performance': LOW_PERFORMANCE_PROFILE,
|
'low-performance': LOW_PERFORMANCE_PROFILE,
|
||||||
scrubbing: SCRUBBING_PROFILE,
|
scrubbing: SCRUBBING_PROFILE,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
const PROFILES = ['casting', 'low-performance', 'scrubbing'] as const;
|
const PROFILES = ['casting', 'doorbell', 'low-performance', 'scrubbing'] as const;
|
||||||
export type ProfileType = (typeof PROFILES)[number];
|
export type ProfileType = (typeof PROFILES)[number];
|
||||||
export const profilesSchema = z.enum(PROFILES).array().optional();
|
export const profilesSchema = z.enum(PROFILES).array().optional();
|
||||||
|
|||||||
+2
-2
@@ -132,8 +132,8 @@ export const CONF_CAMERAS_ARRAY_MEDIA_FOLDERS =
|
|||||||
const CONF_CAMERAS_GLOBAL = 'cameras_global' as const;
|
const CONF_CAMERAS_GLOBAL = 'cameras_global' as const;
|
||||||
export const CONF_CAMERAS_GLOBAL_LIVE_PROVIDER =
|
export const CONF_CAMERAS_GLOBAL_LIVE_PROVIDER =
|
||||||
`${CONF_CAMERAS_GLOBAL}.live_provider` as const;
|
`${CONF_CAMERAS_GLOBAL}.live_provider` as const;
|
||||||
export const CONF_CAMERAS_GLOBAL_TRIGGERS_OCCUPANCY =
|
export const CONF_CAMERAS_GLOBAL_TRIGGERS_DOORBELL =
|
||||||
`${CONF_CAMERAS_GLOBAL}.triggers.occupancy` as const;
|
`${CONF_CAMERAS_GLOBAL}.triggers.doorbell` as const;
|
||||||
export const CONF_CAMERAS_GLOBAL_IMAGE_REFRESH_SECONDS =
|
export const CONF_CAMERAS_GLOBAL_IMAGE_REFRESH_SECONDS =
|
||||||
`${CONF_CAMERAS_GLOBAL}.image.refresh_seconds` as const;
|
`${CONF_CAMERAS_GLOBAL}.image.refresh_seconds` as const;
|
||||||
export const CONF_CAMERAS_GLOBAL_DIMENSIONS_LAYOUT =
|
export const CONF_CAMERAS_GLOBAL_DIMENSIONS_LAYOUT =
|
||||||
|
|||||||
@@ -850,6 +850,7 @@ export class AdvancedCameraCardEditor extends LitElement implements LovelaceCard
|
|||||||
private _profiles: EditorSelectOption[] = [
|
private _profiles: EditorSelectOption[] = [
|
||||||
{ value: '', label: '' },
|
{ value: '', label: '' },
|
||||||
{ value: 'casting', label: localize('config.profiles.casting') },
|
{ value: 'casting', label: localize('config.profiles.casting') },
|
||||||
|
{ value: 'doorbell', label: localize('config.profiles.doorbell') },
|
||||||
{ value: 'low-performance', label: localize('config.profiles.low-performance') },
|
{ value: 'low-performance', label: localize('config.profiles.low-performance') },
|
||||||
{ value: 'scrubbing', label: localize('config.profiles.scrubbing') },
|
{ value: 'scrubbing', label: localize('config.profiles.scrubbing') },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -580,6 +580,7 @@
|
|||||||
},
|
},
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"casting": "Casting",
|
"casting": "Casting",
|
||||||
|
"doorbell": "Doorbell",
|
||||||
"editor_label": "Configuration profiles",
|
"editor_label": "Configuration profiles",
|
||||||
"low-performance": "Low performance",
|
"low-performance": "Low performance",
|
||||||
"scrubbing": "Video scrubbing"
|
"scrubbing": "Video scrubbing"
|
||||||
|
|||||||
@@ -440,24 +440,27 @@ describe('CameraManager', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('should fetch entity list when required', () => {
|
describe('should fetch entity list when required', () => {
|
||||||
it('should fetch with entity based trigger', async () => {
|
it.each([['occupancy' as const], ['motion' as const], ['doorbell' as const]])(
|
||||||
const api = createCardAPI();
|
'should fetch with %s trigger',
|
||||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
async (triggerKey) => {
|
||||||
|
const api = createCardAPI();
|
||||||
|
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||||
|
|
||||||
const manager = createCameraManager(api, mock<CameraManagerEngine>(), [
|
const manager = createCameraManager(api, mock<CameraManagerEngine>(), [
|
||||||
{
|
{
|
||||||
config: createCameraConfig({
|
config: createCameraConfig({
|
||||||
...baseCameraConfig,
|
...baseCameraConfig,
|
||||||
triggers: {
|
triggers: {
|
||||||
occupancy: true,
|
[triggerKey]: true,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await manager.initializeCamerasFromConfig();
|
await manager.initializeCamerasFromConfig();
|
||||||
expect(api.getEntityRegistryManager().fetchEntityList).toBeCalled();
|
expect(api.getEntityRegistryManager().fetchEntityList).toBeCalled();
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
it('should skip without entity based trigger', async () => {
|
it('should skip without entity based trigger', async () => {
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { expect, it } from 'vitest';
|
||||||
|
import { DOORBELL_PROFILE } from '../../../src/config/profiles/doorbell';
|
||||||
|
import { setProfiles } from '../../../src/config/profiles/set-profiles';
|
||||||
|
import { advancedCameraCardConfigSchema } from '../../../src/config/schema/types';
|
||||||
|
import { createRawConfig } from '../../test-utils';
|
||||||
|
|
||||||
|
it('should contain expected defaults', () => {
|
||||||
|
expect(DOORBELL_PROFILE).toEqual({
|
||||||
|
'cameras_global.triggers.doorbell': true,
|
||||||
|
'view.triggers.actions.interaction_mode': 'all',
|
||||||
|
'view.triggers.actions.trigger': 'call',
|
||||||
|
'view.triggers.actions.untrigger': 'call',
|
||||||
|
'view.triggers.show_trigger_status': true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be parseable after application', () => {
|
||||||
|
const rawInputConfig = createRawConfig();
|
||||||
|
const parsedConfig = advancedCameraCardConfigSchema.parse(rawInputConfig);
|
||||||
|
|
||||||
|
setProfiles(rawInputConfig, parsedConfig, ['doorbell']);
|
||||||
|
|
||||||
|
// Reparse the config to ensure the profile did not introduce errors.
|
||||||
|
const parseResult = advancedCameraCardConfigSchema.safeParse(parsedConfig);
|
||||||
|
expect(parseResult.success, parseResult.error?.toString()).toBeTruthy();
|
||||||
|
});
|
||||||
@@ -8,31 +8,24 @@ it('should contain expected defaults', () => {
|
|||||||
expect(LOW_PERFORMANCE_PROFILE).toEqual({
|
expect(LOW_PERFORMANCE_PROFILE).toEqual({
|
||||||
'cameras_global.image.refresh_seconds': 10,
|
'cameras_global.image.refresh_seconds': 10,
|
||||||
'cameras_global.live_provider': 'image',
|
'cameras_global.live_provider': 'image',
|
||||||
'cameras_global.triggers.occupancy': false,
|
|
||||||
'live.auto_mute': [],
|
'live.auto_mute': [],
|
||||||
'live.controls.thumbnails.mode': 'none',
|
'live.controls.thumbnails.mode': 'none',
|
||||||
'live.controls.thumbnails.show_details': false,
|
'live.controls.thumbnails.show_details': false,
|
||||||
'live.controls.thumbnails.show_download_control': false,
|
|
||||||
'live.controls.thumbnails.show_favorite_control': false,
|
'live.controls.thumbnails.show_favorite_control': false,
|
||||||
'live.controls.thumbnails.show_timeline_control': false,
|
|
||||||
'live.controls.timeline.show_recordings': false,
|
'live.controls.timeline.show_recordings': false,
|
||||||
'live.draggable': false,
|
'live.draggable': false,
|
||||||
'live.lazy_unload': ['unselected', 'hidden'],
|
'live.lazy_unload': ['unselected', 'hidden'],
|
||||||
'live.show_image_during_load': false,
|
'live.show_image_during_load': false,
|
||||||
'live.transition_effect': 'none',
|
'live.transition_effect': 'none',
|
||||||
'media_gallery.controls.thumbnails.show_details': false,
|
'media_gallery.controls.thumbnails.show_details': false,
|
||||||
'media_gallery.controls.thumbnails.show_download_control': false,
|
|
||||||
'media_gallery.controls.thumbnails.show_favorite_control': false,
|
'media_gallery.controls.thumbnails.show_favorite_control': false,
|
||||||
'media_gallery.controls.thumbnails.show_timeline_control': false,
|
|
||||||
'media_viewer.auto_mute': [],
|
'media_viewer.auto_mute': [],
|
||||||
'media_viewer.auto_pause': [],
|
'media_viewer.auto_pause': [],
|
||||||
'media_viewer.auto_play': [],
|
'media_viewer.auto_play': [],
|
||||||
'media_viewer.controls.next_previous.style': 'chevrons',
|
'media_viewer.controls.next_previous.style': 'chevrons',
|
||||||
'media_viewer.controls.thumbnails.mode': 'none',
|
'media_viewer.controls.thumbnails.mode': 'none',
|
||||||
'media_viewer.controls.thumbnails.show_details': false,
|
'media_viewer.controls.thumbnails.show_details': false,
|
||||||
'media_viewer.controls.thumbnails.show_download_control': false,
|
|
||||||
'media_viewer.controls.thumbnails.show_favorite_control': false,
|
'media_viewer.controls.thumbnails.show_favorite_control': false,
|
||||||
'media_viewer.controls.thumbnails.show_timeline_control': false,
|
|
||||||
'media_viewer.controls.timeline.show_recordings': false,
|
'media_viewer.controls.timeline.show_recordings': false,
|
||||||
'media_viewer.draggable': false,
|
'media_viewer.draggable': false,
|
||||||
'media_viewer.snapshot_click_plays_clip': false,
|
'media_viewer.snapshot_click_plays_clip': false,
|
||||||
@@ -51,9 +44,7 @@ it('should contain expected defaults', () => {
|
|||||||
'status_bar.style': 'none',
|
'status_bar.style': 'none',
|
||||||
'timeline.controls.thumbnails.mode': 'none',
|
'timeline.controls.thumbnails.mode': 'none',
|
||||||
'timeline.controls.thumbnails.show_details': false,
|
'timeline.controls.thumbnails.show_details': false,
|
||||||
'timeline.controls.thumbnails.show_download_control': false,
|
|
||||||
'timeline.controls.thumbnails.show_favorite_control': false,
|
'timeline.controls.thumbnails.show_favorite_control': false,
|
||||||
'timeline.controls.thumbnails.show_timeline_control': false,
|
|
||||||
'timeline.show_recordings': false,
|
'timeline.show_recordings': false,
|
||||||
'view.triggers.actions.trigger': 'none',
|
'view.triggers.actions.trigger': 'none',
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user