Skip to content

Commit

Permalink
Merge pull request #1212 from dermotduffy/merge-5.2-dev-to-main
Browse files Browse the repository at this point in the history
Merge v5.2.0 release back into main
  • Loading branch information
dermotduffy committed Jun 23, 2023
2 parents 2d977be + 69249b6 commit b10241c
Show file tree
Hide file tree
Showing 61 changed files with 3,484 additions and 885 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
filename: frigate-hass-card.zip

- name: Upload JS files to release
uses: svenstaro/upload-release-action@2.5.0
uses: svenstaro/upload-release-action@2.6.1

with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -41,7 +41,7 @@ jobs:
overwrite: true

- name: Upload Zip file to release
uses: svenstaro/upload-release-action@2.5.0
uses: svenstaro/upload-release-action@2.6.1

with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
91 changes: 88 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ menu:
| `camera_ui` | :white_check_mark: | The `camera_ui` menu button: brings the user to a context-appropriate page on the UI of their camera engine (e.g. the Frigate camera homepage). Will only appear if the camera engine supports a camera UI (e.g. if `frigate.url` option is set for `frigate` engine users).|
| `fullscreen` | :white_check_mark: | The `fullscreen` menu button: expand the card to consume the fullscreen. |
| `expand` | :white_check_mark: | The `expand` menu button: expand the card into a popup/dialog. |
| `screenshot` | :white_check_mark: | The `screenshot` menu button: take a screenshot of the loaded media (e.g. a still from a video). |
| `timeline` | :white_check_mark: | The `timeline` menu button: show the event timeline. |
| `media_player` | :white_check_mark: | The `media_player` menu button: sends the visible media to a remote media player. Supports Frigate clips, snapshots and live camera (only for cameras that specify a `camera_entity` and only using the default HA stream (equivalent to the `ha` live provider). `jsmpeg` or `webrtc-card` are not supported, although live can still be played as long as `camera_entity` is specified. In the player list, a `tap` will send the media to the player, a `hold` will stop the media on the player. |
| `microphone` | :white_check_mark: | The `microphone` button allows usage of 2-way audio in certain configurations. See [Using 2-way audio](#using-2-way-audio). |
Expand Down Expand Up @@ -1291,7 +1292,7 @@ Parameters for the `custom:frigate-card-ptz` element:
| Parameter | Description |
| - | - |
| `action` | Must be `custom:frigate-card-action`. |
| `frigate_card_action` | Call a Frigate Card action. Acceptable values are `default`, `clip`, `clips`, `image`, `live`, `recording`, `recordings`, `snapshot`, `snapshots`, `download`, `timeline`, `camera_ui`, `fullscreen`, `camera_select`, `menu_toggle`, `media_player`, `live_substream_on`, `live_substream_off`, `live_substream_select`, `expand`, `microphone_mute`, `microphone_unmute`, `mute`, `unmute`, `play`, `pause`|
| `frigate_card_action` | Call a Frigate Card action. Acceptable values are `default`, `clip`, `clips`, `image`, `live`, `recording`, `recordings`, `snapshot`, `snapshots`, `download`, `timeline`, `camera_ui`, `fullscreen`, `camera_select`, `menu_toggle`, `media_player`, `live_substream_on`, `live_substream_off`, `live_substream_select`, `expand`, `microphone_mute`, `microphone_unmute`, `mute`, `unmute`, `play`, `pause`, `screenshot`|

<a name="custom-actions"></a>

Expand All @@ -1312,6 +1313,7 @@ Parameters for the `custom:frigate-card-ptz` element:
|`microphone_mute`, `microphone_unmute`| Mute or unmute the microphone. See [Using 2-way audio](#using-2-way-audio). |
|`mute`, `unmute`| Mute or unmute the loaded media. |
|`play`, `pause`| Play or pause the loaded media. |
|`screenshot`| Take a screenshot of the loaded media (e.g. a still from a video). |

<a name="views"></a>

Expand Down Expand Up @@ -1584,6 +1586,10 @@ Pan around a large camera view to only show part of the video feed in the card a

<img src="https://raw.githubusercontent.com/dermotduffy/frigate-hass-card/dev/images/zoom.gif" alt="Zoom Support" width="400px">

### Taking card actions via the URL

<img src="https://raw.githubusercontent.com/dermotduffy/frigate-hass-card/dev/images/navigate-picture-elements.gif" alt="Taking card actions via the URL" width="400px">

## Examples

### Illustrative Expanded Configuration Reference
Expand Down Expand Up @@ -2422,6 +2428,12 @@ elements:
frigate_card_action: media_player
media_player: media_player.nesthub
media_player_action: stop
- type: custom:frigate-card-menu-icon
icon: mdi:alpha-o-circle
title: Screenshot
tap_action:
action: custom:frigate-card-action
frigate_card_action: screenshot
```
</details>

Expand Down Expand Up @@ -3745,6 +3757,47 @@ https://ha.mydomain.org/lovelace-test/0?frigate-card-action:main:clips
```
</details>

<details>
<summary>Expand: Choosing the camera from a separate picture elements card</summary>

In this example, the card will select a given camera when the user navigates from a *separate* Picture Elements card:

<img src="https://raw.githubusercontent.com/dermotduffy/frigate-hass-card/dev/images/navigate-picture-elements.gif" alt="Taking card actions via the URL" width="400px">

Frigate Card configuration:

```yaml
type: custom:frigate-card
cameras:
- camera_entity: camera.living_room
- camera_entity: camera.landing
```
Picture Elements configuration (assumes the dashboard is `/lovelace-frigate/map`):

```yaml
type: picture-elements
image: https://demo.home-assistant.io/stub_config/floorplan.png
elements:
- type: icon
icon: mdi:cctv
style:
top: 22%
left: 30%
tap_action:
action: navigate
navigation_path: /lovelace-frigate/map?frigate-card-action:camera_select=camera.living_room
- type: icon
icon: mdi:cctv
style:
top: 71%
left: 42%
tap_action:
action: navigate
navigation_path: /lovelace-frigate/map?frigate-card-action:camera_select=camera.landing
```

</details>

### Automation actions

Expand Down Expand Up @@ -3836,13 +3889,20 @@ view:

It is possible to pass the Frigate card one or more actions from the URL (e.g. select a particular camera, open the live view in expanded mode, etc).

To send an action to *all* Frigate cards on a dashboard:
The Frigate card will execute these actions in the following circumstances:

* On initial card load.
* On 'tab' change in a dashboard.
* When a `navigate` [action](https://www.home-assistant.io/dashboards/actions/) is called on the dashboard (e.g. a button click requests navigation).
* When the user uses the `back` / `forward` browser buttons whilst viewing a dashboard.

To send an action to *all* Frigate cards:

```
[PATH_TO_YOUR_HA_DASHBOARD]?frigate-card-action:[ACTION]=[VALUE]
```

To send an action to a named Frigate card on the dashboard:
To send an action to a named Frigate card:

```
[PATH_TO_YOUR_HA_DASHBOARD]?frigate-card-action:[CARD_ID]:[ACTION]=[VALUE]
Expand All @@ -3854,6 +3914,10 @@ To send an action to a named Frigate card on the dashboard:
| `CARD_ID` | When specified only cards that have a `card_id` parameter will act. |
| `VALUE` | An optional value to use with the `camera_select` and `live_substream_select` actions. |

**Note**: If a dashboard has multiple Frigate cards on it, even if they are on
different 'tabs' within that dashboard, they will all respond to the actions
unless the action is targeted with a `CARD_ID` as shown above.

#### Actions

| Action | Supported in query string | Explanation |
Expand All @@ -3876,6 +3940,7 @@ To send an action to a named Frigate card on the dashboard:
| `play`, `pause` | :heavy_multiplication_x: | |
| `recording` | :white_check_mark: | |
| `recordings` | :white_check_mark: | |
| `screenshot`| :heavy_multiplication_x: | Latest media information is not available on initial render. |
| `snapshot` | :white_check_mark: | |
| `snapshots` | :white_check_mark: | |

Expand Down Expand Up @@ -3940,6 +4005,21 @@ live:
mode: none
```

### Title "Popups" are annoying / continually popping up

Title popups can be disabled for live or media viewer views with this configuration:

```yaml
live:
controls:
title:
mode: none
media_viewer:
controls:
title:
mode: none
```

### Microphone / 2-way audio doesn't work

There are many requirements for 2-way audio to work. See [Using 2-way
Expand Down Expand Up @@ -4126,3 +4206,8 @@ The Home Assistant container will get preconfigured during first initialization,
1. Use the same version number for the release title and tag.
1. Choose 'This is a pre-release' for a beta version.
1. Hit 'Publish release'.

### Translations
[![translation badge](https://inlang.com/badge?url=github.com/dermotduffy/frigate-hass-card)](https://inlang.com/editor/github.com/dermotduffy/frigate-hass-card?ref=badge)

To add translations, you can manually edit the JSON translation files in `src/localize/languages`, use the [inlang](https://inlang.com/) online editor, or run `yarn machine-translate` to add missing translations using AI from Inlang.
Binary file added images/navigate-picture-elements.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 13 additions & 24 deletions inlang.config.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,17 @@
// @ts-check

/**
* @type { import("@inlang/core/config").DefineConfig }
*/
export async function defineConfig(env) {
const plugin = await env.$import(
"https://cdn.jsdelivr.net/gh/samuelstroschein/inlang-plugin-json@1/dist/index.js"
);

const { standardLintRules } = await env.$import(
"https://cdn.jsdelivr.net/gh/inlang/standard-lint-rules@1/dist/index.js"
);
const { default: pluginJson } = await env.$import(
"https://cdn.jsdelivr.net/gh/samuelstroschein/inlang-plugin-json@2/dist/index.js"
);

const pluginConfig = {
pathPattern: "./src/localize/languages/{language}.json",
};
const { default: standardLintRules } = await env.$import(
"https://cdn.jsdelivr.net/gh/inlang/standard-lint-rules@2/dist/index.js"
);

return {
referenceLanguage: "en",
languages: await plugin.getLanguages({ ...env, pluginConfig }),
readResources: (args) => plugin.readResources({ ...args, ...env, pluginConfig }),
writeResources: (args) => plugin.writeResources({ ...args, ...env, pluginConfig }),
lint: {
rules: [standardLintRules()],
},
};
return {
referenceLanguage: 'en',
plugins: [pluginJson({
pathPattern: "./src/localize/languages/{language}.json",
variableReferencePattern: ["{", "}"],
}), standardLintRules()]
};
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "frigate-hass-card",
"version": "5.1.1",
"version": "5.2.0",
"description": "Frigate Lovelace Card for Home Assistant",
"keywords": [
"frigate",
Expand All @@ -18,7 +18,7 @@
"@cycjimmy/jsmpeg-player": "^6.0.4",
"@dermotduffy/panzoom": "^4.5.1",
"@egjs/hammerjs": "^2.0.17",
"@graphiteds/core": "^1.9.6",
"@graphiteds/core": "^1.9.11",
"@lit-labs/scoped-registry-mixin": "^1.0.1",
"@lit-labs/task": "^1.1.3",
"@types/bluebird": "^3.5.36",
Expand Down
32 changes: 13 additions & 19 deletions src/action-handler-directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
DirectiveParameters,
} from 'lit/directive.js';
import { stopEventFromActivatingCardWideActions } from './utils/action.js';
import { Timer } from './utils/timer.js';

interface ActionHandler extends HTMLElement {
holdTime: number;
Expand All @@ -25,14 +26,13 @@ interface FrigateCardActionHandlerOptions extends ActionHandlerOptions {
}

class ActionHandler extends HTMLElement implements ActionHandler {
public holdTime = 400;
public holdTime = 0.4;

protected timer?: number;
protected holdTimer = new Timer();
protected doubleClickTimer = new Timer();

protected held = false;

private dblClickTimeout?: number;

public connectedCallback(): void {
[
'touchcancel',
Expand All @@ -46,10 +46,7 @@ class ActionHandler extends HTMLElement implements ActionHandler {
document.addEventListener(
ev,
() => {
if (this.timer) {
clearTimeout(this.timer);
this.timer = undefined;
}
this.holdTimer.stop();
},
{ passive: true },
);
Expand Down Expand Up @@ -79,9 +76,9 @@ class ActionHandler extends HTMLElement implements ActionHandler {

const start = (): void => {
this.held = false;
this.timer = window.setTimeout(() => {
this.holdTimer.start(this.holdTime, () => {
this.held = true;
}, this.holdTime);
});

fireEvent(element, 'action', { action: 'start_tap' });
};
Expand All @@ -103,8 +100,7 @@ class ActionHandler extends HTMLElement implements ActionHandler {
return;
}

clearTimeout(this.timer);
this.timer = undefined;
this.holdTimer.stop();

fireEvent(element, 'action', { action: 'end_tap' });

Expand All @@ -113,15 +109,13 @@ class ActionHandler extends HTMLElement implements ActionHandler {
} else if (options?.hasDoubleClick) {
if (
(ev.type === 'click' && (ev as MouseEvent).detail < 2) ||
!this.dblClickTimeout
!this.doubleClickTimer.isRunning()
) {
this.dblClickTimeout = window.setTimeout(() => {
this.dblClickTimeout = undefined;
fireEvent(element, 'action', { action: 'tap' });
}, 250);
this.doubleClickTimer.start(0.25, () =>
fireEvent(element, 'action', { action: 'tap' }),
);
} else {
clearTimeout(this.dblClickTimeout);
this.dblClickTimeout = undefined;
this.doubleClickTimer.stop();
fireEvent(element, 'action', { action: 'double_tap' });
}
} else {
Expand Down
14 changes: 7 additions & 7 deletions src/cached-value-controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ReactiveController, ReactiveControllerHost } from 'lit';
import { Timer } from './utils/timer';

export class CachedValueController<T> implements ReactiveController {
protected _value?: T;
Expand All @@ -7,7 +8,7 @@ export class CachedValueController<T> implements ReactiveController {
protected _callback: () => T;
protected _timerStartCallback?: () => void;
protected _timerStopCallback?: () => void;
protected _timerID?: number;
protected _timer = new Timer();

constructor(
host: ReactiveControllerHost,
Expand Down Expand Up @@ -56,11 +57,10 @@ export class CachedValueController<T> implements ReactiveController {
* Disable the timer.
*/
public stopTimer(): void {
if (this._timerID !== undefined) {
window.clearInterval(this._timerID);
if (this._timer.isRunning()) {
this._timer.stop();
this._timerStopCallback?.();
}
this._timerID = undefined;
}

/**
Expand All @@ -71,15 +71,15 @@ export class CachedValueController<T> implements ReactiveController {

if (this._timerSeconds > 0) {
this._timerStartCallback?.();
this._timerID = window.setInterval(() => {
this._timer.startRepeated(this._timerSeconds, () => {
this.updateValue();
this._host.requestUpdate();
}, this._timerSeconds * 1000);
});
}
}

public hasTimer(): boolean {
return !!this._timerID;
return this._timer.isRunning();
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/camera-manager/frigate/engine-frigate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ export class FrigateCameraManagerEngine
`/api/frigate/${cameraConfig.frigate.client_id}` +
`/recording/${cameraConfig.frigate.camera_name}` +
`/start/${Math.floor(media.getStartTime().getTime() / 1000)}` +
`/end/${Math.floor(media.getEndTime().getTime() / 1000)}}` +
`/end/${Math.floor(media.getEndTime().getTime() / 1000)}` +
`?download=true`,
sign: true,
};
Expand Down
2 changes: 1 addition & 1 deletion src/camera-manager/frigate/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const dayStringToDate = (arg: unknown): Date | unknown => {
return typeof arg === 'string' ? dayToDate(arg) : arg;
};

const eventSchema = z.object({
export const eventSchema = z.object({
camera: z.string(),
end_time: z.number().nullable(),
false_positive: z.boolean().nullable(),
Expand Down
Loading

0 comments on commit b10241c

Please sign in to comment.