Add support for perform-action calls.
This commit is contained in:
@@ -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
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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",
|
||||||
|
|||||||
@@ -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',
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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
@@ -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() }),
|
||||||
|
|||||||
@@ -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, [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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',
|
||||||
},
|
},
|
||||||
@@ -812,7 +813,7 @@ describe('should handle version specific upgrades', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('should move PTZ elements to live', () => {
|
describe('should move PTZ elements to live', () => {
|
||||||
it('case with 1 element', () => {
|
it('xxx case with 1 element', () => {
|
||||||
const config = {
|
const config = {
|
||||||
type: 'custom:frigate-card',
|
type: 'custom:frigate-card',
|
||||||
cameras: [{ camera_entity: 'camera.office' }],
|
cameras: [{ camera_entity: 'camera.office' }],
|
||||||
@@ -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
@@ -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',
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user