Files
advanced-camera-card/tests/card-controller/view/modifiers/substream.test.ts
T
Dermot Duffy f18e4cd4b8 refactor: Unify substream actions into substream_{on,off} (#2497)
## Summary

- Collapse `live_substream_{on,off,select}` into a unified
`substream_{on,off}` pair, symmetric with `call_{start,end}`.
- Rename the `camera` field on the former `live_substream_select` to
`stream` (it always was a stream ID).
- Add optional `camera` field to both new actions for targeting a
non-selected base camera.
- YAML configs are migrated automatically; URL bookmarks must be updated
by hand.

## Migration

### Cycling between camera and substream (toggle button)

Before:
```yaml
tap_action:
  action: custom:advanced-camera-card-action
  advanced_camera_card_action: live_substream_on
```

After:
```yaml
tap_action:
  action: custom:advanced-camera-card-action
  advanced_camera_card_action: substream_on
```

### Selecting a specific substream

Before:
```yaml
tap_action:
  action: custom:advanced-camera-card-action
  advanced_camera_card_action: live_substream_select
  camera: camera.front_door_hd
```

After:
```yaml
tap_action:
  action: custom:advanced-camera-card-action
  advanced_camera_card_action: substream_on
  stream: camera.front_door_hd
```

### Turning the substream off

Before:
```yaml
tap_action:
  action: custom:advanced-camera-card-action
  advanced_camera_card_action: live_substream_off
```

After:
```yaml
tap_action:
  action: custom:advanced-camera-card-action
  advanced_camera_card_action: substream_off
```

### URL querystrings (manual update required)

| Before | After |
| --- | --- |
|
`?advanced-camera-card-action.live_substream_select=camera.front_door_hd`
| `?advanced-camera-card-action.substream_on=camera.front_door_hd` |
2026-06-30 17:45:13 -07:00

70 lines
2.3 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { SubstreamViewModifier } from '../../../../src/card-controller/view/modifiers/substream';
import { createView } from '../../../test-utils';
describe('SubstreamViewModifier', () => {
it('should write the override for the selected camera', () => {
const view = createView({ camera: 'camera' });
new SubstreamViewModifier({ stream: 'substream' }).modify(view);
expect(view.context?.live?.overrides?.get('camera')).toBe('substream');
});
it('should clear the selected camera override when no substream is given', () => {
const view = createView({
camera: 'camera',
context: { live: { overrides: new Map([['camera', 'substream']]) } },
});
new SubstreamViewModifier().modify(view);
expect(view.context?.live?.overrides?.get('camera')).toBeUndefined();
});
it('should write the override for an explicit camera', () => {
const view = createView({
camera: 'camera',
context: { live: { overrides: new Map([['camera', 'substream']]) } },
});
new SubstreamViewModifier({
stream: 'other-substream',
camera: 'other-camera',
}).modify(view);
expect(view.context?.live?.overrides?.get('other-camera')).toBe('other-substream');
expect(view.context?.live?.overrides?.get('camera')).toBe('substream');
});
it('should clear the override for an explicit camera', () => {
const view = createView({
camera: 'camera',
context: { live: { overrides: new Map([['other-camera', 'other-substream']]) } },
});
new SubstreamViewModifier({ camera: 'other-camera' }).modify(view);
expect(view.context?.live?.overrides?.get('other-camera')).toBeUndefined();
});
it('should clear the override when the stream equals the camera itself', () => {
const view = createView({
camera: 'camera',
context: { live: { overrides: new Map([['camera', 'substream']]) } },
});
new SubstreamViewModifier({ stream: 'camera' }).modify(view);
expect(view.context?.live?.overrides?.get('camera')).toBeUndefined();
});
it('should no-op for a view without a camera', () => {
const view = createView({ camera: null });
new SubstreamViewModifier({ stream: 'substream' }).modify(view);
expect(view.context).toBeNull();
});
});