Commit Graph
95 Commits
Author SHA1 Message Date
Dermot Duffy a96dc2e863 chore: Enforce import order via @ianvs/prettier-plugin-sort-imports (#2542) 2026-06-30 17:45:13 -07:00
Dermot DuffyandClaude Opus 4.8 b701366762 feat: Align automations with Home Assistant triggers and conditions (#2527)
Split automations into HA-style `triggers`, ongoing `conditions`, and
`actions`, with compatibility migrations for existing Advanced Camera
Card configs.

## Summary

At a glance (details below):

- **Added** `triggers:` -- a required, HA-shaped block: stock `state` /
`numeric_state` / `template` plus card-specific triggers (`camera`,
`view`, `fullscreen`, ...).
- **Added** the HA-native `if` / `then` / `else` action.
- **Removed** `actions_not` (replaced by `if` / `then` / `else`).
- **Removed** the ambient `advanced_camera_card` template namespace (use
`acc` instead).
- **Changed** the trigger template surface to a top-level `trigger.*`
variable (as in HA); the nested `acc.trigger.*` paths are removed.
- **Changed** `conditions:` to ongoing gates only -- they no longer wake
an automation, and change-only forms (`config`, valueless `camera` /
`view` / `state`) become triggers, not conditions.
- **Changed** action templates to render per step, so a later action
sees state an earlier one changed.
- **Compatibility:** HA-shaped YAML is accepted (singular keys,
single-or-list, `and` / `or` / `not` shorthand, `entity` / `entity_id`).
- **Migration:** existing configs upgrade automatically; anything that
cannot be converted faithfully is recorded under `__UPGRADE_FAILURE__`
for manual fixup.

## Breaking Changes

### 1. Automations now require triggers

Before this PR, `automations[].conditions` served two roles:

- They decided whether the automation should run.
- They also acted as the thing that woke the automation up.

After this PR:

- `triggers` wake the automation.
- `conditions` only gate it at the instant a trigger fires.

Most existing automations are migrated automatically from `conditions:`
to `triggers:`.

### 2. `actions_not` is retired

Legacy `actions_not` is replaced by an HA-style `if` action with `then`
/ `else`.

Faithful conversions are automatic. Cases that cannot be faithfully
converted are recorded under `__UPGRADE_FAILURE__.automations` and must
be migrated manually.

### 3. Template surface aligned with Home Assistant

Two related template changes, both auto-migrated:

- **Top-level `trigger.*`.** Automation actions now receive a top-level
`trigger` template variable, like Home Assistant. Legacy nested paths
such as `acc.trigger.state.to` and
`advanced_camera_card.trigger.camera.to` are migrated automatically when
they appear inside template strings.
- **The ambient `advanced_camera_card` template namespace is removed.**
The long-form ambient namespace (`advanced_camera_card.camera`,
`advanced_camera_card.view`, `advanced_camera_card.config`) is retired
in favour of its shorter `acc` alias -- supported since v7.1.0, and the
only spelling the new trigger surface uses. Existing templates are
migrated automatically by rewriting the `advanced_camera_card.` prefix
to `acc.`.

### 4. Trigger-only condition forms are no longer valid conditions

Some legacy "conditions" were really change detectors. These are now
triggers only:

- `condition: config`
- valueless `camera`
- valueless `view`
- valueless `state` / picture-elements state condition with neither
`state` nor `state_not`

These are automatically promoted in automations and stripped from
overrides/elements where they would no longer be meaningful as ongoing
conditions.

### 5. Template truthiness now follows Home Assistant behavior

Template conditions and template triggers intentionally use different
truthiness rules, matching HA:

- A template condition passes only when the rendered value is `true`
(case-insensitive), matching HA's `condition.py`.
- A template trigger uses HA's broader `result_as_boolean` coercion: a
non-zero number, or `1` / `true` / `yes` / `on` / `enable`
(case-insensitive), counts as true.

### 6. Action templates render when each action executes

Action templates are now rendered per action step, not once for the
whole sequence. This means a later action can see card-local state
changed by an earlier action in the same sequence.

The `trigger` context is fixed for the automation run. HA entity state
updates still depend on the frontend receiving updated HASS state over
the websocket.

## Automatic Migrations

### Automation `conditions:` to `triggers:`

Simple legacy automation:

```yaml
# Before
automations:
  - conditions:
      - condition: fullscreen
        fullscreen: true
    actions:
      - action: custom:advanced-camera-card-action
        advanced_camera_card_action: substream_on
```

```yaml
# After, automatic
automations:
  - triggers:
      - trigger: fullscreen
        fullscreen: true
    actions:
      - action: custom:advanced-camera-card-action
        advanced_camera_card_action: substream_on
```

State conditions become HA-style state triggers:

```yaml
# Before
automations:
  - conditions:
      - condition: state
        entity_id: binary_sensor.front_door
        state: 'on'
    actions:
      - action: custom:advanced-camera-card-action
        advanced_camera_card_action: live
```

```yaml
# After, automatic
automations:
  - triggers:
      - trigger: state
        entity_id: binary_sensor.front_door
        to: 'on'
    actions:
      - action: custom:advanced-camera-card-action
        advanced_camera_card_action: live
```

Multiple conditions become both triggers and ongoing conditions:

```yaml
# Before
automations:
  - conditions:
      - condition: camera
        cameras: [front_door]
      - condition: fullscreen
        fullscreen: true
    actions:
      - action: custom:advanced-camera-card-action
        advanced_camera_card_action: substream_on
```

```yaml
# After, automatic
automations:
  - triggers:
      - trigger: camera
        cameras: [front_door]
      - trigger: fullscreen
        fullscreen: true
    conditions:
      - condition: camera
        cameras: [front_door]
      - condition: fullscreen
        fullscreen: true
    actions:
      - action: custom:advanced-camera-card-action
        advanced_camera_card_action: substream_on
```

The flattened trigger list is an implicit OR. The retained `conditions:`
list is an implicit AND checked when any trigger fires.

### Trigger-only legacy conditions

Legacy `config` conditions become `config` triggers:

```yaml
# Before
automations:
  - conditions:
      - condition: config
        paths: [menu.style]
    actions:
      - action: custom:advanced-camera-card-action
        advanced_camera_card_action: status_bar
```

```yaml
# After, automatic
automations:
  - triggers:
      - trigger: config
        paths: [menu.style]
    actions:
      - action: custom:advanced-camera-card-action
        advanced_camera_card_action: status_bar
```

Trigger-only leaves are removed from retained `conditions:` blocks
because they no longer describe an ongoing state.

### `actions_not` to `if` / `then` / `else`

```yaml
# Before
automations:
  - conditions:
      - condition: state
        entity_id: input_boolean.camera_alerts
        state: 'on'
    actions:
      - action: custom:advanced-camera-card-action
        advanced_camera_card_action: live
    actions_not:
      - action: none
```

```yaml
# After, automatic
automations:
  - triggers:
      - trigger: state
        entity_id: input_boolean.camera_alerts
    actions:
      - if:
          - condition: state
            entity_id: input_boolean.camera_alerts
            state: 'on'
        then:
          - action: custom:advanced-camera-card-action
            advanced_camera_card_action: live
        else:
          - action: none
```

If the legacy automation had no conditions, or only trigger-only
conditions, `actions_not` is dropped because the old `else` branch could
not be reproduced as an ongoing predicate.

### Trigger template paths

```yaml
# Before
message: 'Door is {{ acc.trigger.state.to }} from {{ acc.trigger.state.from }}'
```

```yaml
# After, automatic
message: 'Door is {{ trigger.to_state.state }} from {{ trigger.from_state.state }}'
```

Path rewrites performed automatically:

| Old path                   | New path                   |
| -------------------------- | -------------------------- |
| `acc.trigger.state.entity` | `trigger.entity_id`        |
| `acc.trigger.state.from`   | `trigger.from_state.state` |
| `acc.trigger.state.to`     | `trigger.to_state.state`   |
| `acc.trigger.camera.from`  | `trigger.from_acc.camera`  |
| `acc.trigger.camera.to`    | `trigger.to_acc.camera`    |
| `acc.trigger.view.from`    | `trigger.from_acc.view`    |
| `acc.trigger.view.to`      | `trigger.to_acc.view`      |
| `acc.trigger.config.from`  | `trigger.from_acc.config`  |
| `acc.trigger.config.to`    | `trigger.to_acc.config`    |

The same rewrites are applied for the older
`advanced_camera_card.trigger.*` namespace.

### Ambient template namespace

Any remaining long-form ambient `advanced_camera_card.*` references
(outside the trigger surface) are rewritten to the `acc.*` alias:

```yaml
# Before
title: 'Now viewing {{ advanced_camera_card.camera }}'
```

```yaml
# After, automatic
title: 'Now viewing {{ acc.camera }}'
```

## Manual Migration Cases

### `__UPGRADE_FAILURE__.automations`

If a legacy automation cannot be converted faithfully, the original
automation is recorded under:

```yaml
__UPGRADE_FAILURE__:
  automations:
    - ...
```

These entries require manual migration.

The main known case is legacy `actions_not` with a condition whose
trigger can only fire on a rising edge, such as:

- `condition: template`
- `condition: screen`
- `condition: numeric_state` without an entity-backed state to watch

Those conditions can start the `then` branch, but cannot reliably start
the `else` branch when they stop matching.

### Unsupported HA conditions and triggers

This PR aligns the card with HA where supported, but it is not a full HA
automation engine.

Unsupported HA condition families include:

- `time`
- `zone`
- `sun`
- `location`
- `device`
- `condition: trigger`

Unsupported HA trigger platforms include:

- `event`
- `time`
- `time_pattern`
- `sun`
- `zone`
- `calendar`
- `webhook`
- `tag`
- `device`
- `mqtt`

The card-specific camera `triggers:` feature (which auto-selects and
wakes the card on camera events such as motion) is a separate feature
from automation `triggers:`, despite the shared word.

### Trigger IDs and variables

HA keys such as `id`, `alias`, and `variables` are accepted so pasted HA
YAML validates, but they are ignored by the card. There is no
`trigger.id` support in this PR.

## New Compatibility Features

This PR also makes card config more forgiving for HA-style YAML:

- `trigger`, `condition`, and `action` singular keys are accepted and
normalized to `triggers`, `conditions`, and `actions`.
- Single trigger, condition, and action objects are accepted where lists
are expected.
- `if`, `then`, and `else` accept a single item or a list.
- Composite condition shorthand is accepted:
  - `{ and: [...] }`
  - `{ or: [...] }`
  - `{ not: [...] }`
  - `{ condition: [...] }` as an implicit AND
- State conditions resolve expected state values that name another
entity, matching HA/Lovelace behavior.
- Both `entity` and `entity_id` are accepted on state and numeric
conditions and triggers (a superset of HA's two dialects), so there is
no forced rename.
- `state_not` remains supported as a card/Lovelace-friendly extension.

## Trigger Payloads

Automation action templates receive a top-level `trigger` object.

For stock `state` and `numeric_state` triggers:

```yaml
trigger.platform
trigger.entity_id
trigger.entity
trigger.from_state
trigger.to_state
```

For template triggers:

```yaml
trigger.platform
```

For card-specific triggers:

```yaml
trigger.platform # "acc"
trigger.type
trigger.from_acc
trigger.to_acc
```

The card does not currently expose HA's `id`, `idx`, `for`, `attribute`,
`above`, `below`, or `alias` trigger fields.

BREAKING CHANGE: Automations now follow Home Assistant's `triggers:` /
`conditions:` / `actions:` model. Automations require a `triggers:`
block and `conditions:` no longer wake an automation; `actions_not` is
removed in favour of an `if` / `then` / `else` action; the nested
`acc.trigger.*` template paths and the ambient `advanced_camera_card`
template namespace are removed (use the top-level `trigger.*` surface
and the `acc` alias); trigger-only condition forms (`config`, valueless
`camera` / `view` / `state`) are no longer valid conditions; and
template-condition vs template-trigger truthiness now follow HA.
Existing configs are upgraded automatically where a faithful conversion
exists; anything that cannot be converted is recorded under
`__UPGRADE_FAILURE__` for manual migration.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 17:45:13 -07:00
Dermot Duffy fc32727860 feat: Add support for Frigate reviews / detections [initial PR] (#2315)
- Add support for Frigate reviews / detections.
 - Add support for GenAI metadata.
- Significant internal refactor to more flexible "UnifiedQuery" to allow
mixing cameras with simple metadata and review metadata (e.g. a timeline
view of a Frigate camera with reviews, and a Reolink camera with simple
metadata).
 - Add support for folder media as camera media.

There are a few more PRs to commit prior to this going live, but
commiting this for now due to the scale of the change.

BREAKING CHANGE: `media_type` and `events_type` are retired under
`live`, `viewer` and `timeline` configuration sections, instead media
type is associated (once) with the camera under `media`.
2026-01-19 13:32:50 -08:00
Dermot Duffy 1f6a4ddce2 fix: Fix folder media in timeline when camera is not timeline supported (#2246)
- Closes: #2205
2025-11-24 14:33:06 -08:00
Dermot Duffy 7edaac079e fix: Timeline should should show folder media even without camera support (#2216)
- Closes #2205
2025-10-13 21:15:32 -07:00
Dermot Duffy 78dee9194f feat: Add timeline support for folder media (#2197)
- Closes #2106
2025-10-04 15:47:39 -07:00
Dermot Duffy c6a4c8aea2 feat: Implement basic general folder support (#2051)
- Related: #1748
2025-05-21 19:59:21 -07:00
Dermot Duffy d6225fefda refactor: Break apart monolithic configuration schema (#1977)
[skip ci]
2025-03-22 15:06:11 -07:00
Dermot Duffy a79ffa6edc refactor: Retire the use of unmaintained custom-card-helpers (#1956)
- Closes #1295
2025-03-09 16:02:33 -07:00
Dermot Duffy cc376a8be1 feat: Rename the card to advanced-camera-card (#1873)
Whilst the Frigate support in this card is the best among camera
engines, the name incorrectly suggests that Frigate is a requirement.
Instead, to broaden the appeal, change to more camera agnostic name.
This does not suggest any change in priority, role or support for
Frigate.

This change is likely to be bug prone, due to the size of the rename --
the code contains 1500+ references to "Frigate" most of which make sense
to rename, some which do not, all of which needed human assessment.

- Closes #1298


BREAKING CHANGE: References to `frigate-card` in all kinds of
configuration need to be updated to `advanced-camera-card`. An automated
config upgrade should take care of the majority of usecases (click `Edit
-> Upgrade -> Save`), though may not be perfect.
2025-02-06 19:32:32 -08:00
Dermot Duffy d5c4e56c45 Refactor views to support dynamic updates. 2024-07-26 19:15:38 -07:00
Dermot Duffy 95c44adcf1 Allow camera capabilities to be configured. 2024-03-26 21:00:15 -07:00
Dermot Duffy 9b72dd8089 Refactor main types.ts . 2023-10-02 22:35:58 -07:00
Dermot Duffy b663e0b731 Initial support for grid for live and media viewer. 2023-08-08 22:26:25 -07:00
Dermot Duffy e1d74dc35a Enable smooth panning between past/live. 2023-04-09 19:23:34 -07:00
Dermot Duffy ecf954249d Select thumbnail unless in viewer. 2023-04-07 14:38:00 -07:00
Dermot Duffy 03cea41675 Add a performance option to control media chunk size. 2023-02-13 21:27:39 -08:00
Dermot Duffy 06b4cc914c Refactor camera initialization into the camera engines. 2023-02-12 16:10:22 -08:00
Dermot Duffy 01c3a79ff1 Remove the MediaQuery wrapper from the camera manager. 2023-01-29 20:00:45 -08:00
Dermot Duffy 730eb42983 Rename camera directory to camera-manager. 2023-01-29 19:02:24 -08:00
Dermot Duffy 980ba1fc6f Break apart view.ts into multiple files. 2023-01-24 19:36:54 -08:00
Dermot Duffy 39b44863ce Rename DataManager to CameraManager. 2023-01-24 19:36:54 -08:00
Dermot Duffy 093925cb40 Initial code for major engine refactor. 2023-01-24 19:36:54 -08:00
Dermot Duffy 143a597039 Rename TimelineDataManager to DataManager . 2022-10-02 20:19:47 -07:00
Dermot Duffy 2f34d6e7fa Comment cleanups. 2022-09-25 13:44:54 -07:00
Dermot Duffy 69ca622284 Make mini timelines editable. 2022-09-25 09:12:36 -07:00
Dermot Duffy 5ec3cbac54 Garbage collect segments. 2022-09-24 13:39:17 -07:00
Dermot Duffy a2e80d93a3 Fetch timeline events from events endpoint not media browser. 2022-09-24 11:31:03 -07:00
Dermot Duffy 0491018aab Minor fixes. 2022-09-23 18:21:17 -07:00
Dermot Duffy c60fff243d Improvements to updating timeline from view. 2022-09-22 17:44:43 -07:00
Dermot Duffy eb88c6e0e0 Rename surround-thumbnails to just surround. 2022-09-22 17:44:43 -07:00
Dermot Duffy ca831a7184 Break circular dependency. 2022-09-22 17:44:43 -07:00
Dermot Duffy eb1b44eb27 Initial mini-timeline commit. 2022-09-22 17:44:43 -07:00
Dermot Duffy 70d445d921 Fix drawer open bug in iOS Safari. 2022-08-13 13:48:01 -07:00
Dermot Duffy 883c622032 Minor fixes. 2022-08-07 13:54:11 -07:00
Dermot Duffy c19a62d426 Initial attempt add hiding elements during loading. 2022-08-07 13:20:03 -07:00
Dermot Duffy 5fbbf127ab Add support for fetching thumnails separately 2022-07-31 10:21:43 -07:00
Dermot Duffy 0f04e9383a Add event starring from the card. 2022-06-28 20:19:32 -07:00
Dermot Duffy 12bdb38cb2 Various timeline fixes for Safari 2022-06-25 12:55:59 -07:00
Dermot Duffy 22ac4b0b7e No longer support actions on timeline view. 2022-06-25 09:09:57 -07:00
Dermot Duffy 5a3703457d Add message priorities. 2022-06-24 19:42:25 -07:00
Dermot Duffy dce1c4b161 Pre-fetch events from a larger range than the timeline shows 2022-06-20 03:39:50 +00:00
Dermot Duffy 241b9b85a2 Don't execute card actions when timeline is dragged 2022-06-20 03:16:01 +00:00
Dermot Duffy 4901905ca1 Make error handling and console logging consistent 2022-06-20 03:03:27 +00:00
Dermot Duffy ffa31c05ed Clean lit-plugin warnings 2022-06-19 07:30:08 +00:00
Dermot Duffy acbbef1bdb Deduplicate Frigate cameras on the timeline. 2022-06-11 16:06:48 -07:00
Dermot Duffy e0c5259207 Change styling when recordings are disabled. 2022-06-11 12:35:13 -07:00
Dermot Duffy a62eaf4bc9 Change configuration path for Frigate options. 2022-06-10 13:44:10 -07:00
Dermot Duffy a02ec12958 Add some new screenshots. 2022-06-04 11:31:27 -07:00
Dermot Duffy ac3b8634e8 Rename the event_viewer to media_viewer. 2022-06-04 11:04:38 -07:00