Automation update (#382)
* Add missing triggers * Move onChangeEvent to generic util * Generate dev service worker in dev mode * Add actions * Lint * Add condition support * Add condition actions
This commit is contained in:
parent
77f934b738
commit
f02d42d79c
|
@ -1,6 +1,7 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import Trigger from './trigger';
|
||||
import Condition from './condition';
|
||||
import Script from './script';
|
||||
|
||||
export default class Automation extends Component {
|
||||
|
@ -9,6 +10,7 @@ export default class Automation extends Component {
|
|||
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.triggerChanged = this.triggerChanged.bind(this);
|
||||
this.conditionChanged = this.conditionChanged.bind(this);
|
||||
this.actionChanged = this.actionChanged.bind(this);
|
||||
}
|
||||
|
||||
|
@ -26,6 +28,13 @@ export default class Automation extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
conditionChanged(condition) {
|
||||
this.props.onChange({
|
||||
...this.props.automation,
|
||||
condition,
|
||||
});
|
||||
}
|
||||
|
||||
actionChanged(action) {
|
||||
this.props.onChange({
|
||||
...this.props.automation,
|
||||
|
@ -83,12 +92,7 @@ export default class Automation extends Component {
|
|||
Learn more about conditions.
|
||||
</a></p>
|
||||
</span>
|
||||
<paper-card>
|
||||
<div class='card-content'>
|
||||
Conditions are not supported yet.
|
||||
<pre>{JSON.stringify(condition, null, 2)}</pre>
|
||||
</div>
|
||||
</paper-card>
|
||||
<Condition condition={condition} onChange={this.conditionChanged} />
|
||||
</ha-config-section>}
|
||||
|
||||
<ha-config-section is-wide={isWide}>
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import NumericStateCondition from './numeric_state';
|
||||
import StateCondition from './state';
|
||||
import SunCondition from './sun';
|
||||
import TemplateCondition from './template';
|
||||
import TimeCondition from './time';
|
||||
import ZoneCondition from './zone';
|
||||
|
||||
const TYPES = {
|
||||
state: StateCondition,
|
||||
numeric_state: NumericStateCondition,
|
||||
sun: SunCondition,
|
||||
template: TemplateCondition,
|
||||
time: TimeCondition,
|
||||
zone: ZoneCondition,
|
||||
};
|
||||
|
||||
const OPTIONS = Object.keys(TYPES).sort();
|
||||
|
||||
export default class ConditionRow extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.typeChanged = this.typeChanged.bind(this);
|
||||
}
|
||||
|
||||
typeChanged(ev) {
|
||||
const type = ev.target.selectedItem.innerHTML;
|
||||
|
||||
if (type !== this.props.condition.condition) {
|
||||
this.props.onChange(this.props.index, {
|
||||
condition: type,
|
||||
...TYPES[type].defaultConfig
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render({ index, condition, onChange }) {
|
||||
const Comp = TYPES[condition.condition];
|
||||
const selected = OPTIONS.indexOf(condition.condition);
|
||||
|
||||
if (!Comp) {
|
||||
return (
|
||||
<div>
|
||||
Unsupported condition: {condition.condition}
|
||||
<pre>{JSON.stringify(condition, null, 2)}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<paper-dropdown-menu-light label="Condition Type" no-animations>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
selected={selected}
|
||||
oniron-select={this.typeChanged}
|
||||
>
|
||||
{OPTIONS.map(opt => <paper-item>{opt}</paper-item>)}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu-light>
|
||||
<Comp
|
||||
index={index}
|
||||
condition={condition}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import ConditionEdit from './condition_edit';
|
||||
|
||||
export default class ConditionRow extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onDelete = this.onDelete.bind(this);
|
||||
}
|
||||
|
||||
onDelete() {
|
||||
// eslint-disable-next-line
|
||||
if (confirm('Sure you want to delete?')) {
|
||||
this.props.onChange(this.props.index, null);
|
||||
}
|
||||
}
|
||||
|
||||
render(props) {
|
||||
return (
|
||||
<paper-card>
|
||||
<div class='card-menu'>
|
||||
<paper-menu-button
|
||||
no-animations
|
||||
horizontal-align="right"
|
||||
horizontal-offset="-5"
|
||||
vertical-offset="-5"
|
||||
>
|
||||
<paper-icon-button
|
||||
icon="mdi:dots-vertical"
|
||||
slot="dropdown-trigger"
|
||||
/>
|
||||
<paper-listbox slot="dropdown-content">
|
||||
<paper-item disabled>Duplicate</paper-item>
|
||||
<paper-item onTap={this.onDelete}>Delete</paper-item>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
</div>
|
||||
<div class='card-content'>
|
||||
<ConditionEdit {...props} />
|
||||
</div>
|
||||
</paper-card>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import ConditionRow from './condition_row';
|
||||
|
||||
export default class Condition extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.addCondition = this.addCondition.bind(this);
|
||||
this.conditionChanged = this.conditionChanged.bind(this);
|
||||
}
|
||||
|
||||
addCondition() {
|
||||
const condition = this.props.condition.concat({
|
||||
condition: 'state',
|
||||
});
|
||||
|
||||
this.props.onChange(condition);
|
||||
}
|
||||
|
||||
conditionChanged(index, newValue) {
|
||||
const condition = this.props.condition.concat();
|
||||
|
||||
if (newValue === null) {
|
||||
condition.splice(index, 1);
|
||||
} else {
|
||||
condition[index] = newValue;
|
||||
}
|
||||
|
||||
this.props.onChange(condition);
|
||||
}
|
||||
|
||||
render({ condition }) {
|
||||
return (
|
||||
<div class="triggers">
|
||||
{condition.map((cnd, idx) => (
|
||||
<ConditionRow
|
||||
index={idx}
|
||||
condition={cnd}
|
||||
onChange={this.conditionChanged}
|
||||
/>))}
|
||||
<paper-card>
|
||||
<div class='card-actions add-card'>
|
||||
<paper-button onTap={this.addCondition}>Add condition</paper-button>
|
||||
</div>
|
||||
</paper-card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class NumericStateCondition extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChangeEvent.bind(this, 'condition');
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
render({ condition }) {
|
||||
const { value_template, entity_id, below, above } = condition;
|
||||
return (
|
||||
<div>
|
||||
<paper-input
|
||||
label="Entity Id"
|
||||
name="entity_id"
|
||||
value={entity_id}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<paper-input
|
||||
label="Above"
|
||||
name="above"
|
||||
value={above}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<paper-input
|
||||
label="Below"
|
||||
name="below"
|
||||
value={below}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<paper-textarea
|
||||
label="Value template (optional)"
|
||||
name="value_template"
|
||||
value={value_template}
|
||||
onvalue-changed={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
NumericStateCondition.defaultConfig = {
|
||||
entity_id: '',
|
||||
above: '',
|
||||
below: '',
|
||||
value_template: '',
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class StateCondition extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChangeEvent.bind(this, 'condition');
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
render({ condition }) {
|
||||
const { entity_id, state } = condition;
|
||||
const cndFor = condition.for;
|
||||
return (
|
||||
<div>
|
||||
<paper-input
|
||||
label="Entity Id"
|
||||
name="entity_id"
|
||||
value={entity_id}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<paper-input
|
||||
label="State"
|
||||
name="state"
|
||||
value={state}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
{cndFor && <pre>For: {JSON.stringify(cndFor, null, 2)}</pre>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
StateCondition.defaultConfig = {
|
||||
entity_id: '',
|
||||
state: '',
|
||||
};
|
|
@ -0,0 +1,74 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class SunCondition extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChangeEvent.bind(this, 'condition');
|
||||
this.afterPicked = this.radioGroupPicked.bind(this, 'after');
|
||||
this.beforePicked = this.radioGroupPicked.bind(this, 'before');
|
||||
}
|
||||
|
||||
radioGroupPicked(key, ev) {
|
||||
const condition = { ...this.props.condition };
|
||||
|
||||
if (ev.target.selected) {
|
||||
condition[key] = ev.target.value;
|
||||
} else {
|
||||
delete condition[key];
|
||||
}
|
||||
|
||||
this.props.onChange(this.props.index, condition);
|
||||
}
|
||||
|
||||
render({ condition }) {
|
||||
/* eslint-disable camelcase */
|
||||
const { after, after_offset, before, before_offset } = condition;
|
||||
return (
|
||||
<div>
|
||||
<label id="beforelabel">Before:</label>
|
||||
<paper-radio-group
|
||||
allow-empty-selection
|
||||
selected={before}
|
||||
aria-labelledby="beforelabel"
|
||||
onpaper-radio-group-changed={this.beforePicked}
|
||||
>
|
||||
<paper-radio-button name="sunrise">Sunrise</paper-radio-button>
|
||||
<paper-radio-button name="sunset">Sunset</paper-radio-button>
|
||||
</paper-radio-group>
|
||||
|
||||
<paper-input
|
||||
label="Before offset (optional)"
|
||||
name="before_offset"
|
||||
value={before_offset}
|
||||
onChange={this.onChange}
|
||||
disabled={before === undefined}
|
||||
/>
|
||||
|
||||
<label id="afterlabel">After:</label>
|
||||
<paper-radio-group
|
||||
allow-empty-selection
|
||||
selected={after}
|
||||
aria-labelledby="afterlabel"
|
||||
onpaper-radio-group-changed={this.afterPicked}
|
||||
>
|
||||
<paper-radio-button name="sunrise">Sunrise</paper-radio-button>
|
||||
<paper-radio-button name="sunset">Sunset</paper-radio-button>
|
||||
</paper-radio-group>
|
||||
|
||||
<paper-input
|
||||
label="After offset (optional)"
|
||||
name="after_offset"
|
||||
value={after_offset}
|
||||
onChange={this.onChange}
|
||||
disabled={after === undefined}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SunCondition.defaultConfig = {
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class TemplateCondition extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChangeEvent.bind(this, 'condition');
|
||||
}
|
||||
|
||||
render({ condition }) {
|
||||
/* eslint-disable camelcase */
|
||||
const { value_template } = condition;
|
||||
return (
|
||||
<div>
|
||||
<paper-textarea
|
||||
label="Value Template"
|
||||
name="value_template"
|
||||
value={value_template}
|
||||
onvalue-changed={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TemplateCondition.defaultConfig = {
|
||||
value_template: '',
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class StateCondition extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChangeEvent.bind(this, 'condition');
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
render({ condition }) {
|
||||
const { after, before } = condition;
|
||||
return (
|
||||
<div>
|
||||
<paper-input
|
||||
label="After"
|
||||
name="after"
|
||||
value={after}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<paper-input
|
||||
label="Before"
|
||||
name="before"
|
||||
value={before}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
StateCondition.defaultConfig = {
|
||||
after: '',
|
||||
before: '',
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class ZoneCondition extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChangeEvent.bind(this, 'condition');
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
render({ condition }) {
|
||||
const { entity_id, zone } = condition;
|
||||
return (
|
||||
<div>
|
||||
<paper-input
|
||||
label="Entity Id"
|
||||
name="entity_id"
|
||||
value={entity_id}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<paper-input
|
||||
label="Zone entity id"
|
||||
name="zone"
|
||||
value={zone}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ZoneCondition.defaultConfig = {
|
||||
entity_id: '',
|
||||
zone: '',
|
||||
};
|
|
@ -38,7 +38,7 @@ export default class JSONTextArea extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
render(props, { value, isValid }) {
|
||||
render({ label }, { value, isValid }) {
|
||||
const style = {
|
||||
minWidth: 300,
|
||||
width: '100%',
|
||||
|
@ -47,10 +47,11 @@ export default class JSONTextArea extends Component {
|
|||
style.border = '1px solid red';
|
||||
}
|
||||
return (
|
||||
<iron-autogrow-textarea
|
||||
<paper-textarea
|
||||
label={label}
|
||||
value={value}
|
||||
style={style}
|
||||
onValue-Changed={this.onChange}
|
||||
onvalue-changed={this.onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import CallServiceAction from './call_service';
|
||||
import ConditionAction from './condition';
|
||||
import DelayAction from './delay';
|
||||
import EventAction from './event';
|
||||
import WaitAction from './wait';
|
||||
|
||||
const TYPES = {
|
||||
'Call Service': CallServiceAction,
|
||||
Delay: DelayAction,
|
||||
Wait: WaitAction,
|
||||
Condition: ConditionAction,
|
||||
'Fire Event': EventAction,
|
||||
};
|
||||
|
||||
const OPTIONS = Object.keys(TYPES).sort();
|
||||
|
||||
function getType(action) {
|
||||
const keys = Object.keys(TYPES);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (TYPES[keys[i]].configKey in action) {
|
||||
return keys[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export default class Action extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.typeChanged = this.typeChanged.bind(this);
|
||||
}
|
||||
|
||||
typeChanged(ev) {
|
||||
const newType = ev.target.selectedItem.innerHTML;
|
||||
const oldType = getType(this.props.action);
|
||||
|
||||
if (oldType !== newType) {
|
||||
this.props.onChange(this.props.index, TYPES[newType].defaultConfig);
|
||||
}
|
||||
}
|
||||
|
||||
render({ index, action, onChange }) {
|
||||
const type = getType(action);
|
||||
const Comp = type && TYPES[type];
|
||||
const selected = OPTIONS.indexOf(type);
|
||||
|
||||
if (!Comp) {
|
||||
return (
|
||||
<div>
|
||||
Unsupported action
|
||||
<pre>{JSON.stringify(action, null, 2)}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<paper-dropdown-menu-light label="Action Type" no-animations>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
selected={selected}
|
||||
oniron-select={this.typeChanged}
|
||||
>
|
||||
{OPTIONS.map(opt => <paper-item>{opt}</paper-item>)}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu-light>
|
||||
<Comp
|
||||
index={index}
|
||||
action={action}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import ActionEdit from './action_edit';
|
||||
|
||||
export default class Action extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onDelete = this.onDelete.bind(this);
|
||||
}
|
||||
|
||||
onDelete() {
|
||||
// eslint-disable-next-line
|
||||
if (confirm('Sure you want to delete?')) {
|
||||
this.props.onChange(this.props.index, null);
|
||||
}
|
||||
}
|
||||
|
||||
render(props) {
|
||||
return (
|
||||
<paper-card>
|
||||
<div class='card-menu'>
|
||||
<paper-menu-button
|
||||
no-animations
|
||||
horizontal-align="right"
|
||||
horizontal-offset="-5"
|
||||
vertical-offset="-5"
|
||||
>
|
||||
<paper-icon-button
|
||||
icon="mdi:dots-vertical"
|
||||
slot="dropdown-trigger"
|
||||
/>
|
||||
<paper-listbox slot="dropdown-content">
|
||||
<paper-item disabled>Duplicate</paper-item>
|
||||
<paper-item onTap={this.onDelete}>Delete</paper-item>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
</div>
|
||||
<div class='card-content'>
|
||||
<ActionEdit {...props} />
|
||||
</div>
|
||||
</paper-card>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,23 +1,16 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import JSONTextArea from '../json_textarea';
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class CallServiceAction extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onChange = onChangeEvent.bind(this, 'action');
|
||||
this.serviceDataChanged = this.serviceDataChanged.bind(this);
|
||||
}
|
||||
|
||||
onChange(ev) {
|
||||
this.props.onChange(this.props.index, {
|
||||
...this.props.action,
|
||||
[ev.target.name]: ev.target.value
|
||||
});
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
serviceDataChanged(data) {
|
||||
this.props.onChange(this.props.index, {
|
||||
...this.props.action,
|
||||
|
@ -41,8 +34,8 @@ export default class CallServiceAction extends Component {
|
|||
value={service}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Service Data<br />
|
||||
<JSONTextArea
|
||||
label="Service Data"
|
||||
value={data}
|
||||
onChange={this.serviceDataChanged}
|
||||
/>
|
||||
|
@ -50,3 +43,10 @@ export default class CallServiceAction extends Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
CallServiceAction.configKey = 'service';
|
||||
CallServiceAction.defaultConfig = {
|
||||
alias: '',
|
||||
service: '',
|
||||
data: {}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import StateCondition from '../condition/state';
|
||||
import ConditionEdit from '../condition/condition_edit';
|
||||
|
||||
export default class ConditionAction extends Component {
|
||||
// eslint-disable-next-line
|
||||
render({ action, index, onChange }) {
|
||||
return (
|
||||
<ConditionEdit
|
||||
condition={action}
|
||||
onChange={onChange}
|
||||
index={index}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ConditionAction.configKey = 'condition';
|
||||
ConditionAction.defaultConfig = {
|
||||
condition: 'state',
|
||||
...StateCondition.defaultConfig,
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
import { h, Component } from 'preact';
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class DelayAction extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChangeEvent.bind(this, 'action');
|
||||
}
|
||||
|
||||
render({ action }) {
|
||||
const { delay } = action;
|
||||
return (
|
||||
<div>
|
||||
<paper-input
|
||||
label="Delay"
|
||||
name="delay"
|
||||
value={delay}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DelayAction.configKey = 'delay';
|
||||
DelayAction.defaultConfig = {
|
||||
delay: '',
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import JSONTextArea from '../json_textarea';
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class EventAction extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChangeEvent.bind(this, 'action');
|
||||
this.serviceDataChanged = this.serviceDataChanged.bind(this);
|
||||
}
|
||||
|
||||
serviceDataChanged(data) {
|
||||
this.props.onChange(this.props.index, {
|
||||
...this.props.action,
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
render({ action }) {
|
||||
/* eslint-disable camelcase */
|
||||
const { event, event_data } = action;
|
||||
return (
|
||||
<div>
|
||||
<paper-input
|
||||
label="Event"
|
||||
name="event"
|
||||
value={event}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<JSONTextArea
|
||||
label="Service Data"
|
||||
value={event_data}
|
||||
onChange={this.serviceDataChanged}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
EventAction.configKey = 'event';
|
||||
EventAction.defaultConfig = {
|
||||
event: '',
|
||||
event_data: {},
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import ScriptAction from './script_action';
|
||||
import ActionRow from './action_row';
|
||||
|
||||
export default class Script extends Component {
|
||||
constructor() {
|
||||
|
@ -34,7 +34,7 @@ export default class Script extends Component {
|
|||
return (
|
||||
<div class="script">
|
||||
{script.map((act, idx) => (
|
||||
<ScriptAction
|
||||
<ActionRow
|
||||
index={idx}
|
||||
action={act}
|
||||
onChange={this.actionChanged}
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import CallService from './call_service';
|
||||
|
||||
function getType(action) {
|
||||
if ('service' in action) {
|
||||
return 'Call Service';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const TYPES = {
|
||||
'Call Service': CallService,
|
||||
Delay: null,
|
||||
'Templated Delay': null,
|
||||
Condition: null,
|
||||
'Fire Event': null,
|
||||
};
|
||||
|
||||
const OPTIONS = Object.keys(TYPES).sort();
|
||||
|
||||
export default class Action extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.typeChanged = this.typeChanged.bind(this);
|
||||
this.onDelete = this.onDelete.bind(this);
|
||||
}
|
||||
|
||||
typeChanged(ev) {
|
||||
const newType = ev.target.selectedItem.innerHTML;
|
||||
const oldType = getType(this.props.action);
|
||||
|
||||
if (oldType !== newType) {
|
||||
this.props.onChange(this.props.index, {
|
||||
platform: newType,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onDelete() {
|
||||
// eslint-disable-next-line
|
||||
if (confirm('Sure you want to delete?')) {
|
||||
this.props.onChange(this.props.index, null);
|
||||
}
|
||||
}
|
||||
|
||||
render({ index, action, onChange }) {
|
||||
const type = getType(action);
|
||||
const Comp = TYPES[type];
|
||||
const selected = OPTIONS.indexOf(type);
|
||||
let content;
|
||||
|
||||
if (Comp) {
|
||||
content = (
|
||||
<div>
|
||||
<paper-dropdown-menu-light label="Action Type" no-animations>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
selected={selected}
|
||||
oniron-select={this.typeChanged}
|
||||
>
|
||||
{OPTIONS.map(opt => <paper-item>{opt}</paper-item>)}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu-light>
|
||||
<Comp
|
||||
index={index}
|
||||
action={action}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<div>
|
||||
Unsupported action
|
||||
<pre>{JSON.stringify(action, null, 2)}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<paper-card>
|
||||
<div class='card-menu'>
|
||||
<paper-menu-button
|
||||
no-animations
|
||||
horizontal-align="right"
|
||||
horizontal-offset="-5"
|
||||
vertical-offset="-5"
|
||||
>
|
||||
<paper-icon-button
|
||||
icon="mdi:dots-vertical"
|
||||
slot="dropdown-trigger"
|
||||
/>
|
||||
<paper-listbox slot="dropdown-content">
|
||||
<paper-item disabled>Duplicate</paper-item>
|
||||
<paper-item onTap={this.onDelete}>Delete</paper-item>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
</div>
|
||||
<div class='card-content'>{content}</div>
|
||||
</paper-card>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import { h, Component } from 'preact';
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class WaitAction extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChangeEvent.bind(this, 'action');
|
||||
this.onTemplateChange = this.onTemplateChange.bind(this);
|
||||
}
|
||||
|
||||
// Gets fired on mount. If empty, onChangeEvent removes attribute.
|
||||
// Without the attribute this action is no longer matched to this component.
|
||||
onTemplateChange(ev) {
|
||||
this.props.onChange(this.props.index, {
|
||||
...this.props.trigger,
|
||||
[ev.target.name]: ev.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
render({ action }) {
|
||||
/* eslint-disable camelcase */
|
||||
const { wait_template, timeout } = action;
|
||||
return (
|
||||
<div>
|
||||
<paper-textarea
|
||||
label="Wait Template"
|
||||
name="wait_template"
|
||||
value={wait_template}
|
||||
onvalue-changed={this.onTemplateChange}
|
||||
/>
|
||||
<paper-input
|
||||
label="Timeout (Optional)"
|
||||
name="timeout"
|
||||
value={timeout}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
WaitAction.configKey = 'wait_template';
|
||||
WaitAction.defaultConfig = {
|
||||
wait_template: '',
|
||||
timeout: '',
|
||||
};
|
|
@ -2,13 +2,13 @@ import { h, Component } from 'preact';
|
|||
|
||||
import JSONTextArea from '../json_textarea';
|
||||
|
||||
import { onChange } from './util';
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class EventTrigger extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChange.bind(this);
|
||||
this.onChange = onChangeEvent.bind(this, 'trigger');
|
||||
this.eventDataChanged = this.eventDataChanged.bind(this);
|
||||
}
|
||||
|
||||
|
@ -30,8 +30,8 @@ export default class EventTrigger extends Component {
|
|||
value={event_type}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Event Data
|
||||
<JSONTextArea
|
||||
label="Event Data"
|
||||
value={event_data}
|
||||
onChange={this.eventDataChanged}
|
||||
/>
|
||||
|
@ -39,3 +39,8 @@ export default class EventTrigger extends Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
EventTrigger.defaultConfig = {
|
||||
event_type: '',
|
||||
event_data: {},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
export default class HassTrigger extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.radioGroupPicked = this.radioGroupPicked.bind(this);
|
||||
}
|
||||
|
||||
radioGroupPicked(ev) {
|
||||
this.props.onChange(this.props.index, {
|
||||
...this.props.trigger,
|
||||
event: ev.target.selected,
|
||||
});
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
render({ trigger }) {
|
||||
const { event } = trigger;
|
||||
return (
|
||||
<div>
|
||||
<label id="eventlabel">Event:</label>
|
||||
<paper-radio-group
|
||||
selected={event}
|
||||
aria-labelledby="eventlabel"
|
||||
onpaper-radio-group-changed={this.radioGroupPicked}
|
||||
>
|
||||
<paper-radio-button name="start">Start</paper-radio-button>
|
||||
<paper-radio-button name="shutdown">Shutdown</paper-radio-button>
|
||||
</paper-radio-group>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
HassTrigger.defaultConfig = {
|
||||
event: 'start'
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import TriggerRow from './trigger_row';
|
||||
import StateTrigger from './state';
|
||||
|
||||
export default class Trigger extends Component {
|
||||
constructor() {
|
||||
|
@ -12,7 +13,8 @@ export default class Trigger extends Component {
|
|||
|
||||
addTrigger() {
|
||||
const trigger = this.props.trigger.concat({
|
||||
platform: 'event',
|
||||
platform: 'state',
|
||||
...StateTrigger.defaultConfig,
|
||||
});
|
||||
|
||||
this.props.onChange(trigger);
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class MQTTTrigger extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChangeEvent.bind(this, 'trigger');
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
render({ trigger }) {
|
||||
const { topic, payload } = trigger;
|
||||
return (
|
||||
<div>
|
||||
<paper-input
|
||||
label="Topic"
|
||||
name="topic"
|
||||
value={topic}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<paper-input
|
||||
label="Payload (Optional)"
|
||||
name="payload"
|
||||
value={payload}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MQTTTrigger.defaultConfig = {
|
||||
topic: '',
|
||||
payload: '',
|
||||
};
|
|
@ -1,12 +1,12 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChange } from './util';
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class NumericStateTrigger extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChange.bind(this);
|
||||
this.onChange = onChangeEvent.bind(this, 'trigger');
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
|
@ -32,14 +32,20 @@ export default class NumericStateTrigger extends Component {
|
|||
value={below}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Value template (optional)<br />
|
||||
<textarea
|
||||
<paper-textarea
|
||||
label="Value template (optional)"
|
||||
name="value_template"
|
||||
value={value_template}
|
||||
style={{ width: '100%', height: 100 }}
|
||||
onChange={this.onChange}
|
||||
onvalue-changed={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
NumericStateTrigger.defaultConfig = {
|
||||
entity_id: '',
|
||||
above: '',
|
||||
below: '',
|
||||
value_template: '',
|
||||
};
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChange } from './util';
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class StateTrigger extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChange.bind(this);
|
||||
this.onChange = onChangeEvent.bind(this, 'trigger');
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
|
@ -39,3 +39,9 @@ export default class StateTrigger extends Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
StateTrigger.defaultConfig = {
|
||||
entity_id: '',
|
||||
from: '',
|
||||
to: '',
|
||||
};
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class SunTrigger extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChangeEvent.bind(this, 'trigger');
|
||||
this.radioGroupPicked = this.radioGroupPicked.bind(this);
|
||||
}
|
||||
|
||||
radioGroupPicked(ev) {
|
||||
this.props.onChange(this.props.index, {
|
||||
...this.props.trigger,
|
||||
event: ev.target.selected,
|
||||
});
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
render({ trigger }) {
|
||||
const { offset, event } = trigger;
|
||||
return (
|
||||
<div>
|
||||
<label id="eventlabel">Event:</label>
|
||||
<paper-radio-group
|
||||
selected={event}
|
||||
aria-labelledby="eventlabel"
|
||||
onpaper-radio-group-changed={this.radioGroupPicked}
|
||||
>
|
||||
<paper-radio-button name="sunrise">Sunrise</paper-radio-button>
|
||||
<paper-radio-button name="sunset">Sunset</paper-radio-button>
|
||||
</paper-radio-group>
|
||||
|
||||
<paper-input
|
||||
label="Offset (optional)"
|
||||
name="offset"
|
||||
value={offset}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SunTrigger.defaultConfig = {
|
||||
event: 'sunrise',
|
||||
offset: '',
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class TemplateTrigger extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChangeEvent.bind(this, 'trigger');
|
||||
}
|
||||
|
||||
render({ trigger }) {
|
||||
/* eslint-disable camelcase */
|
||||
const { value_template } = trigger;
|
||||
return (
|
||||
<div>
|
||||
<paper-textarea
|
||||
label="Value Template"
|
||||
name="value_template"
|
||||
value={value_template}
|
||||
onvalue-changed={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TemplateTrigger.defaultConfig = {
|
||||
value_template: '',
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class TimeTrigger extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChangeEvent.bind(this, 'trigger');
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
render({ trigger }) {
|
||||
const { at } = trigger;
|
||||
return (
|
||||
<div>
|
||||
<paper-input
|
||||
label="At"
|
||||
name="at"
|
||||
value={at}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TimeTrigger.defaultConfig = {
|
||||
at: '',
|
||||
};
|
|
@ -0,0 +1,76 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import EventTrigger from './event';
|
||||
import HassTrigger from './homeassistant';
|
||||
import MQTTTrigger from './mqtt';
|
||||
import NumericStateTrigger from './numeric_state';
|
||||
import StateTrigger from './state';
|
||||
import SunTrigger from './sun';
|
||||
import TemplateTrigger from './template';
|
||||
import TimeTrigger from './time';
|
||||
import ZoneTrigger from './zone';
|
||||
|
||||
const TYPES = {
|
||||
event: EventTrigger,
|
||||
state: StateTrigger,
|
||||
homeassistant: HassTrigger,
|
||||
mqtt: MQTTTrigger,
|
||||
numeric_state: NumericStateTrigger,
|
||||
sun: SunTrigger,
|
||||
template: TemplateTrigger,
|
||||
time: TimeTrigger,
|
||||
zone: ZoneTrigger,
|
||||
};
|
||||
|
||||
const OPTIONS = Object.keys(TYPES).sort();
|
||||
|
||||
export default class TriggerEdit extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.typeChanged = this.typeChanged.bind(this);
|
||||
}
|
||||
|
||||
typeChanged(ev) {
|
||||
const type = ev.target.selectedItem.innerHTML;
|
||||
|
||||
if (type !== this.props.trigger.platform) {
|
||||
this.props.onChange(this.props.index, {
|
||||
platform: type,
|
||||
...TYPES[type].defaultConfig
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render({ index, trigger, onChange }) {
|
||||
const Comp = TYPES[trigger.platform];
|
||||
const selected = OPTIONS.indexOf(trigger.platform);
|
||||
|
||||
if (!Comp) {
|
||||
return (
|
||||
<div>
|
||||
Unsupported platform: {trigger.platform}
|
||||
<pre>{JSON.stringify(trigger, null, 2)}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<paper-dropdown-menu-light label="Trigger Type" no-animations>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
selected={selected}
|
||||
oniron-select={this.typeChanged}
|
||||
>
|
||||
{OPTIONS.map(opt => <paper-item>{opt}</paper-item>)}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu-light>
|
||||
<Comp
|
||||
index={index}
|
||||
trigger={trigger}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,41 +1,14 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import EventTrigger from './event';
|
||||
import StateTrigger from './state';
|
||||
import NumericStateTrigger from './numeric_state';
|
||||
|
||||
const TYPES = {
|
||||
event: EventTrigger,
|
||||
state: StateTrigger,
|
||||
homeassistant: null,
|
||||
mqtt: null,
|
||||
numeric_state: NumericStateTrigger,
|
||||
sun: null,
|
||||
template: null,
|
||||
time: null,
|
||||
zone: null,
|
||||
};
|
||||
|
||||
const OPTIONS = Object.keys(TYPES).sort();
|
||||
import TriggerEdit from './trigger_edit';
|
||||
|
||||
export default class TriggerRow extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.typeChanged = this.typeChanged.bind(this);
|
||||
this.onDelete = this.onDelete.bind(this);
|
||||
}
|
||||
|
||||
typeChanged(ev) {
|
||||
const type = ev.target.selectedItem.innerHTML;
|
||||
|
||||
if (type !== this.props.trigger.platform) {
|
||||
this.props.onChange(this.props.index, {
|
||||
platform: type,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onDelete() {
|
||||
// eslint-disable-next-line
|
||||
if (confirm('Sure you want to delete?')) {
|
||||
|
@ -43,40 +16,7 @@ export default class TriggerRow extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
render({ index, trigger, onChange }) {
|
||||
const Comp = TYPES[trigger.platform];
|
||||
const selected = OPTIONS.indexOf(trigger.platform);
|
||||
|
||||
let content;
|
||||
|
||||
if (Comp) {
|
||||
content = (
|
||||
<div>
|
||||
<paper-dropdown-menu-light label="Trigger Type" no-animations>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
selected={selected}
|
||||
oniron-select={this.typeChanged}
|
||||
>
|
||||
{OPTIONS.map(opt => <paper-item>{opt}</paper-item>)}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu-light>
|
||||
<Comp
|
||||
index={index}
|
||||
trigger={trigger}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<div>
|
||||
Unsupported platform: {trigger.platform}
|
||||
<pre>{JSON.stringify(trigger, null, 2)}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render(props) {
|
||||
return (
|
||||
<paper-card>
|
||||
<div class='card-menu'>
|
||||
|
@ -96,7 +36,9 @@ export default class TriggerRow extends Component {
|
|||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
</div>
|
||||
<div class='card-content'>{content}</div>
|
||||
<div class='card-content'>
|
||||
<TriggerEdit {...props} />
|
||||
</div>
|
||||
</paper-card>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
export function onChange(ev) {
|
||||
const trigger = { ...this.props.trigger };
|
||||
|
||||
if (ev.target.value) {
|
||||
trigger[ev.target.name] = ev.target.value;
|
||||
} else {
|
||||
delete trigger[ev.target.name];
|
||||
}
|
||||
|
||||
this.props.onChange(this.props.index, trigger);
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../util';
|
||||
|
||||
export default class ZoneTrigger extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChangeEvent.bind(this, 'trigger');
|
||||
this.radioGroupPicked = this.radioGroupPicked.bind(this);
|
||||
}
|
||||
|
||||
radioGroupPicked(ev) {
|
||||
this.props.onChange(this.props.index, {
|
||||
...this.props.trigger,
|
||||
event: ev.target.selected,
|
||||
});
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
render({ trigger }) {
|
||||
const { entity_id, zone, event } = trigger;
|
||||
return (
|
||||
<div>
|
||||
<paper-input
|
||||
label="Entity Id"
|
||||
name="entity_id"
|
||||
value={entity_id}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<paper-input
|
||||
label="Zone"
|
||||
name="zone"
|
||||
value={zone}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<label id="eventlabel">Event:</label>
|
||||
<paper-radio-group
|
||||
selected={event}
|
||||
aria-labelledby="eventlabel"
|
||||
onpaper-radio-group-changed={this.radioGroupPicked}
|
||||
>
|
||||
<paper-radio-button name="enter">Enter</paper-radio-button>
|
||||
<paper-radio-button name="leave">Leave</paper-radio-button>
|
||||
</paper-radio-group>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ZoneTrigger.defaultConfig = {
|
||||
entity_id: '',
|
||||
zone: '',
|
||||
event: 'enter',
|
||||
};
|
|
@ -1,3 +1,17 @@
|
|||
export function validEntityId(entityId) {
|
||||
return /^(\w+)\.(\w+)$/.test(entityId);
|
||||
}
|
||||
|
||||
export function onChangeEvent(prop, ev) {
|
||||
const data = { ...this.props[prop] };
|
||||
|
||||
if (ev.target.value === data[ev.target.name]) {
|
||||
return;
|
||||
} else if (ev.target.value) {
|
||||
data[ev.target.name] = ev.target.value;
|
||||
} else {
|
||||
delete data[ev.target.name];
|
||||
}
|
||||
|
||||
this.props.onChange(this.props.index, data);
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
"gulp": "gulp",
|
||||
"build": "BUILD_DEV=0 gulp",
|
||||
"build_demo": "BUILD_DEV=0 BUILD_DEMO=1 gulp",
|
||||
"dev": "npm run gulp ru_all",
|
||||
"dev-watch": "npm run gulp watch_ru_all",
|
||||
"dev": "npm run gulp ru_all gen-service-worker",
|
||||
"dev-watch": "npm run gulp watch_ru_all gen-service-worker",
|
||||
"lint_js": "eslint src panels js --ext js,html",
|
||||
"lint_html": "ls -1 src/home-assistant.html panels/**/ha-panel-*.html | xargs polymer lint --input",
|
||||
"test": "npm run lint_js && npm run lint_html"
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
<link rel="import" href="../../../bower_components/paper-item/paper-item-body.html">
|
||||
<link rel="import" href="../../../bower_components/paper-icon-button/paper-icon-button.html">
|
||||
<link rel="import" href="../../../bower_components/paper-input/paper-input.html">
|
||||
<link rel="import" href="../../../bower_components/paper-input/paper-textarea.html">
|
||||
<link rel="import" href="../../../bower_components/paper-radio-button/paper-radio-button.html">
|
||||
<link rel="import" href="../../../bower_components/paper-radio-group/paper-radio-group.html">
|
||||
<link rel="import" href="../../../bower_components/paper-dropdown-menu/paper-dropdown-menu-light.html">
|
||||
<link rel="import" href="../../../bower_components/paper-listbox/paper-listbox.html">
|
||||
<link rel="import" href="../../../bower_components/paper-menu-button/paper-menu-button.html">
|
||||
|
|
Loading…
Reference in New Issue