Merge pull request #1520 from dermotduffy/perform-action

Add support for `perform-action` calls
This commit is contained in:
Dermot Duffy
2024-09-13 18:30:02 -07:00
committed by GitHub
14 changed files with 246 additions and 111 deletions
@@ -4,6 +4,8 @@
Call a service. See [Home Assistant actions documentation](https://www.home-assistant.io/dashboards/actions/). Call a service. See [Home Assistant actions documentation](https://www.home-assistant.io/dashboards/actions/).
!> Home Assistant has deprecated the `call-service` action, please use [`perform-action`](#perform-action) instead.
```yaml ```yaml
action: call-service action: call-service
# [...] # [...]
@@ -27,6 +29,15 @@ action: navigate
# [...] # [...]
``` ```
## `perform-action`
Perform a Home Assistant action. See [Home Assistant actions documentation](https://www.home-assistant.io/dashboards/actions/).
```yaml
action: perform-action
# [...]
```
## `toggle` ## `toggle`
Toggle an entity. See [Home Assistant actions documentation](https://www.home-assistant.io/dashboards/actions/). Toggle an entity. See [Home Assistant actions documentation](https://www.home-assistant.io/dashboards/actions/).
@@ -117,4 +128,15 @@ elements:
tap_action: tap_action:
action: fire-dom-event action: fire-dom-event
key: value key: value
- type: icon
icon: mdi:numeric-8-box
title: Perform action
style:
left: 200px
top: 400px
tap_action:
action: perform-action
perform_action: homeassistant.toggle
target:
entity_id: light.office_main_lights
``` ```
+11 -11
View File
@@ -210,8 +210,8 @@ the integration currently offers.
| Option | Default | Description | | Option | Default | Description |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `actions_left`, `actions_right`, `actions_up`, `actions_down`, `actions_zoom_in`, `actions_zoom_out`, `actions_home` | Set by camera [engine](./engine.md) of the selected camera | The [call-service](../actions/stock/README.md?id=call-service) action that will be called for each PTZ action for relative movements. | | `actions_left`, `actions_right`, `actions_up`, `actions_down`, `actions_zoom_in`, `actions_zoom_out`, `actions_home` | Set by camera [engine](./engine.md) of the selected camera | The [perform-action](../actions/stock/README.md?id=perform-action) action that will be called for each PTZ action for relative movements. |
| `actions_left_start`, `actions_left_stop`, `actions_right_start`, `actions_right_stop`,`actions_up_start`, `actions_up_stop`,`actions_down_start`, `actions_down_stop`,`actions_zoom_in_start`, `actions_zoom_in_stop`,`actions_zoom_out_start`, `actions_zoom_out_stop` | Set by camera [engine](./engine.md) of the selected camera | The [call-service](../actions/stock/README.md?id=call-service) action that will be called for each PTZ action for continous movements. Both a `_start` and `_stop` variety must be provided for an action to be usable. | | `actions_left_start`, `actions_left_stop`, `actions_right_start`, `actions_right_stop`,`actions_up_start`, `actions_up_stop`,`actions_down_start`, `actions_down_stop`,`actions_zoom_in_start`, `actions_zoom_in_stop`,`actions_zoom_out_start`, `actions_zoom_out_stop` | Set by camera [engine](./engine.md) of the selected camera | The [perform-action](../actions/stock/README.md?id=perform-action) action that will be called for each PTZ action for continous movements. Both a `_start` and `_stop` variety must be provided for an action to be usable. |
| `c2r_delay_between_calls_seconds` | `0.2` | When the camera is configured with continuous actions only (e.g. `left_start` and `left_stop`, but not `left`), if something requests a relative action (e.g. a manually configured [action](../actions/README.md)), then `start` will be called, followed by a delay of this number of seconds and finally `stop` will be called. Cameras / integrations that are slower to respond to continuous steps may need to increase this value to avoid the continuous motion being too small. Cameras / integrations that are rapid to respond may need to decrease this value to avoid the "relative step" being too large. | | `c2r_delay_between_calls_seconds` | `0.2` | When the camera is configured with continuous actions only (e.g. `left_start` and `left_stop`, but not `left`), if something requests a relative action (e.g. a manually configured [action](../actions/README.md)), then `start` will be called, followed by a delay of this number of seconds and finally `stop` will be called. Cameras / integrations that are slower to respond to continuous steps may need to increase this value to avoid the continuous motion being too small. Cameras / integrations that are rapid to respond may need to decrease this value to avoid the "relative step" being too large. |
| `data_left`, `data_right`, `data_up`, `data_down`, `data_zoom_in`, `data_zoom_out`, `data_home` | | Shorthand for relative actions that call the service defined by the `service` parameter, with the data provided in this argument. Internally, this is just translated into the longer-form `actions_[action]`. If both `actions_X` and `data_X` are specified, `actions_X` takes priority. This is compatible with [AlexxIT's WebRTC Card PTZ configuration](https://github.com/AlexxIT/WebRTC/wiki/PTZ-Config-Examples). | | `data_left`, `data_right`, `data_up`, `data_down`, `data_zoom_in`, `data_zoom_out`, `data_home` | | Shorthand for relative actions that call the service defined by the `service` parameter, with the data provided in this argument. Internally, this is just translated into the longer-form `actions_[action]`. If both `actions_X` and `data_X` are specified, `actions_X` takes priority. This is compatible with [AlexxIT's WebRTC Card PTZ configuration](https://github.com/AlexxIT/WebRTC/wiki/PTZ-Config-Examples). |
| `data_left_start`, `data_left_stop`, `data_right_start`, `data_right_stop`, `data_up_start`, `data_up_stop`, `data_down_start`, `data_down_stop`, `data_zoom_in_start`, `data_zoom_in_stop`, `data_zoom_out_start`, `data_zoom_out_stop` | | Shorthand for continuous actions that call the service defined by the `service` parameter, with the data provided in this argument. Internally, this is just translated into the longer-form `actions_[action]_start` and `actions_[action]_stop`. If both `actions_X_*` and `data_X_*` are specified, `actions_X_*` takes priority. This is compatible with [AlexxIT's WebRTC Card PTZ configuration](https://github.com/AlexxIT/WebRTC/wiki/PTZ-Config-Examples). Both a `_start` and `_stop` variety must be provided for an action to be usable. | | `data_left_start`, `data_left_stop`, `data_right_start`, `data_right_stop`, `data_up_start`, `data_up_stop`, `data_down_start`, `data_down_stop`, `data_zoom_in_start`, `data_zoom_in_stop`, `data_zoom_out_start`, `data_zoom_out_stop` | | Shorthand for continuous actions that call the service defined by the `service` parameter, with the data provided in this argument. Internally, this is just translated into the longer-form `actions_[action]_start` and `actions_[action]_stop`. If both `actions_X_*` and `data_X_*` are specified, `actions_X_*` takes priority. This is compatible with [AlexxIT's WebRTC Card PTZ configuration](https://github.com/AlexxIT/WebRTC/wiki/PTZ-Config-Examples). Both a `_start` and `_stop` variety must be provided for an action to be usable. |
@@ -232,7 +232,7 @@ cameras:
? [action] ? [action]
``` ```
`[action]` is any [call-service](../actions/stock/README.md?id=call-service) action. `[action]` is any [perform-action](../actions/stock/README.md?id=perform-action) action.
## `triggers` ## `triggers`
@@ -382,22 +382,22 @@ cameras:
r2c_delay_between_calls_seconds: 0.5 r2c_delay_between_calls_seconds: 0.5
# Relative action (only `left` shown) # Relative action (only `left` shown)
actions_left: actions_left:
action: call-service action: perform-action
service: service.of_your_choice perform_action: service.of_your_choice
data: data:
device: '048123' device: '048123'
cmd: left cmd: left
# Continuous action (only `right` shown) # Continuous action (only `right` shown)
actions_right_start: actions_right_start:
action: call-service action: perform-action
service: service.of_your_choice perform_action: service.of_your_choice
data: data:
device: '048123' device: '048123'
cmd: right cmd: right
phase: start phase: start
actions_right_stop: actions_right_stop:
action: call-service action: perform-action
service: service.of_your_choice perform_action: service.of_your_choice
data: data:
device: '048123' device: '048123'
phase: stop phase: stop
@@ -419,8 +419,8 @@ cameras:
presets: presets:
# Preset using long form. # Preset using long form.
armchair: armchair:
action: call-service action: perform-action
service: service.of_your_choice perform_action: service.of_your_choice
data: data:
device: '048123' device: '048123'
cmd: preset cmd: preset
+4 -4
View File
@@ -205,7 +205,7 @@ elements:
right: 25px right: 25px
bottom: 50px bottom: 50px
tap_action: tap_action:
action: call-service action: perform-action
service: amcrest.ptz_control service: amcrest.ptz_control
data: data:
entity_id: camera.kitchen entity_id: camera.kitchen
@@ -252,9 +252,9 @@ elements:
style: style:
color: red color: red
tap_action: tap_action:
action: call-service action: perform-action
service: homeassistant.toggle perform_action: homeassistant.toggle
data: target:
entity_id: siren.siren entity_id: siren.siren
conditions: conditions:
- condition: triggered - condition: triggered
+2 -2
View File
@@ -16,7 +16,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@cycjimmy/jsmpeg-player": "^6.0.5", "@cycjimmy/jsmpeg-player": "^6.0.5",
"@dermotduffy/custom-card-helpers": "^1.9.0", "@dermotduffy/custom-card-helpers": "^1.9.1",
"@dermotduffy/panzoom": "^4.5.1", "@dermotduffy/panzoom": "^4.5.1",
"@egjs/hammerjs": "^2.0.17", "@egjs/hammerjs": "^2.0.17",
"@graphiteds/core": "^1.9.21", "@graphiteds/core": "^1.9.21",
@@ -29,7 +29,7 @@
"date-fns-tz": "^3.1.3", "date-fns-tz": "^3.1.3",
"embla-carousel": "^8.1.4", "embla-carousel": "^8.1.4",
"embla-carousel-wheel-gestures": "^8.0.1", "embla-carousel-wheel-gestures": "^8.0.1",
"home-assistant-js-websocket": "^8.2.0", "home-assistant-js-websocket": "^9.4.0",
"keycharm": "^0.4.0", "keycharm": "^0.4.0",
"lit": "^3.1.4", "lit": "^3.1.4",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
-1
View File
@@ -101,7 +101,6 @@ const config = {
moduleContext: { moduleContext: {
'./node_modules/@formatjs/intl-utils/lib/src/diff.js': 'window', './node_modules/@formatjs/intl-utils/lib/src/diff.js': 'window',
'./node_modules/@formatjs/intl-utils/lib/src/resolve-locale.js': 'window', './node_modules/@formatjs/intl-utils/lib/src/resolve-locale.js': 'window',
'./node_modules/flatpickr/dist/esm/index.js': 'window',
}, },
}; };
+1 -1
View File
@@ -131,7 +131,7 @@ export class FrigateCardImage extends LitElement implements FrigateCardMediaPlay
this._cachedValueController?.clearValue(); this._cachedValueController?.clearValue();
return true; return true;
} }
return false; return !this.hasUpdated;
} }
return true; return true;
} }
+5 -3
View File
@@ -229,12 +229,14 @@ export class FrigateCardSubmenuSelect extends LitElement {
title: title || option, title: title || option,
...((entityID.startsWith('select.') || entityID.startsWith('input_select.')) && { ...((entityID.startsWith('select.') || entityID.startsWith('input_select.')) && {
tap_action: { tap_action: {
action: 'call-service', action: 'perform-action',
service: entityID.startsWith('select.') perform_action: entityID.startsWith('select.')
? 'select.select_option' ? 'select.select_option'
: 'input_select.select_option', : 'input_select.select_option',
data: { target: {
entity_id: entityID, entity_id: entityID,
},
data: {
option: option, option: option,
}, },
}, },
+29
View File
@@ -446,6 +446,21 @@ const conditionToConditionsTransform = (data: unknown): boolean => {
return false; return false;
}; };
const callServiceToPerformActionTransform = (data: unknown): boolean => {
if (
typeof data !== 'object' ||
!data ||
data['action'] !== 'call-service' ||
typeof data['service'] !== 'string'
) {
return false;
}
data['action'] = 'perform-action';
data['perform_action'] = data['service'];
delete data['service'];
return true;
};
/** /**
* Transform service_data -> data * Transform service_data -> data
* See: https://github.com/dermotduffy/frigate-hass-card/issues/1103 * See: https://github.com/dermotduffy/frigate-hass-card/issues/1103
@@ -849,4 +864,18 @@ const UPGRADES = [
}), }),
deleteWithOverrides('live.controls.title'), deleteWithOverrides('live.controls.title'),
deleteWithOverrides('media_viewer.controls.title'), deleteWithOverrides('media_viewer.controls.title'),
// Upgrade call-service calls throughout the card config. They could show up
// attached to any element, any automation, or any card/view action (i.e. very
// broadly across the config), so it's challenging to better target this
// upgrade. As written, this will convert things that look like call-service
// calls recurseively throughout the whole card config, but this could
// conceivably be an overreach if (e.g.) some totally unrelated object has {
// action: 'call-service', service: '<any string>' } that means something
// different.
(data: unknown): boolean => {
return upgradeObjectRecursively(callServiceToPerformActionTransform)(
typeof data === 'object' && data ? (data as RawFrigateCardConfig) : {},
);
},
]; ];
+47 -21
View File
@@ -4,9 +4,11 @@ import {
MoreInfoActionConfig, MoreInfoActionConfig,
NavigateActionConfig, NavigateActionConfig,
NoActionConfig, NoActionConfig,
PerformActionActionConfig,
ToggleActionConfig, ToggleActionConfig,
UrlActionConfig, UrlActionConfig,
} from '@dermotduffy/custom-card-helpers'; } from '@dermotduffy/custom-card-helpers';
import { HassServiceTarget } from 'home-assistant-js-websocket';
import { z } from 'zod'; import { z } from 'zod';
import { MEDIA_CHUNK_SIZE_DEFAULT, MEDIA_CHUNK_SIZE_MAX } from '../const.js'; import { MEDIA_CHUNK_SIZE_DEFAULT, MEDIA_CHUNK_SIZE_MAX } from '../const.js';
import { capabilityKeys } from '../types.js'; import { capabilityKeys } from '../types.js';
@@ -168,6 +170,28 @@ const toggleActionSchema = schemaForType<
}), }),
); );
const targetSchema = schemaForType<HassServiceTarget>()(
z.object({
entity_id: z.string().optional(),
device_id: z.string().optional(),
area_id: z.string().optional(),
}),
);
const performActionActionSchema = schemaForType<
PerformActionActionConfig & ExtendedConfirmationRestrictionConfig
>()(
actionBaseSchema.extend({
action: z.literal('perform-action'),
perform_action: z.string(),
data: z.object({}).passthrough().optional(),
target: targetSchema.optional(),
}),
);
// Note: call-service is deprecated and will eventually go away. Please use
// perform-action instead.
// See: https://www.home-assistant.io/blog/2024/08/07/release-20248/#goodbye-service-calls-hello-actions-
const callServiceActionSchema = schemaForType< const callServiceActionSchema = schemaForType<
CallServiceActionConfig & ExtendedConfirmationRestrictionConfig CallServiceActionConfig & ExtendedConfirmationRestrictionConfig
>()( >()(
@@ -175,6 +199,7 @@ const callServiceActionSchema = schemaForType<
action: z.literal('call-service'), action: z.literal('call-service'),
service: z.string(), service: z.string(),
data: z.object({}).passthrough().optional(), data: z.object({}).passthrough().optional(),
target: targetSchema.optional(),
}), }),
); );
@@ -391,6 +416,7 @@ export type FrigateCardCustomAction = z.infer<typeof frigateCardCustomActionSche
export const actionSchema = z.union([ export const actionSchema = z.union([
toggleActionSchema, toggleActionSchema,
callServiceActionSchema, callServiceActionSchema,
performActionActionSchema,
navigateActionSchema, navigateActionSchema,
urlActionSchema, urlActionSchema,
moreInfoActionSchema, moreInfoActionSchema,
@@ -814,8 +840,8 @@ const dataPTZFormatToFullFormat = function (suffix: string): (data: unknown) =>
const name = match?.[1]; const name = match?.[1];
if (name && !(`${suffix}${name}` in data)) { if (name && !(`${suffix}${name}` in data)) {
out[`${suffix}${name}`] = { out[`${suffix}${name}`] = {
action: 'call-service', action: 'perform-action',
service: data['service'], perform_action: data['service'],
data: data[key], data: data[key],
}; };
delete out[key]; delete out[key];
@@ -830,29 +856,29 @@ const ptzCameraConfigSchema = z.preprocess(
dataPTZFormatToFullFormat('actions_'), dataPTZFormatToFullFormat('actions_'),
z z
.object({ .object({
actions_left: callServiceActionSchema.optional(), actions_left: performActionActionSchema.optional(),
actions_left_start: callServiceActionSchema.optional(), actions_left_start: performActionActionSchema.optional(),
actions_left_stop: callServiceActionSchema.optional(), actions_left_stop: performActionActionSchema.optional(),
actions_right: callServiceActionSchema.optional(), actions_right: performActionActionSchema.optional(),
actions_right_start: callServiceActionSchema.optional(), actions_right_start: performActionActionSchema.optional(),
actions_right_stop: callServiceActionSchema.optional(), actions_right_stop: performActionActionSchema.optional(),
actions_up: callServiceActionSchema.optional(), actions_up: performActionActionSchema.optional(),
actions_up_start: callServiceActionSchema.optional(), actions_up_start: performActionActionSchema.optional(),
actions_up_stop: callServiceActionSchema.optional(), actions_up_stop: performActionActionSchema.optional(),
actions_down: callServiceActionSchema.optional(), actions_down: performActionActionSchema.optional(),
actions_down_start: callServiceActionSchema.optional(), actions_down_start: performActionActionSchema.optional(),
actions_down_stop: callServiceActionSchema.optional(), actions_down_stop: performActionActionSchema.optional(),
actions_zoom_in: callServiceActionSchema.optional(), actions_zoom_in: performActionActionSchema.optional(),
actions_zoom_in_start: callServiceActionSchema.optional(), actions_zoom_in_start: performActionActionSchema.optional(),
actions_zoom_in_stop: callServiceActionSchema.optional(), actions_zoom_in_stop: performActionActionSchema.optional(),
actions_zoom_out: callServiceActionSchema.optional(), actions_zoom_out: performActionActionSchema.optional(),
actions_zoom_out_start: callServiceActionSchema.optional(), actions_zoom_out_start: performActionActionSchema.optional(),
actions_zoom_out_stop: callServiceActionSchema.optional(), actions_zoom_out_stop: performActionActionSchema.optional(),
// The number of seconds between subsequent relative calls when converting a // The number of seconds between subsequent relative calls when converting a
// relative request into a continuous request. // relative request into a continuous request.
@@ -870,7 +896,7 @@ const ptzCameraConfigSchema = z.preprocess(
.preprocess( .preprocess(
dataPTZFormatToFullFormat(''), dataPTZFormatToFullFormat(''),
z.union([ z.union([
z.record(callServiceActionSchema), z.record(performActionActionSchema),
// This is used by the data_ style of action. // This is used by the data_ style of action.
z.object({ service: z.string().optional() }), z.object({ service: z.string().optional() }),
+2 -2
View File
@@ -1120,8 +1120,8 @@ describe('CameraManager', async () => {
const hass = createHASS(); const hass = createHASS();
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(hass); vi.mocked(api.getHASSManager().getHASS).mockReturnValue(hass);
const action = { const action = {
action: 'call-service' as const, action: 'perform-action' as const,
service: 'service', perform_action: 'action',
}; };
const manager = createCameraManager(api, engine, [ const manager = createCameraManager(api, engine, [
{ {
+4 -4
View File
@@ -1,15 +1,15 @@
import { describe, expect, it, vi } from 'vitest'; import { describe, expect, it } from 'vitest';
import { import {
getConfiguredPTZAction, getConfiguredPTZAction,
getConfiguredPTZMovementType, getConfiguredPTZMovementType,
getPTZCapabilitiesFromCameraConfig, getPTZCapabilitiesFromCameraConfig,
} from '../../../src/camera-manager/utils/ptz'; } from '../../../src/camera-manager/utils/ptz';
import { createCameraConfig } from '../../test-utils';
import { PTZAction } from '../../../src/config/ptz'; import { PTZAction } from '../../../src/config/ptz';
import { createCameraConfig } from '../../test-utils';
const action = { const action = {
action: 'call-service' as const, action: 'perform-action' as const,
service: 'service', perform_action: 'action',
data: { data: {
device: '048123', device: '048123',
cmd: 'preset', cmd: 'preset',
+98 -34
View File
@@ -21,6 +21,7 @@ import {
import { PTZControlAction } from '../../src/config/ptz'; import { PTZControlAction } from '../../src/config/ptz';
import { import {
Actions, Actions,
PerformActionActionConfig,
RawFrigateCardConfig, RawFrigateCardConfig,
frigateCardConfigSchema, frigateCardConfigSchema,
} from '../../src/config/types'; } from '../../src/config/types';
@@ -752,8 +753,8 @@ describe('should handle version specific upgrades', () => {
color: 'white', color: 'white',
}, },
tap_action: { tap_action: {
action: 'call-service', action: 'perform-action',
service: 'notify.persistent_notification', perform_action: 'notify.persistent_notification',
data: { data: {
message: 'Hello 1', message: 'Hello 1',
}, },
@@ -773,15 +774,15 @@ describe('should handle version specific upgrades', () => {
view: { view: {
actions: { actions: {
double_tap_action: { double_tap_action: {
action: 'call-service', action: 'perform-action',
service: 'notify.persistent_notification', perform_action: 'notify.persistent_notification',
data: { data: {
message: 'Hello 2', message: 'Hello 2',
}, },
}, },
hold_action: { hold_action: {
action: 'call-service', action: 'perform-action',
service: 'notify.persistent_notification', perform_action: 'notify.persistent_notification',
data: { data: {
message: 'Hello 3', message: 'Hello 3',
}, },
@@ -843,11 +844,11 @@ describe('should handle version specific upgrades', () => {
cameras_global: { cameras_global: {
ptz: { ptz: {
actions_up: { actions_up: {
action: 'call-service', action: 'perform-action',
data: { data: {
message: 'Hello 1', message: 'Hello 1',
}, },
service: 'notify.persistent_notification', perform_action: 'notify.persistent_notification',
}, },
}, },
}, },
@@ -907,11 +908,11 @@ describe('should handle version specific upgrades', () => {
cameras_global: { cameras_global: {
ptz: { ptz: {
actions_up: { actions_up: {
action: 'call-service', action: 'perform-action',
data: { data: {
message: 'Hello 1', message: 'Hello 1',
}, },
service: 'notify.persistent_notification', perform_action: 'notify.persistent_notification',
}, },
}, },
}, },
@@ -1000,11 +1001,11 @@ describe('should handle version specific upgrades', () => {
cameras_global: { cameras_global: {
ptz: { ptz: {
actions_up: { actions_up: {
action: 'call-service', action: 'perform-action',
data: { data: {
message: 'Hello 1', message: 'Hello 1',
}, },
service: 'notify.persistent_notification', perform_action: 'notify.persistent_notification',
}, },
}, },
}, },
@@ -1073,11 +1074,11 @@ describe('should handle version specific upgrades', () => {
cameras_global: { cameras_global: {
ptz: { ptz: {
actions_up: { actions_up: {
action: 'call-service', action: 'perform-action',
data: { data: {
message: 'Hello 1', message: 'Hello 1',
}, },
service: 'notify.persistent_notification', perform_action: 'notify.persistent_notification',
}, },
}, },
}, },
@@ -1160,11 +1161,11 @@ describe('should handle version specific upgrades', () => {
cameras_global: { cameras_global: {
ptz: { ptz: {
actions_up: { actions_up: {
action: 'call-service', action: 'perform-action',
data: { data: {
message: 'Hello 1', message: 'Hello 1',
}, },
service: 'notify.persistent_notification', perform_action: 'notify.persistent_notification',
}, },
}, },
}, },
@@ -1237,11 +1238,11 @@ describe('should handle version specific upgrades', () => {
cameras_global: { cameras_global: {
ptz: { ptz: {
actions_up: { actions_up: {
action: 'call-service', action: 'perform-action',
data: { data: {
message: 'Original', message: 'Original',
}, },
service: 'notify.persistent_notification', perform_action: 'notify.persistent_notification',
}, },
}, },
}, },
@@ -2855,7 +2856,7 @@ describe('should handle version specific upgrades', () => {
}); });
describe('split live.ptz', () => { describe('split live.ptz', () => {
const getAction = (action: PTZControlAction): CallServiceActionConfig => ({ const getActionBefore = (action: PTZControlAction): CallServiceActionConfig => ({
action: 'call-service', action: 'call-service',
service: 'service', service: 'service',
data: { data: {
@@ -2863,8 +2864,16 @@ describe('should handle version specific upgrades', () => {
}, },
}); });
const getActionAfter = (action: PTZControlAction): PerformActionActionConfig => ({
action: 'perform-action',
perform_action: 'service',
data: {
arg: action,
},
});
const getTapAction = (action: PTZControlAction): Actions => ({ const getTapAction = (action: PTZControlAction): Actions => ({
tap_action: getAction(action), tap_action: getActionBefore(action),
}); });
it('with action_ format', () => { it('with action_ format', () => {
@@ -2882,7 +2891,7 @@ describe('should handle version specific upgrades', () => {
actions_zoom_out: getTapAction('zoom_out'), actions_zoom_out: getTapAction('zoom_out'),
actions_home: { actions_home: {
...getTapAction('home'), ...getTapAction('home'),
double_tap_action: getAction('up'), double_tap_action: getActionBefore('up'),
}, },
mode: 'auto' as const, mode: 'auto' as const,
@@ -2907,14 +2916,14 @@ describe('should handle version specific upgrades', () => {
cameras: [{}], cameras: [{}],
cameras_global: { cameras_global: {
ptz: { ptz: {
actions_left: getAction('left'), actions_left: getActionAfter('left'),
actions_right: getAction('right'), actions_right: getActionAfter('right'),
actions_up: getAction('up'), actions_up: getActionAfter('up'),
actions_down: getAction('down'), actions_down: getActionAfter('down'),
actions_zoom_in: getAction('zoom_in'), actions_zoom_in: getActionAfter('zoom_in'),
actions_zoom_out: getAction('zoom_out'), actions_zoom_out: getActionAfter('zoom_out'),
presets: { presets: {
home: getAction('home'), home: getActionAfter('home'),
}, },
}, },
}, },
@@ -3052,7 +3061,7 @@ describe('should handle version specific upgrades', () => {
controls: { controls: {
ptz: { ptz: {
actions_up: { actions_up: {
double_tap_action: getAction('up'), double_tap_action: getActionBefore('up'),
}, },
actions_down: getTapAction('down'), actions_down: getTapAction('down'),
}, },
@@ -3065,7 +3074,7 @@ describe('should handle version specific upgrades', () => {
expect.objectContaining({ expect.objectContaining({
cameras_global: { cameras_global: {
ptz: { ptz: {
actions_down: getAction('down'), actions_down: getActionAfter('down'),
}, },
}, },
}), }),
@@ -3081,8 +3090,8 @@ describe('should handle version specific upgrades', () => {
controls: { controls: {
ptz: { ptz: {
presets: { presets: {
home: getAction('left'), home: getActionBefore('left'),
other: getAction('right'), other: getActionBefore('right'),
}, },
actions_home: getTapAction('home'), actions_home: getTapAction('home'),
}, },
@@ -3096,8 +3105,8 @@ describe('should handle version specific upgrades', () => {
cameras_global: { cameras_global: {
ptz: { ptz: {
presets: { presets: {
other: getAction('right'), other: getActionAfter('right'),
home: getAction('home'), home: getActionAfter('home'),
}, },
}, },
}, },
@@ -3303,5 +3312,60 @@ describe('should handle version specific upgrades', () => {
}); });
}); });
}); });
it('call-service -> perform-action', () => {
const config = {
type: 'custom:frigate-card',
cameras: [{ camera_entity: 'camera.office' }],
elements: [
{
type: 'icon',
icon: 'mdi:cow',
tap_action: {
action: 'call-service',
service: 'foo',
},
},
],
view: {
actions: {
double_tap_action: {
action: 'call-service',
service: 'bar',
data: {
key: 'value',
},
},
},
},
};
expect(upgradeConfig(config)).toBeTruthy();
expect(config).toEqual({
type: 'custom:frigate-card',
cameras: [{ camera_entity: 'camera.office' }],
elements: [
{
type: 'icon',
icon: 'mdi:cow',
tap_action: {
action: 'perform-action',
perform_action: 'foo',
},
},
],
view: {
actions: {
double_tap_action: {
action: 'perform-action',
perform_action: 'bar',
data: {
key: 'value',
},
},
},
},
});
postUpgradeChecks(config);
});
}); });
}); });
+10 -10
View File
@@ -423,8 +423,8 @@ describe('should convert webrtc card PTZ to Frigate card PTZ', () => {
expect.objectContaining({ expect.objectContaining({
ptz: expect.objectContaining({ ptz: expect.objectContaining({
[`actions_${action}`]: { [`actions_${action}`]: {
action: 'call-service', action: 'perform-action',
service: 'foo', perform_action: 'foo',
data: { data: {
device: '048123', device: '048123',
cmd: action, cmd: action,
@@ -465,8 +465,8 @@ describe('should convert webrtc card PTZ to Frigate card PTZ', () => {
expect.objectContaining({ expect.objectContaining({
ptz: expect.objectContaining({ ptz: expect.objectContaining({
[`actions_${action}_start`]: { [`actions_${action}_start`]: {
action: 'call-service', action: 'perform-action',
service: 'foo', perform_action: 'foo',
data: { data: {
device: '048123', device: '048123',
cmd: action, cmd: action,
@@ -474,8 +474,8 @@ describe('should convert webrtc card PTZ to Frigate card PTZ', () => {
}, },
}, },
[`actions_${action}_stop`]: { [`actions_${action}_stop`]: {
action: 'call-service', action: 'perform-action',
service: 'foo', perform_action: 'foo',
data: { data: {
device: '048123', device: '048123',
cmd: action, cmd: action,
@@ -511,16 +511,16 @@ describe('should convert webrtc card PTZ to Frigate card PTZ', () => {
ptz: expect.objectContaining({ ptz: expect.objectContaining({
presets: { presets: {
home: { home: {
action: 'call-service', action: 'perform-action',
service: 'service_inner', perform_action: 'service_inner',
data: { data: {
device: '048123', device: '048123',
cmd: 'home', cmd: 'home',
}, },
}, },
another: { another: {
action: 'call-service', action: 'perform-action',
service: 'service_inner', perform_action: 'service_inner',
data: { data: {
device: '048123', device: '048123',
cmd: 'another', cmd: 'another',
+11 -18
View File
@@ -603,18 +603,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@dermotduffy/custom-card-helpers@npm:^1.9.0": "@dermotduffy/custom-card-helpers@npm:^1.9.1":
version: 1.9.0 version: 1.9.1
resolution: "@dermotduffy/custom-card-helpers@npm:1.9.0" resolution: "@dermotduffy/custom-card-helpers@npm:1.9.1"
dependencies: dependencies:
"@formatjs/intl-utils": "npm:^3.8.4" "@formatjs/intl-utils": "npm:^3.8.4"
home-assistant-js-websocket: "npm:^6.0.1" home-assistant-js-websocket: "npm:^9.4.0"
intl-messageformat: "npm:^9.11.1" intl-messageformat: "npm:^9.11.1"
lit: "npm:^2.1.1" lit: "npm:^2.1.1"
rollup: "npm:^2.63.0" rollup: "npm:^2.63.0"
superstruct: "npm:^0.15.3" superstruct: "npm:^0.15.3"
typescript: "npm:^4.5.4" typescript: "npm:^4.5.4"
checksum: 10c0/312a31a9abe6ddc37109ffe2725df30e72213b4fa2cceacbb4bdb3712b46a73e49b9fb7fab69dc521050538e882d7180e3d7434011f637a34f2bc9098b880e97 checksum: 10c0/e42073e00682195c3d253f9b36dc091f7329a5e2a5cdd395bf0988ec2c8585db887ef772975894e4a1126653a2f3cb348a4ea4c1fbe8ce739002fd0f8461da40
languageName: node languageName: node
linkType: hard linkType: hard
@@ -4075,7 +4075,7 @@ __metadata:
"@babel/plugin-proposal-decorators": "npm:^7.24.7" "@babel/plugin-proposal-decorators": "npm:^7.24.7"
"@babel/plugin-transform-class-properties": "npm:^7.24.7" "@babel/plugin-transform-class-properties": "npm:^7.24.7"
"@cycjimmy/jsmpeg-player": "npm:^6.0.5" "@cycjimmy/jsmpeg-player": "npm:^6.0.5"
"@dermotduffy/custom-card-helpers": "npm:^1.9.0" "@dermotduffy/custom-card-helpers": "npm:^1.9.1"
"@dermotduffy/panzoom": "npm:^4.5.1" "@dermotduffy/panzoom": "npm:^4.5.1"
"@egjs/hammerjs": "npm:^2.0.17" "@egjs/hammerjs": "npm:^2.0.17"
"@graphiteds/core": "npm:^1.9.21" "@graphiteds/core": "npm:^1.9.21"
@@ -4106,7 +4106,7 @@ __metadata:
eslint-config-airbnb-base: "npm:^15.0.0" eslint-config-airbnb-base: "npm:^15.0.0"
eslint-config-prettier: "npm:^9.1.0" eslint-config-prettier: "npm:^9.1.0"
eslint-plugin-import: "npm:^2.29.1" eslint-plugin-import: "npm:^2.29.1"
home-assistant-js-websocket: "npm:^8.2.0" home-assistant-js-websocket: "npm:^9.4.0"
jsdom: "npm:^21.1.2" jsdom: "npm:^21.1.2"
keycharm: "npm:^0.4.0" keycharm: "npm:^0.4.0"
lit: "npm:^3.1.4" lit: "npm:^3.1.4"
@@ -4559,17 +4559,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"home-assistant-js-websocket@npm:^6.0.1": "home-assistant-js-websocket@npm:^9.4.0":
version: 6.1.1 version: 9.4.0
resolution: "home-assistant-js-websocket@npm:6.1.1" resolution: "home-assistant-js-websocket@npm:9.4.0"
checksum: 10c0/fe00b8d7a681d39b31ac5078b79fa1ef67cccd37df2c7ec31b1ad9ded33c899570b453c9439db71c3bda24df6d0c1e3d9d040583b0717f4630476ab528120a9f checksum: 10c0/9c8a5ff903648ebe7856f62a58af53e3d89dd24a30c600a2145ed518cd5f5f2218345048cfc42bdc3ee5d6f2994da3948765c77b6012ed56cf4546173a1a1de1
languageName: node
linkType: hard
"home-assistant-js-websocket@npm:^8.2.0":
version: 8.2.0
resolution: "home-assistant-js-websocket@npm:8.2.0"
checksum: 10c0/9a1f1f5cb39ce0629653347d5ae31a14f14742596c15fea7bd72f4c0ee4ce4613b1172b4f4df52f293b4e5aeb3b5ead75f395d5fe5c4785193e7d00f8355ef5a
languageName: node languageName: node
linkType: hard linkType: hard