Merge pull request #3970 from emontnemery/automation_device_action_form

Allow device triggers to specify extra fields
This commit is contained in:
Bram Kragten 2019-10-09 21:48:57 +02:00 committed by GitHub
commit f1fabd09a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 122 additions and 10 deletions

View File

@ -36,8 +36,9 @@ class HaForm extends EventsMixin(PolymerElement) {
schema="[[item]]" schema="[[item]]"
error="[[_getValue(error, item)]]" error="[[_getValue(error, item)]]"
on-data-changed="_valueChanged" on-data-changed="_valueChanged"
compute-label="[[computeLabel]]"
compute-error="[[computeError]]" compute-error="[[computeError]]"
compute-label="[[computeLabel]]"
compute-suffix="[[computeSuffix]]"
></ha-form> ></ha-form>
</template> </template>
</template> </template>
@ -126,7 +127,9 @@ class HaForm extends EventsMixin(PolymerElement) {
required="[[schema.required]]" required="[[schema.required]]"
auto-validate="[[schema.required]]" auto-validate="[[schema.required]]"
error-message="Required" error-message="Required"
></paper-input> >
<span suffix="" slot="suffix">[[computeSuffix(schema)]]</span>
</paper-input>
</template> </template>
<template <template
@ -173,14 +176,24 @@ class HaForm extends EventsMixin(PolymerElement) {
schema: Object, schema: Object,
error: Object, error: Object,
// A function that will computes the label to be displayed for a given // A function that computes the label to be displayed for a given
// schema object. // schema object.
computeLabel: { computeLabel: {
type: Function, type: Function,
value: () => (schema) => schema && schema.name, value: () => (schema) => schema && schema.name,
}, },
// A function that will computes an error message to be displayed for a // A function that computes the suffix to be displayed for a given
// schema object.
computeSuffix: {
type: Function,
value: () => (schema) =>
schema &&
schema.description &&
schema.description.unit_of_measurement,
},
// A function that computes an error message to be displayed for a
// given error ID, and relevant schema object // given error ID, and relevant schema object
computeError: { computeError: {
type: Function, type: Function,

View File

@ -39,6 +39,17 @@ export const fetchDeviceTriggers = (hass: HomeAssistant, deviceId: string) =>
device_id: deviceId, device_id: deviceId,
}); });
export const fetchDeviceTriggerCapabilities = (
hass: HomeAssistant,
trigger: DeviceTrigger
) =>
hass.callWS<DeviceTrigger[]>({
type: "device_automation/trigger/capabilities",
trigger,
});
const whitelist = ["above", "below", "for"];
export const deviceAutomationsEqual = ( export const deviceAutomationsEqual = (
a: DeviceAutomation, a: DeviceAutomation,
b: DeviceAutomation b: DeviceAutomation
@ -48,11 +59,17 @@ export const deviceAutomationsEqual = (
} }
for (const property in a) { for (const property in a) {
if (whitelist.includes(property)) {
continue;
}
if (!Object.is(a[property], b[property])) { if (!Object.is(a[property], b[property])) {
return false; return false;
} }
} }
for (const property in b) { for (const property in b) {
if (whitelist.includes(property)) {
continue;
}
if (!Object.is(a[property], b[property])) { if (!Object.is(a[property], b[property])) {
return false; return false;
} }

View File

@ -24,6 +24,7 @@ declare global {
"mwc-button": any; "mwc-button": any;
"ha-device-trigger-picker": any; "ha-device-trigger-picker": any;
"ha-device-action-picker": any; "ha-device-action-picker": any;
"ha-form": any;
} }
} }
} }

View File

@ -2,29 +2,50 @@ import { h, Component } from "preact";
import "../../../../components/device/ha-device-picker"; import "../../../../components/device/ha-device-picker";
import "../../../../components/device/ha-device-trigger-picker"; import "../../../../components/device/ha-device-trigger-picker";
import "../../../../components/ha-form";
import {
fetchDeviceTriggerCapabilities,
deviceAutomationsEqual,
} from "../../../../data/device_automation";
export default class DeviceTrigger extends Component<any, any> { export default class DeviceTrigger extends Component<any, any> {
private _origTrigger;
constructor() { constructor() {
super(); super();
this.devicePicked = this.devicePicked.bind(this); this.devicePicked = this.devicePicked.bind(this);
this.deviceTriggerPicked = this.deviceTriggerPicked.bind(this); this.deviceTriggerPicked = this.deviceTriggerPicked.bind(this);
this.state = { device_id: undefined }; this._extraFieldsChanged = this._extraFieldsChanged.bind(this);
this.state = { device_id: undefined, capabilities: undefined };
} }
public devicePicked(ev) { public devicePicked(ev) {
this.setState({ device_id: ev.target.value }); this.setState({ ...this.state, device_id: ev.target.value });
} }
public deviceTriggerPicked(ev) { public deviceTriggerPicked(ev) {
const deviceTrigger = ev.target.value; let trigger = ev.target.value;
this.props.onChange(this.props.index, deviceTrigger); if (
this._origTrigger &&
deviceAutomationsEqual(this._origTrigger, trigger)
) {
trigger = this._origTrigger;
}
this.props.onChange(this.props.index, trigger);
} }
/* eslint-disable camelcase */ /* eslint-disable camelcase */
public render({ trigger, hass }, { device_id }) { public render({ trigger, hass }, { device_id, capabilities }) {
if (device_id === undefined) { if (device_id === undefined) {
device_id = trigger.device_id; device_id = trigger.device_id;
} }
const extraFieldsData =
capabilities && capabilities.extra_fields
? capabilities.extra_fields.map((item) => {
return { [item.name]: this.props.trigger[item.name] };
})
: undefined;
return ( return (
<div> <div>
@ -41,9 +62,64 @@ export default class DeviceTrigger extends Component<any, any> {
hass={hass} hass={hass}
label="Trigger" label="Trigger"
/> />
{extraFieldsData && (
<ha-form
data={Object.assign({}, ...extraFieldsData)}
onData-changed={this._extraFieldsChanged}
schema={this.state.capabilities.extra_fields}
computeLabel={this._extraFieldsComputeLabelCallback(hass.localize)}
/>
)}
</div> </div>
); );
} }
public componentDidMount() {
if (!this.state.capabilities) {
this._getCapabilities();
}
if (this.props.trigger) {
this._origTrigger = this.props.trigger;
}
}
public componentDidUpdate(prevProps) {
if (prevProps.trigger !== this.props.trigger) {
this._getCapabilities();
}
}
private async _getCapabilities() {
const trigger = this.props.trigger;
const capabilities = trigger.domain
? await fetchDeviceTriggerCapabilities(this.props.hass, trigger)
: null;
this.setState({ ...this.state, capabilities });
}
private _extraFieldsChanged(ev) {
if (!ev.detail.path) {
return;
}
const item = ev.detail.path.replace("data.", "");
const value = ev.detail.value || undefined;
this.props.onChange(this.props.index, {
...this.props.trigger,
[item]: value,
});
}
private _extraFieldsComputeLabelCallback(localize) {
// Returns a callback for ha-form to calculate labels per schema object
return (schema) =>
localize(
`ui.panel.config.automation.editor.triggers.type.device.extra_fields.${
schema.name
}`
) || schema.name;
}
} }
(DeviceTrigger as any).defaultConfig = { (DeviceTrigger as any).defaultConfig = {

View File

@ -737,7 +737,12 @@
"type_select": "Trigger type", "type_select": "Trigger type",
"type": { "type": {
"device": { "device": {
"label": "Device" "label": "Device",
"extra_fields": {
"above": "Above",
"below": "Below",
"for": "Duration"
}
}, },
"event": { "event": {
"label": "Event", "label": "Event",