Allow device actions to specify extra fields (#4002)

* Allow device actions to specify extra fields

* Typing etc.

* Use deviceAutomationsEqual to compare automations
This commit is contained in:
Erik Montnemery 2019-10-14 21:27:51 +02:00 committed by Bram Kragten
parent ce6a97d065
commit 555b746f4b
5 changed files with 98 additions and 8 deletions

View File

@ -39,6 +39,15 @@ export const fetchDeviceTriggers = (hass: HomeAssistant, deviceId: string) =>
device_id: deviceId, device_id: deviceId,
}); });
export const fetchDeviceActionCapabilities = (
hass: HomeAssistant,
action: DeviceAction
) =>
hass.callWS<DeviceAction[]>({
type: "device_automation/action/capabilities",
action,
});
export const fetchDeviceConditionCapabilities = ( export const fetchDeviceConditionCapabilities = (
hass: HomeAssistant, hass: HomeAssistant,
condition: DeviceCondition condition: DeviceCondition
@ -57,7 +66,7 @@ export const fetchDeviceTriggerCapabilities = (
trigger, trigger,
}); });
const whitelist = ["above", "below", "for"]; const whitelist = ["above", "below", "code", "for"];
export const deviceAutomationsEqual = ( export const deviceAutomationsEqual = (
a: DeviceAutomation, a: DeviceAutomation,

View File

@ -83,7 +83,7 @@ export default class DeviceCondition extends Component<any, any> {
} }
public componentDidUpdate(prevProps) { public componentDidUpdate(prevProps) {
if (prevProps.condition !== this.props.condition) { if (!deviceAutomationsEqual(prevProps.condition, this.props.condition)) {
this._getCapabilities(); this._getCapabilities();
} }
} }

View File

@ -2,8 +2,14 @@ import { h, Component } from "preact";
import "../../../../components/device/ha-device-picker"; import "../../../../components/device/ha-device-picker";
import "../../../../components/device/ha-device-action-picker"; import "../../../../components/device/ha-device-action-picker";
import { HomeAssistant } from "../../../../types"; import "../../../../components/ha-form";
import {
fetchDeviceActionCapabilities,
deviceAutomationsEqual,
} from "../../../../data/device_automation";
import { DeviceAction } from "../../../../data/script"; import { DeviceAction } from "../../../../data/script";
import { HomeAssistant } from "../../../../types";
export default class DeviceActionEditor extends Component< export default class DeviceActionEditor extends Component<
{ {
@ -14,6 +20,7 @@ export default class DeviceActionEditor extends Component<
}, },
{ {
device_id: string | undefined; device_id: string | undefined;
capabilities: any | undefined;
} }
> { > {
public static defaultConfig: DeviceAction = { public static defaultConfig: DeviceAction = {
@ -22,16 +29,26 @@ export default class DeviceActionEditor extends Component<
entity_id: "", entity_id: "",
}; };
private _origAction;
constructor() { constructor() {
super(); super();
this.devicePicked = this.devicePicked.bind(this); this.devicePicked = this.devicePicked.bind(this);
this.deviceActionPicked = this.deviceActionPicked.bind(this); this.deviceActionPicked = this.deviceActionPicked.bind(this);
this.state = { device_id: undefined }; this._extraFieldsChanged = this._extraFieldsChanged.bind(this);
this.state = { device_id: undefined, capabilities: undefined };
} }
public render() { public render() {
const { action, hass } = this.props; const { action, hass } = this.props;
const deviceId = this.state.device_id || action.device_id; const deviceId = this.state.device_id || action.device_id;
const capabilities = this.state.capabilities;
const extraFieldsData =
capabilities && capabilities.extra_fields
? capabilities.extra_fields.map((item) => {
return { [item.name]: this.props.action[item.name] };
})
: undefined;
return ( return (
<div> <div>
@ -48,16 +65,77 @@ export default class DeviceActionEditor extends Component<
hass={hass} hass={hass}
label="Action" label="Action"
/> />
{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.action) {
this._origAction = this.props.action;
}
}
public componentDidUpdate(prevProps) {
if (!deviceAutomationsEqual(prevProps.action, this.props.action)) {
this._getCapabilities();
}
}
private devicePicked(ev) { private devicePicked(ev) {
this.setState({ device_id: ev.target.value }); this.setState({ ...this.state, device_id: ev.target.value });
} }
private deviceActionPicked(ev) { private deviceActionPicked(ev) {
const deviceAction = { ...ev.target.value }; let deviceAction = ev.target.value;
if (
this._origAction &&
deviceAutomationsEqual(this._origAction, deviceAction)
) {
deviceAction = this._origAction;
}
this.props.onChange(this.props.index, deviceAction); this.props.onChange(this.props.index, deviceAction);
} }
private async _getCapabilities() {
const action = this.props.action;
const capabilities = action.domain
? await fetchDeviceActionCapabilities(this.props.hass, action)
: 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.action,
[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.actions.type.device_id.extra_fields.${
schema.name
}`
) || schema.name;
}
} }

View File

@ -84,7 +84,7 @@ export default class DeviceTrigger extends Component<any, any> {
} }
public componentDidUpdate(prevProps) { public componentDidUpdate(prevProps) {
if (prevProps.trigger !== this.props.trigger) { if (!deviceAutomationsEqual(prevProps.trigger, this.props.trigger)) {
this._getCapabilities(); this._getCapabilities();
} }
} }

View File

@ -908,7 +908,10 @@
"service_data": "[%key:ui::panel::config::automation::editor::actions::type::service::service_data%]" "service_data": "[%key:ui::panel::config::automation::editor::actions::type::service::service_data%]"
}, },
"device_id": { "device_id": {
"label": "Device" "label": "Device",
"extra_fields": {
"code": "Code"
}
}, },
"scene": { "scene": {
"label": "Activate scene" "label": "Activate scene"