Add localization to the automation editor (#923)

* Add localization to the automation editor

* Unnest automation strings from `section`
This commit is contained in:
Adam Mills 2018-02-26 19:15:51 -05:00 committed by Paulus Schoutsen
parent 9f3458fcd1
commit d4b257854d
37 changed files with 337 additions and 159 deletions

View File

@ -42,7 +42,7 @@ export default class Automation extends Component {
});
}
render({ automation, isWide, hass }) {
render({ automation, isWide, hass, localize }) {
const {
alias, trigger, condition, action
} = automation;
@ -52,12 +52,12 @@ export default class Automation extends Component {
<ha-config-section is-wide={isWide}>
<span slot='header'>{alias}</span>
<span slot='introduction'>
Use automations to bring your home alive.
{localize('ui.panel.config.automation.editor.introduction')}
</span>
<paper-card>
<div class='card-content'>
<paper-input
label="Name"
label={localize('ui.panel.config.automation.editor.alias')}
name="alias"
value={alias}
onvalue-changed={this.onChange}
@ -67,55 +67,41 @@ export default class Automation extends Component {
</ha-config-section>
<ha-config-section is-wide={isWide}>
<span slot='header'>Triggers</span>
<span slot='header'>{localize('ui.panel.config.automation.editor.triggers.header')}</span>
<span slot='introduction'>
Triggers are what starts the processing of an automation rule.
It is possible to specify multiple triggers for the same rule.
Once a trigger starts, Home Assistant will validate the conditions,
if any, and call the action.
<p><a href="https://home-assistant.io/docs/automation/trigger/" target="_blank">
Learn more about triggers.
</a></p>
<ha-markdown content={localize('ui.panel.config.automation.editor.triggers.introduction')} />
</span>
<Trigger
trigger={trigger}
onChange={this.triggerChanged}
hass={hass}
localize={localize}
/>
</ha-config-section>
<ha-config-section is-wide={isWide}>
<span slot='header'>Conditions</span>
<span slot='header'>{localize('ui.panel.config.automation.editor.conditions.header')}</span>
<span slot='introduction'>
Conditions are an optional part of an automation rule and can be used to prevent
an action from happening when triggered. Conditions look very similar to triggers
but are very different. A trigger will look at events happening in the system
while a condition only looks at how the system looks right now. A trigger can
observe that a switch is being turned on. A condition can only see if a switch
is currently on or off.
<p><a href="https://home-assistant.io/docs/scripts/conditions/" target="_blank">
Learn more about conditions.
</a></p>
<ha-markdown content={localize('ui.panel.config.automation.editor.conditions.introduction')} />
</span>
<Condition
condition={condition || []}
onChange={this.conditionChanged}
hass={hass}
localize={localize}
/>
</ha-config-section>
<ha-config-section is-wide={isWide}>
<span slot='header'>Action</span>
<span slot='header'>{localize('ui.panel.config.automation.editor.actions.header')}</span>
<span slot='introduction'>
The actions are what Home Assistant will do when the automation is triggered.
<p><a href="https://home-assistant.io/docs/scripts/" target="_blank">
Learn more about actions.
</a></p>
<ha-markdown content={localize('ui.panel.config.automation.editor.actions.introduction')} />
</span>
<Script
script={action}
onChange={this.actionChanged}
hass={hass}
localize={localize}
/>
</ha-config-section>
</div>

View File

@ -26,7 +26,7 @@ export default class ConditionRow extends Component {
}
typeChanged(ev) {
const type = ev.target.selectedItem.innerHTML;
const type = ev.target.selectedItem.attributes.condition.value;
if (type !== this.props.condition.condition) {
this.props.onChange(this.props.index, {
@ -36,14 +36,14 @@ export default class ConditionRow extends Component {
}
}
render({ index, condition, onChange, hass }) {
render({ index, condition, onChange, hass, localize }) {
const Comp = TYPES[condition.condition];
const selected = OPTIONS.indexOf(condition.condition);
if (!Comp) {
return (
<div>
Unsupported condition: {condition.condition}
{localize('ui.panel.config.automation.editor.conditions.unsupported_condition', 'condition', condition.condition)}
<pre>{JSON.stringify(condition, null, 2)}</pre>
</div>
);
@ -51,13 +51,13 @@ export default class ConditionRow extends Component {
return (
<div>
<paper-dropdown-menu-light label="Condition Type" no-animations>
<paper-dropdown-menu-light label={localize('ui.panel.config.automation.editor.conditions.type_select')} no-animations>
<paper-listbox
slot="dropdown-content"
selected={selected}
oniron-select={this.typeChanged}
>
{OPTIONS.map(opt => <paper-item>{opt}</paper-item>)}
{OPTIONS.map(opt => <paper-item condition={opt}>{localize(`ui.panel.config.automation.editor.conditions.type.${opt}.label`)}</paper-item>)}
</paper-listbox>
</paper-dropdown-menu-light>
<Comp
@ -65,6 +65,7 @@ export default class ConditionRow extends Component {
condition={condition}
onChange={onChange}
hass={hass}
localize={localize}
/>
</div>
);

View File

@ -11,7 +11,7 @@ export default class ConditionRow extends Component {
onDelete() {
// eslint-disable-next-line
if (confirm('Sure you want to delete?')) {
if (confirm(this.props.localize('ui.panel.config.automation.editor.conditions.delete_confirm'))) {
this.props.onChange(this.props.index, null);
}
}
@ -31,8 +31,8 @@ export default class ConditionRow extends Component {
slot="dropdown-trigger"
/>
<paper-listbox slot="dropdown-content">
<paper-item disabled>Duplicate</paper-item>
<paper-item onTap={this.onDelete}>Delete</paper-item>
<paper-item disabled>{props.localize('ui.panel.config.automation.editor.conditions.duplicate')}</paper-item>
<paper-item onTap={this.onDelete}>{props.localize('ui.panel.config.automation.editor.conditions.delete')}</paper-item>
</paper-listbox>
</paper-menu-button>
</div>

View File

@ -30,7 +30,7 @@ export default class Condition extends Component {
this.props.onChange(condition);
}
render({ condition, hass }) {
render({ condition, hass, localize }) {
return (
<div class="triggers">
{condition.map((cnd, idx) => (
@ -39,10 +39,11 @@ export default class Condition extends Component {
condition={cnd}
onChange={this.conditionChanged}
hass={hass}
localize={localize}
/>))}
<paper-card>
<div class='card-actions add-card'>
<paper-button onTap={this.addCondition}>Add condition</paper-button>
<paper-button onTap={this.addCondition}>{localize('ui.panel.config.automation.editor.conditions.add')}</paper-button>
</div>
</paper-card>
</div>

View File

@ -18,7 +18,7 @@ export default class NumericStateCondition extends Component {
}
/* eslint-disable camelcase */
render({ condition, hass }) {
render({ condition, hass, localize }) {
const {
value_template, entity_id, below, above
} = condition;
@ -31,19 +31,19 @@ export default class NumericStateCondition extends Component {
allowCustomEntity
/>
<paper-input
label="Above"
label={localize('ui.panel.config.automation.editor.conditions.type.numeric_state.above')}
name="above"
value={above}
onvalue-changed={this.onChange}
/>
<paper-input
label="Below"
label={localize('ui.panel.config.automation.editor.conditions.type.numeric_state.below')}
name="below"
value={below}
onvalue-changed={this.onChange}
/>
<paper-textarea
label="Value template (optional)"
label={localize('ui.panel.config.automation.editor.conditions.type.numeric_state.value_template')}
name="value_template"
value={value_template}
onvalue-changed={this.onChange}

View File

@ -18,7 +18,7 @@ export default class StateCondition extends Component {
}
/* eslint-disable camelcase */
render({ condition, hass }) {
render({ condition, hass, localize }) {
const { entity_id, state } = condition;
const cndFor = condition.for;
return (
@ -30,7 +30,7 @@ export default class StateCondition extends Component {
allowCustomEntity
/>
<paper-input
label="State"
label={localize('ui.panel.config.automation.editor.conditions.type.state.state')}
name="state"
value={state}
onvalue-changed={this.onChange}

View File

@ -23,45 +23,45 @@ export default class SunCondition extends Component {
this.props.onChange(this.props.index, condition);
}
render({ condition }) {
render({ condition, localize }) {
/* eslint-disable camelcase */
const {
after, after_offset, before, before_offset
} = condition;
return (
<div>
<label id="beforelabel">Before:</label>
<label id="beforelabel">{localize('ui.panel.config.automation.editor.conditions.type.sun.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-button name="sunrise">{localize('ui.panel.config.automation.editor.conditions.type.sun.sunrise')}</paper-radio-button>
<paper-radio-button name="sunset">{localize('ui.panel.config.automation.editor.conditions.type.sun.sunset')}</paper-radio-button>
</paper-radio-group>
<paper-input
label="Before offset (optional)"
label={localize('ui.panel.config.automation.editor.conditions.type.sun.before_offset')}
name="before_offset"
value={before_offset}
onvalue-changed={this.onChange}
disabled={before === undefined}
/>
<label id="afterlabel">After:</label>
<label id="afterlabel">{localize('ui.panel.config.automation.editor.conditions.type.sun.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-button name="sunrise">{localize('ui.panel.config.automation.editor.conditions.type.sun.sunrise')}</paper-radio-button>
<paper-radio-button name="sunset">{localize('ui.panel.config.automation.editor.conditions.type.sun.sunset')}</paper-radio-button>
</paper-radio-group>
<paper-input
label="After offset (optional)"
label={localize('ui.panel.config.automation.editor.conditions.type.sun.after_offset')}
name="after_offset"
value={after_offset}
onvalue-changed={this.onChange}

View File

@ -9,13 +9,13 @@ export default class TemplateCondition extends Component {
this.onChange = onChangeEvent.bind(this, 'condition');
}
render({ condition }) {
render({ condition, localize }) {
/* eslint-disable camelcase */
const { value_template } = condition;
return (
<div>
<paper-textarea
label="Value Template"
label={localize('ui.panel.config.automation.editor.conditions.type.template.value_template')}
name="value_template"
value={value_template}
onvalue-changed={this.onChange}

View File

@ -10,18 +10,18 @@ export default class TimeCondition extends Component {
}
/* eslint-disable camelcase */
render({ condition }) {
render({ condition, localize }) {
const { after, before } = condition;
return (
<div>
<paper-input
label="After"
label={localize('ui.panel.config.automation.editor.conditions.type.time.after')}
name="after"
value={after}
onvalue-changed={this.onChange}
/>
<paper-input
label="Before"
label={localize('ui.panel.config.automation.editor.conditions.type.time.before')}
name="before"
value={before}
onvalue-changed={this.onChange}

View File

@ -32,12 +32,12 @@ export default class ZoneCondition extends Component {
}
/* eslint-disable camelcase */
render({ condition, hass }) {
render({ condition, hass, localize }) {
const { entity_id, zone } = condition;
return (
<div>
<ha-entity-picker
label='Entity with location'
label={localize('ui.panel.config.automation.editor.conditions.type.zone.entity')}
value={entity_id}
onChange={this.entityPicked}
hass={hass}
@ -45,7 +45,7 @@ export default class ZoneCondition extends Component {
entityFilter={zoneAndLocationFilter}
/>
<ha-entity-picker
label='Zone'
label={localize('ui.panel.config.automation.editor.conditions.type.zone.zone')}
value={zone}
onChange={this.zonePicked}
hass={hass}

View File

@ -24,7 +24,7 @@ export default class ScriptEditor extends Component {
});
}
render({ script, isWide, hass }) {
render({ script, isWide, hass, localize }) {
const { alias, sequence } = script;
return (
@ -58,6 +58,7 @@ export default class ScriptEditor extends Component {
script={sequence}
onChange={this.sequenceChanged}
hass={hass}
localize={localize}
/>
</ha-config-section>
</div>

View File

@ -7,11 +7,11 @@ import EventAction from './event.js';
import WaitAction from './wait.js';
const TYPES = {
'Call Service': CallServiceAction,
Delay: DelayAction,
Wait: WaitAction,
Condition: ConditionAction,
'Fire Event': EventAction,
service: CallServiceAction,
delay: DelayAction,
wait_template: WaitAction,
condition: ConditionAction,
event: EventAction,
};
const OPTIONS = Object.keys(TYPES).sort();
@ -19,7 +19,7 @@ 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) {
if (keys[i] in action) {
return keys[i];
}
}
@ -34,7 +34,7 @@ export default class Action extends Component {
}
typeChanged(ev) {
const newType = ev.target.selectedItem.innerHTML;
const newType = ev.target.selectedItem.attributes.action.value;
const oldType = getType(this.props.action);
if (oldType !== newType) {
@ -42,7 +42,7 @@ export default class Action extends Component {
}
}
render({ index, action, onChange, hass }) {
render({ index, action, onChange, hass, localize }) {
const type = getType(action);
const Comp = type && TYPES[type];
const selected = OPTIONS.indexOf(type);
@ -50,20 +50,20 @@ export default class Action extends Component {
if (!Comp) {
return (
<div>
Unsupported action
{localize('ui.panel.config.automation.editor.actions.unsupported_action', 'action', type)}
<pre>{JSON.stringify(action, null, 2)}</pre>
</div>
);
}
return (
<div>
<paper-dropdown-menu-light label="Action Type" no-animations>
<paper-dropdown-menu-light label={localize('ui.panel.config.automation.editor.actions.type_select')} no-animations>
<paper-listbox
slot="dropdown-content"
selected={selected}
oniron-select={this.typeChanged}
>
{OPTIONS.map(opt => <paper-item>{opt}</paper-item>)}
{OPTIONS.map(opt => <paper-item action={opt}>{localize(`ui.panel.config.automation.editor.actions.type.${opt}.label`)}</paper-item>)}
</paper-listbox>
</paper-dropdown-menu-light>
<Comp
@ -71,6 +71,7 @@ export default class Action extends Component {
action={action}
onChange={onChange}
hass={hass}
localize={localize}
/>
</div>
);

View File

@ -11,7 +11,7 @@ export default class Action extends Component {
onDelete() {
// eslint-disable-next-line
if (confirm('Sure you want to delete?')) {
if (confirm(this.props.localize('ui.panel.config.automation.editor.actions.delete_confirm'))) {
this.props.onChange(this.props.index, null);
}
}
@ -31,8 +31,8 @@ export default class Action extends Component {
slot="dropdown-trigger"
/>
<paper-listbox slot="dropdown-content">
<paper-item disabled>Duplicate</paper-item>
<paper-item onTap={this.onDelete}>Delete</paper-item>
<paper-item disabled>{props.localize('ui.panel.config.automation.editor.actions.duplicate')}</paper-item>
<paper-item onTap={this.onDelete}>{props.localize('ui.panel.config.automation.editor.actions.delete')}</paper-item>
</paper-listbox>
</paper-menu-button>
</div>

View File

@ -24,7 +24,7 @@ export default class CallServiceAction extends Component {
});
}
render({ action, hass }) {
render({ action, hass, localize }) {
const { service, data } = action;
return (
@ -35,7 +35,7 @@ export default class CallServiceAction extends Component {
onChange={this.serviceChanged}
/>
<JSONTextArea
label="Service Data"
label={localize('ui.panel.config.automation.editor.actions.type.service.service_data')}
value={data}
onChange={this.serviceDataChanged}
/>
@ -44,7 +44,6 @@ export default class CallServiceAction extends Component {
}
}
CallServiceAction.configKey = 'service';
CallServiceAction.defaultConfig = {
alias: '',
service: '',

View File

@ -5,19 +5,19 @@ import ConditionEdit from '../condition/condition_edit.js';
export default class ConditionAction extends Component {
// eslint-disable-next-line
render({ action, index, onChange, hass }) {
render({ action, index, onChange, hass, localize }) {
return (
<ConditionEdit
condition={action}
onChange={onChange}
index={index}
hass={hass}
localize={localize}
/>
);
}
}
ConditionAction.configKey = 'condition';
ConditionAction.defaultConfig = {
condition: 'state',
...StateCondition.defaultConfig,

View File

@ -8,12 +8,12 @@ export default class DelayAction extends Component {
this.onChange = onChangeEvent.bind(this, 'action');
}
render({ action }) {
render({ action, localize }) {
const { delay } = action;
return (
<div>
<paper-input
label="Delay"
label={localize('ui.panel.config.automation.editor.actions.type.delay.delay')}
name="delay"
value={delay}
onvalue-changed={this.onChange}
@ -23,7 +23,6 @@ export default class DelayAction extends Component {
}
}
DelayAction.configKey = 'delay';
DelayAction.defaultConfig = {
delay: '',
};

View File

@ -18,19 +18,19 @@ export default class EventAction extends Component {
});
}
render({ action }) {
render({ action, localize }) {
/* eslint-disable camelcase */
const { event, event_data } = action;
return (
<div>
<paper-input
label="Event"
label={localize('ui.panel.config.automation.editor.actions.type.event.event')}
name="event"
value={event}
onvalue-changed={this.onChange}
/>
<JSONTextArea
label="Service Data"
label={localize('ui.panel.config.automation.editor.actions.type.event.service_data')}
value={event_data}
onChange={this.serviceDataChanged}
/>
@ -39,7 +39,6 @@ export default class EventAction extends Component {
}
}
EventAction.configKey = 'event';
EventAction.defaultConfig = {
event: '',
event_data: {},

View File

@ -30,7 +30,7 @@ export default class Script extends Component {
this.props.onChange(script);
}
render({ script, hass }) {
render({ script, hass, localize }) {
return (
<div class="script">
{script.map((act, idx) => (
@ -39,10 +39,11 @@ export default class Script extends Component {
action={act}
onChange={this.actionChanged}
hass={hass}
localize={localize}
/>))}
<paper-card>
<div class='card-actions add-card'>
<paper-button onTap={this.addAction}>Add action</paper-button>
<paper-button onTap={this.addAction}>{localize('ui.panel.config.automation.editor.actions.add')}</paper-button>
</div>
</paper-card>
</div>

View File

@ -18,19 +18,19 @@ export default class WaitAction extends Component {
});
}
render({ action }) {
render({ action, localize }) {
/* eslint-disable camelcase */
const { wait_template, timeout } = action;
return (
<div>
<paper-textarea
label="Wait Template"
label={localize('ui.panel.config.automation.editor.actions.type.wait_template.wait_template')}
name="wait_template"
value={wait_template}
onvalue-changed={this.onTemplateChange}
/>
<paper-input
label="Timeout (Optional)"
label={localize('ui.panel.config.automation.editor.actions.type.wait_template.timeout')}
name="timeout"
value={timeout}
onvalue-changed={this.onChange}
@ -40,7 +40,6 @@ export default class WaitAction extends Component {
}
}
WaitAction.configKey = 'wait_template';
WaitAction.defaultConfig = {
wait_template: '',
timeout: '',

View File

@ -19,18 +19,18 @@ export default class EventTrigger extends Component {
});
}
render({ trigger }) {
render({ trigger, localize }) {
const { event_type, event_data } = trigger;
return (
<div>
<paper-input
label="Event Type"
label={localize('ui.panel.config.automation.editor.triggers.type.event.event_type')}
name="event_type"
value={event_type}
onvalue-changed={this.onChange}
/>
<JSONTextArea
label="Event Data"
label={localize('ui.panel.config.automation.editor.triggers.type.event.event_data')}
value={event_data}
onChange={this.eventDataChanged}
/>

View File

@ -15,18 +15,18 @@ export default class HassTrigger extends Component {
}
/* eslint-disable camelcase */
render({ trigger }) {
render({ trigger, localize }) {
const { event } = trigger;
return (
<div>
<label id="eventlabel">Event:</label>
<label id="eventlabel">{localize('ui.panel.config.automation.editor.triggers.type.homeassistant.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-button name="start">{localize('ui.panel.config.automation.editor.triggers.type.homeassistant.start')}</paper-radio-button>
<paper-radio-button name="shutdown">{localize('ui.panel.config.automation.editor.triggers.type.homeassistant.shutdown')}</paper-radio-button>
</paper-radio-group>
</div>
);

View File

@ -32,7 +32,7 @@ export default class Trigger extends Component {
this.props.onChange(trigger);
}
render({ trigger, hass }) {
render({ trigger, hass, localize }) {
return (
<div class="triggers">
{trigger.map((trg, idx) => (
@ -41,10 +41,11 @@ export default class Trigger extends Component {
trigger={trg}
onChange={this.triggerChanged}
hass={hass}
localize={localize}
/>))}
<paper-card>
<div class='card-actions add-card'>
<paper-button onTap={this.addTrigger}>Add trigger</paper-button>
<paper-button onTap={this.addTrigger}>{localize('ui.panel.config.automation.editor.triggers.add')}</paper-button>
</div>
</paper-card>
</div>

View File

@ -10,18 +10,18 @@ export default class MQTTTrigger extends Component {
}
/* eslint-disable camelcase */
render({ trigger }) {
render({ trigger, localize }) {
const { topic, payload } = trigger;
return (
<div>
<paper-input
label="Topic"
label={localize('ui.panel.config.automation.editor.triggers.type.mqtt.topic')}
name="topic"
value={topic}
onvalue-changed={this.onChange}
/>
<paper-input
label="Payload (Optional)"
label={localize('ui.panel.config.automation.editor.triggers.type.mqtt.payload')}
name="payload"
value={payload}
onvalue-changed={this.onChange}

View File

@ -18,7 +18,7 @@ export default class NumericStateTrigger extends Component {
}
/* eslint-disable camelcase */
render({ trigger, hass }) {
render({ trigger, hass, localize }) {
const {
value_template, entity_id, below, above
} = trigger;
@ -32,19 +32,19 @@ export default class NumericStateTrigger extends Component {
allowCustomEntity
/>
<paper-input
label="Above"
label={localize('ui.panel.config.automation.editor.triggers.type.numeric_state.above')}
name="above"
value={above}
onvalue-changed={this.onChange}
/>
<paper-input
label="Below"
label={localize('ui.panel.config.automation.editor.triggers.type.numeric_state.below')}
name="below"
value={below}
onvalue-changed={this.onChange}
/>
<paper-textarea
label="Value template (optional)"
label={localize('ui.panel.config.automation.editor.triggers.type.numeric_state.value_template')}
name="value_template"
value={value_template}
onvalue-changed={this.onChange}

View File

@ -18,7 +18,7 @@ export default class StateTrigger extends Component {
}
/* eslint-disable camelcase */
render({ trigger, hass }) {
render({ trigger, hass, localize }) {
const { entity_id, to } = trigger;
const trgFrom = trigger.from;
const trgFor = trigger.for;
@ -31,13 +31,13 @@ export default class StateTrigger extends Component {
allowCustomEntity
/>
<paper-input
label="From"
label={localize('ui.panel.config.automation.editor.triggers.type.state.from')}
name="from"
value={trgFrom}
onvalue-changed={this.onChange}
/>
<paper-input
label="To"
label={localize('ui.panel.config.automation.editor.triggers.type.state.to')}
name="to"
value={to}
onvalue-changed={this.onChange}

View File

@ -18,22 +18,22 @@ export default class SunTrigger extends Component {
}
/* eslint-disable camelcase */
render({ trigger }) {
render({ trigger, localize }) {
const { offset, event } = trigger;
return (
<div>
<label id="eventlabel">Event:</label>
<label id="eventlabel">{localize('ui.panel.config.automation.editor.triggers.type.sun.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-button name="sunrise">{localize('ui.panel.config.automation.editor.triggers.type.sun.sunrise')}</paper-radio-button>
<paper-radio-button name="sunset">{localize('ui.panel.config.automation.editor.triggers.type.sun.sunset')}</paper-radio-button>
</paper-radio-group>
<paper-input
label="Offset (optional)"
label={localize('ui.panel.config.automation.editor.triggers.type.sun.offset')}
name="offset"
value={offset}
onvalue-changed={this.onChange}

View File

@ -9,13 +9,13 @@ export default class TemplateTrigger extends Component {
this.onChange = onChangeEvent.bind(this, 'trigger');
}
render({ trigger }) {
render({ trigger, localize }) {
/* eslint-disable camelcase */
const { value_template } = trigger;
return (
<div>
<paper-textarea
label="Value Template"
label={localize('ui.panel.config.automation.editor.triggers.type.template.value_template')}
name="value_template"
value={value_template}
onvalue-changed={this.onChange}

View File

@ -10,12 +10,12 @@ export default class TimeTrigger extends Component {
}
/* eslint-disable camelcase */
render({ trigger }) {
render({ trigger, localize }) {
const { at } = trigger;
return (
<div>
<paper-input
label="At"
label={localize('ui.panel.config.automation.editor.triggers.type.time.at')}
name="at"
value={at}
onvalue-changed={this.onChange}

View File

@ -32,7 +32,7 @@ export default class TriggerEdit extends Component {
}
typeChanged(ev) {
const type = ev.target.selectedItem.innerHTML;
const type = ev.target.selectedItem.attributes.platform.value;
if (type !== this.props.trigger.platform) {
this.props.onChange(this.props.index, {
@ -42,27 +42,27 @@ export default class TriggerEdit extends Component {
}
}
render({ index, trigger, onChange, hass }) {
render({ index, trigger, onChange, hass, localize }) {
const Comp = TYPES[trigger.platform];
const selected = OPTIONS.indexOf(trigger.platform);
if (!Comp) {
return (
<div>
Unsupported platform: {trigger.platform}
{localize('ui.panel.config.automation.editor.triggers.unsupported_platform', 'platform', trigger.platform)}
<pre>{JSON.stringify(trigger, null, 2)}</pre>
</div>
);
}
return (
<div>
<paper-dropdown-menu-light label="Trigger Type" no-animations>
<paper-dropdown-menu-light label={localize('ui.panel.config.automation.editor.triggers.type_select')} no-animations>
<paper-listbox
slot="dropdown-content"
selected={selected}
oniron-select={this.typeChanged}
>
{OPTIONS.map(opt => <paper-item>{opt}</paper-item>)}
{OPTIONS.map(opt => <paper-item platform={opt}>{localize(`ui.panel.config.automation.editor.triggers.type.${opt}.label`)}</paper-item>)}
</paper-listbox>
</paper-dropdown-menu-light>
<Comp
@ -70,6 +70,7 @@ export default class TriggerEdit extends Component {
trigger={trigger}
onChange={onChange}
hass={hass}
localize={localize}
/>
</div>
);

View File

@ -11,7 +11,7 @@ export default class TriggerRow extends Component {
onDelete() {
// eslint-disable-next-line
if (confirm('Sure you want to delete?')) {
if (confirm(this.props.localize('ui.panel.config.automation.editor.triggers.delete_confirm'))) {
this.props.onChange(this.props.index, null);
}
}
@ -31,8 +31,8 @@ export default class TriggerRow extends Component {
slot="dropdown-trigger"
/>
<paper-listbox slot="dropdown-content">
<paper-item disabled>Duplicate</paper-item>
<paper-item onTap={this.onDelete}>Delete</paper-item>
<paper-item disabled>{props.localize('ui.panel.config.automation.editor.triggers.duplicate')}</paper-item>
<paper-item onTap={this.onDelete}>{props.localize('ui.panel.config.automation.editor.triggers.delete')}</paper-item>
</paper-listbox>
</paper-menu-button>
</div>

View File

@ -40,12 +40,12 @@ export default class ZoneTrigger extends Component {
}
/* eslint-disable camelcase */
render({ trigger, hass }) {
render({ trigger, hass, localize }) {
const { entity_id, zone, event } = trigger;
return (
<div>
<ha-entity-picker
label='Entity with location'
label={localize('ui.panel.config.automation.editor.triggers.type.zone.entity')}
value={entity_id}
onChange={this.entityPicked}
hass={hass}
@ -53,21 +53,21 @@ export default class ZoneTrigger extends Component {
entityFilter={zoneAndLocationFilter}
/>
<ha-entity-picker
label='Zone'
label={localize('ui.panel.config.automation.editor.triggers.type.zone.zone')}
value={zone}
onChange={this.zonePicked}
hass={hass}
allowCustomEntity
domainFilter='zone'
/>
<label id="eventlabel">Event:</label>
<label id="eventlabel">{localize('ui.panel.config.automation.editor.triggers.type.zone.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-button name="enter">{localize('ui.panel.config.automation.editor.triggers.type.zone.enter')}</paper-radio-button>
<paper-radio-button name="leave">{localize('ui.panel.config.automation.editor.triggers.type.zone.leave')}</paper-radio-button>
</paper-radio-group>
</div>
);

View File

@ -17,6 +17,7 @@
<link rel='import' href='../../../src/components/entity/ha-entity-picker.html'>
<link rel='import' href='../../../src/components/ha-combo-box.html'>
<link rel='import' href='../../../src/components/ha-markdown.html'>
<link rel='import' href='../../../src/components/ha-service-picker.html'>
<link rel='import' href='../../../src/layouts/ha-app-layout.html'>
<link rel='import' href='../../../src/util/hass-mixins.html'>
@ -89,7 +90,7 @@
icon='mdi:arrow-left'
on-click='backTapped'
></paper-icon-button>
<div main-title>Automation [[name]]</div>
<div main-title>[[name]]</div>
</app-toolbar>
</app-header>
@ -103,7 +104,7 @@
is-wide$='[[isWide]]'
dirty$='[[dirty]]'
icon='mdi:content-save'
title='Save'
title="[[localize('ui.panel.config.automation.editor.save')]]"
on-click='saveAutomation'
></paper-fab>
</ha-app-layout>
@ -112,7 +113,12 @@
</dom-module>
<script>
class HaAutomationEditor extends window.hassMixins.EventsMixin(Polymer.Element) {
/*
* @appliesMixin window.hassMixins.LocalizeMixin
* @appliesMixin window.hassMixins.EventsMixin
*/
class HaAutomationEditor extends
window.hassMixins.LocalizeMixin(window.hassMixins.EventsMixin(Polymer.Element)) {
static get is() { return 'ha-automation-editor'; }
static get properties() {
@ -158,7 +164,7 @@ class HaAutomationEditor extends window.hassMixins.EventsMixin(Polymer.Element)
name: {
type: String,
computed: 'computeName(automation)'
computed: 'computeName(automation, localize)'
},
isWide: {
@ -231,7 +237,7 @@ class HaAutomationEditor extends window.hassMixins.EventsMixin(Polymer.Element)
}
this.dirty = false;
this.config = {
alias: 'New Automation',
alias: this.localize('ui.panel.config.automation.editor.default_name'),
trigger: [
{ platform: 'state' },
],
@ -246,7 +252,7 @@ class HaAutomationEditor extends window.hassMixins.EventsMixin(Polymer.Element)
backTapped() {
if (this.dirty &&
// eslint-disable-next-line
!confirm('You have unsaved changes. Are you sure you want to leave?')) {
!confirm(this.localize('ui.panel.config.automation.editor.unsaved_confirm'))) {
return;
}
history.back();
@ -261,6 +267,7 @@ class HaAutomationEditor extends window.hassMixins.EventsMixin(Polymer.Element)
onChange: this.configChanged,
isWide: this.isWide,
hass: this.hass,
localize: this.localize,
}, this._rendered);
this._renderScheduled = false;
});
@ -281,8 +288,10 @@ class HaAutomationEditor extends window.hassMixins.EventsMixin(Polymer.Element)
}.bind(this));
}
computeName(automation) {
return automation && window.hassUtil.computeStateName(automation);
computeName(automation, localize) {
return automation
? window.hassUtil.computeStateName(automation)
: localize('ui.panel.config.automation.editor.default_name');
}
}

View File

@ -19,10 +19,6 @@
display: block;
}
/* .content {
padding: 16px;
}
*/
paper-item {
cursor: pointer;
}
@ -42,6 +38,10 @@
a {
color: var(--primary-color);
}
ha-markdown p {
margin: 0px;
}
</style>
<ha-app-layout has-scrolling-region>
@ -58,16 +58,15 @@
<ha-config-section
is-wide='[[isWide]]'
>
<div slot='header'>Automation Editor</div>
<div slot='header'>[[localize('ui.panel.config.automation.picker.header')]]</div>
<div slot='introduction'>
The automation editor allows you to create and edit automations.
Please read <a href='https://home-assistant.io/docs/automation/editor/' target='_blank'>the instructions</a> to make sure that you have configured Home Assistant correctly.
<ha-markdown content="[[localize('ui.panel.config.automation.picker.introduction')]]"></ha-markdown>
</div>
<paper-card heading='Pick automation to edit'>
<paper-card heading="[[localize('ui.panel.config.automation.picker.pick_automation')]]">
<template is='dom-if' if='[[!automations.length]]'>
<div class='card-content'>
<p>We couldn't find any editable automations.</p>
<p>[[localize('ui.panel.config.automation.picker.no_automations')]]</p>
</div>
</template>
<template is='dom-repeat' items='[[automations]]' as='automation'>
@ -85,7 +84,7 @@
<paper-fab slot="fab"
is-wide$='[[isWide]]'
icon='mdi:plus'
title='Add Automation'
title="[[localize('ui.panel.config.automation.picker.add_automation')]]"
on-click='addAutomation'
></paper-fab>
</ha-app-layout>

View File

@ -110,7 +110,10 @@
</dom-module>
<script>
class HaScriptEditor extends window.hassMixins.NavigateMixin(Polymer.Element) {
/*
* @appliesMixin window.hassMixins.LocalizeMixin
*/
class HaScriptEditor extends window.hassMixins.LocalizeMixin(Polymer.Element) {
static get is() { return 'ha-script-editor'; }
static get properties() {
@ -256,6 +259,7 @@ class HaScriptEditor extends window.hassMixins.NavigateMixin(Polymer.Element) {
onChange: this.configChanged,
isWide: this.isWide,
hass: this.hass,
localize: this.localize,
}, this._rendered);
this._renderScheduled = false;
});

View File

@ -3,6 +3,8 @@
<link rel="import" href="../../../bower_components/paper-item/paper-icon-item.html">
<link rel="import" href="../../../bower_components/paper-item/paper-item-body.html">
<link rel='import' href='../../../src/util/hass-mixins.html'>
<link rel="import" href="./state-badge.html">
<dom-module id="ha-entity-picker">
@ -28,7 +30,7 @@
>
<paper-input
autofocus="[[autofocus]]"
label="[[label]]"
label="[[_computeLabel(label, localize)]]"
class="input"
value='[[value]]'
disabled='[[disabled]]'
@ -66,7 +68,10 @@
</dom-module>
<script>
class HaEntityPicker extends Polymer.Element {
/*
* @appliesMixin window.hassMixins.LocalizeMixin
*/
class HaEntityPicker extends window.hassMixins.LocalizeMixin(Polymer.Element) {
static get is() { return 'ha-entity-picker'; }
static get properties() {
@ -87,7 +92,6 @@ class HaEntityPicker extends Polymer.Element {
autofocus: Boolean,
label: {
type: String,
value: 'Entity',
},
value: {
type: String,
@ -110,6 +114,12 @@ class HaEntityPicker extends Polymer.Element {
};
}
_computeLabel(label, localize) {
return label === undefined
? localize('ui.components.entity.entity-picker.entity')
: label;
}
_computeStates(hass, domainFilter, entityFilter) {
if (!hass) return [];

View File

@ -5,7 +5,7 @@
<dom-module id="ha-service-picker">
<template>
<ha-combo-box
label='Service'
label="[[localize('ui.components.service-picker.service')]]"
items='[[_services]]'
value='{{value}}'
allow-custom-value
@ -14,7 +14,10 @@
</dom-module>
<script>
class HaServicePicker extends Polymer.Element {
/*
* @appliesMixin window.hassMixins.LocalizeMixin
*/
class HaServicePicker extends window.hassMixins.LocalizeMixin(Polymer.Element) {
static get is() { return 'ha-service-picker'; }
static get properties() {

View File

@ -313,6 +313,16 @@
"loading": "Loading",
"cancel": "Cancel"
},
"components": {
"entity": {
"entity-picker": {
"entity": "Entity"
}
},
"service-picker": {
"service": "Service"
}
},
"duration": {
"second": "{count} {count, plural,\n one {second}\n other {seconds}\n}",
"day": "{count} {count, plural,\n one {day}\n other {days}\n}",
@ -389,7 +399,161 @@
},
"automation": {
"caption": "Automation",
"description": "Create and edit automations"
"description": "Create and edit automations",
"picker": {
"header": "Automation editor",
"introduction": "The automation editor allows you to create and edit automations. Please read [the instructions](https://home-assistant.io/docs/automation/editor/) to make sure that you have configured Home Assistant correctly.",
"pick_automation": "Pick automation to edit",
"no_automations": "We couldnt find any editable automations",
"add_automation": "Add automation"
},
"editor": {
"introduction": "Use automations to bring your home alive",
"default_name": "New Automation",
"save": "Save",
"unsaved_confirm": "You have unsaved changes. Are you sure you want to leave?",
"alias": "Name",
"triggers": {
"header": "Triggers",
"introduction": "Triggers are what starts the processing of an automation rule. It is possible to specify multiple triggers for the same rule. Once a trigger starts, Home Assistant will validate the conditions, if any, and call the action.\n\n[Learn more about triggers.](https://home-assistant.io/docs/automation/trigger/)",
"add": "Add trigger",
"duplicate": "Duplicate",
"delete": "[%key:ui::panel::mailbox::delete_button%]",
"delete_confirm": "Sure you want to delete?",
"unsupported_platform": "Unsupported platform: {platform}",
"type_select": "Trigger type",
"type": {
"event": {
"label": "Event",
"event_type": "Event type",
"event_data": "Event data"
},
"state": {
"label": "State",
"from": "From",
"to": "To"
},
"homeassistant": {
"label": "Home Assistant",
"event": "Event:",
"start": "Start",
"shutdown": "Shutdown"
},
"mqtt": {
"label": "MQTT",
"topic": "Topic",
"payload": "Payload (optional)"
},
"numeric_state": {
"label": "Numeric state",
"above": "Above",
"below": "Below",
"value_template": "Value template (optional)"
},
"sun": {
"label": "Sun",
"event": "[%key:ui::panel::config::automation::editor::triggers::type::homeassistant::event%]",
"sunrise": "Sunrise",
"sunset": "Sunset",
"offset": "Offset (optional)"
},
"template": {
"label": "Template",
"value_template": "Value template"
},
"time": {
"label": "Time",
"at": "At"
},
"zone": {
"label": "Zone",
"entity": "Entity with location",
"zone": "[%key:ui::panel::config::automation::editor::triggers::type::zone::label%]",
"event": "[%key:ui::panel::config::automation::editor::triggers::type::homeassistant::event%]",
"enter": "Enter",
"leave": "Leave"
}
}
},
"conditions": {
"header": "Conditions",
"introduction": "Conditions are an optional part of an automation rule and can be used to prevent an action from happening when triggered. Conditions look very similar to triggers but are very different. A trigger will look at events happening in the system while a condition only looks at how the system looks right now. A trigger can observe that a switch is being turned on. A condition can only see if a switch is currently on or off.\n\n[Learn more about conditions.](https://home-assistant.io/docs/scripts/conditions/)",
"add": "Add condition",
"duplicate": "[%key:ui::panel::config::automation::editor::triggers::duplicate%]",
"delete": "[%key:ui::panel::mailbox::delete_button%]",
"delete_confirm": "[%key:ui::panel::config::automation::editor::triggers::delete_confirm%]",
"unsupported_condition": "Unsupported condition: {condition}",
"type_select": "Condition type",
"type": {
"state": {
"label": "[%key:ui::panel::config::automation::editor::triggers::type::state::label%]",
"state": "[%key:ui::panel::config::automation::editor::triggers::type::state::label%]"
},
"numeric_state": {
"label": "[%key:ui::panel::config::automation::editor::triggers::type::numeric_state::label%]",
"above": "[%key:ui::panel::config::automation::editor::triggers::type::numeric_state::above%]",
"below": "[%key:ui::panel::config::automation::editor::triggers::type::numeric_state::below%]",
"value_template": "[%key:ui::panel::config::automation::editor::triggers::type::numeric_state::value_template%]"
},
"sun": {
"label": "[%key:ui::panel::config::automation::editor::triggers::type::sun::label%]",
"before": "Before:",
"after": "After:",
"before_offset": "Before offset (optional)",
"after_offset": "After offset (optional)",
"sunrise": "Sunrise",
"sunset": "Sunset"
},
"template": {
"label": "[%key:ui::panel::config::automation::editor::triggers::type::template::label%]",
"value_template": "[%key:ui::panel::config::automation::editor::triggers::type::template::value_template%]"
},
"time": {
"label": "[%key:ui::panel::config::automation::editor::triggers::type::time::label%]",
"after": "After",
"before": "Before"
},
"zone": {
"label": "[%key:ui::panel::config::automation::editor::triggers::type::zone::label%]",
"entity": "[%key:ui::panel::config::automation::editor::triggers::type::zone::entity%]",
"zone": "[%key:ui::panel::config::automation::editor::triggers::type::zone::label%]"
}
}
},
"actions": {
"header": "Actions",
"introduction": "The actions are what Home Assistant will do when the automation is triggered.\n\n[Learn more about actions.](https://home-assistant.io/docs/automation/action/)",
"add": "Add action",
"duplicate": "[%key:ui::panel::config::automation::editor::triggers::duplicate%]",
"delete": "[%key:ui::panel::mailbox::delete_button%]",
"delete_confirm": "[%key:ui::panel::config::automation::editor::triggers::delete_confirm%]",
"unsupported_action": "Unsupported action: {action}",
"type_select": "Action type",
"type": {
"service": {
"label": "Call service",
"service_data": "Service data"
},
"delay": {
"label": "Delay",
"delay": "[%key:ui::panel::config::automation::editor::actions::type::delay::label%]"
},
"wait_template": {
"label": "Wait",
"wait_template": "Wait Template",
"timeout": "Timeout (optional)"
},
"condition": {
"label": "Condition"
},
"event": {
"label": "Fire event",
"event": "[%key:ui::panel::config::automation::editor::triggers::type::homeassistant::event%]",
"service_data": "[%key:ui::panel::config::automation::editor::actions::type::service::service_data%]"
}
}
}
}
},
"script": {
"caption": "Script",