mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 01:36:49 +00:00
Translations for core states (#575)
* Fix deeper nested translations build * Make fallback to message optional * Use translated state names * Remove unused switch cases * Use src en.json as fallback instead of downloaded * Use separate translations for badge states * Eliminate unnecessary StatesMixin * Remove now unused localize fallback parameter * Fix capitalization to match material guidelines * Move media player text generation back to model * Make localize args object * Change Mixin to use computed function * Revert to normal args spread for haLocalize * Rename to computeHaLocalize * Allow state to default for badge and media player * Denormalize en.json with Lokalise placeholders * Fix cleanups missed after master merge * Split zwave query stage states to separate keys * Throw error to fail gulp build * Fix zwave template and regression on general state
This commit is contained in:
parent
926c46b701
commit
056e9e0d74
@ -14,10 +14,10 @@ const outDir = 'build-translations';
|
|||||||
const tasks = [];
|
const tasks = [];
|
||||||
|
|
||||||
function recursiveFlatten(prefix, data) {
|
function recursiveFlatten(prefix, data) {
|
||||||
var output = {};
|
let output = {};
|
||||||
Object.keys(data).forEach(function (key) {
|
Object.keys(data).forEach(function (key) {
|
||||||
if (typeof (data[key]) === 'object') {
|
if (typeof (data[key]) === 'object') {
|
||||||
output = Object.assign({}, output, recursiveFlatten(key + '.', data[key]));
|
output = Object.assign({}, output, recursiveFlatten(prefix + key + '.', data[key]));
|
||||||
} else {
|
} else {
|
||||||
output[prefix + key] = data[key];
|
output[prefix + key] = data[key];
|
||||||
}
|
}
|
||||||
@ -29,16 +29,68 @@ function flatten(data) {
|
|||||||
return recursiveFlatten('', data);
|
return recursiveFlatten('', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
let taskName = 'build-merged-translations';
|
/**
|
||||||
|
* Replace Lokalise key placeholders with their actual values.
|
||||||
|
*
|
||||||
|
* We duplicate the behavior of Lokalise here so that placeholders can
|
||||||
|
* be included in src/translations/en.json, but still be usable while
|
||||||
|
* developing locally.
|
||||||
|
*
|
||||||
|
* @link https://docs.lokalise.co/article/KO5SZWLLsy-key-referencing
|
||||||
|
*/
|
||||||
|
const re_key_reference = /\[%key:([^%]+)%\]/;
|
||||||
|
function lokalise_transform (data, original) {
|
||||||
|
const output = {};
|
||||||
|
Object.entries(data).forEach(([key, value]) => {
|
||||||
|
if (value instanceof Object) {
|
||||||
|
output[key] = lokalise_transform(value, original);
|
||||||
|
} else {
|
||||||
|
output[key] = value.replace(re_key_reference, (match, key) => {
|
||||||
|
const replace = key.split('::').reduce((tr, k) => tr[k], original);
|
||||||
|
if (typeof replace !== 'string') {
|
||||||
|
throw Error(`Invalid key placeholder ${key} in src/translations/en.json`);
|
||||||
|
}
|
||||||
|
return replace;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This task will build a master translation file, to be used as the base for
|
||||||
|
* all languages. This starts with src/translations/en.json, and replaces all
|
||||||
|
* Lokalise key placeholders with their target values. Under normal circumstances,
|
||||||
|
* this will be the same as translations/en.json However, we build it here to
|
||||||
|
* facilitate both making changes in development mode, and to ensure that the
|
||||||
|
* project is buildable immediately after merging new translation keys, since
|
||||||
|
* the Lokalise update to translations/en.json will not happen immediately.
|
||||||
|
*/
|
||||||
|
let taskName = 'build-master-translation';
|
||||||
gulp.task(taskName, function () {
|
gulp.task(taskName, function () {
|
||||||
|
return gulp.src('src/translations/en.json')
|
||||||
|
.pipe(transform(function(data, file) {
|
||||||
|
return lokalise_transform(data, data);
|
||||||
|
}))
|
||||||
|
.pipe(rename('translation-master.json'))
|
||||||
|
.pipe(gulp.dest(outDir));
|
||||||
|
});
|
||||||
|
tasks.push(taskName);
|
||||||
|
|
||||||
|
taskName = 'build-merged-translations';
|
||||||
|
gulp.task(taskName, ['build-master-translation'], function () {
|
||||||
return gulp.src(inDir + '/*.json')
|
return gulp.src(inDir + '/*.json')
|
||||||
.pipe(foreach(function (stream, file) {
|
.pipe(foreach(function(stream, file) {
|
||||||
// For each language generate a merged json file. It begins with en.json as
|
// For each language generate a merged json file. It begins with the master
|
||||||
// a failsafe for untranslated strings, and merges all parent tags into one
|
// translation as a failsafe for untranslated strings, and merges all parent
|
||||||
// file for each specific subtag
|
// tags into one file for each specific subtag
|
||||||
|
//
|
||||||
|
// TODO: This is a naive interpretation of BCP47 that should be improved.
|
||||||
|
// Will be OK for now as long as we don't have anything more complicated
|
||||||
|
// than a base translation + region.
|
||||||
const tr = path.basename(file.history[0], '.json');
|
const tr = path.basename(file.history[0], '.json');
|
||||||
const subtags = tr.split('-');
|
const subtags = tr.split('-');
|
||||||
const src = [inDir + '/en.json']; // Start with en as a fallback for missing translations
|
const src = [outDir + '/translation-master.json'];
|
||||||
for (let i = 1; i <= subtags.length; i++) {
|
for (let i = 1; i <= subtags.length; i++) {
|
||||||
const lang = subtags.slice(0, i).join('-');
|
const lang = subtags.slice(0, i).join('-');
|
||||||
src.push(inDir + '/' + lang + '.json');
|
src.push(inDir + '/' + lang + '.json');
|
||||||
|
@ -153,8 +153,8 @@
|
|||||||
|
|
||||||
<div class='caption'>
|
<div class='caption'>
|
||||||
[[computeStateName(stateObj)]]
|
[[computeStateName(stateObj)]]
|
||||||
<div class='title'>[[playerObj.primaryText]]</div>
|
<div class='title'>[[computePrimaryText(haLocalize, playerObj)]]</div>
|
||||||
[[playerObj.secondaryText]]<br />
|
[[playerObj.secondaryTitle]]<br />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -206,7 +206,8 @@
|
|||||||
</dom-module>
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
class HaMediaPlayerCard extends window.hassMixins.EventsMixin(Polymer.Element) {
|
class HaMediaPlayerCard extends
|
||||||
|
window.hassMixins.LocalizeMixin(window.hassMixins.EventsMixin(Polymer.Element)) {
|
||||||
static get is() { return 'ha-media_player-card'; }
|
static get is() { return 'ha-media_player-card'; }
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
@ -309,6 +310,12 @@ class HaMediaPlayerCard extends window.hassMixins.EventsMixin(Polymer.Element) {
|
|||||||
return new window.MediaPlayerEntity(hass, stateObj);
|
return new window.MediaPlayerEntity(hass, stateObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
computePrimaryText(haLocalize, playerObj) {
|
||||||
|
return playerObj.primaryTitle
|
||||||
|
|| haLocalize('state.media_player', playerObj.stateObj.state)
|
||||||
|
|| haLocalize('state.default', playerObj.stateObj.state) || playerObj.stateObj.state;
|
||||||
|
}
|
||||||
|
|
||||||
computePlaybackControlIcon(playerObj) {
|
computePlaybackControlIcon(playerObj) {
|
||||||
if (playerObj.isPlaying) {
|
if (playerObj.isPlaying) {
|
||||||
return playerObj.supportsPause ? 'mdi:pause' : 'mdi:stop';
|
return playerObj.supportsPause ? 'mdi:pause' : 'mdi:stop';
|
||||||
|
@ -40,22 +40,24 @@
|
|||||||
value='[[computeValue(state)]]'
|
value='[[computeValue(state)]]'
|
||||||
icon='[[computeIcon(state)]]'
|
icon='[[computeIcon(state)]]'
|
||||||
image='[[computeImage(state)]]'
|
image='[[computeImage(state)]]'
|
||||||
label='[[computeLabel(state)]]'
|
label='[[computeLabel(haLocalize, state)]]'
|
||||||
description='[[computeDescription(state)]]'
|
description='[[computeDescription(state)]]'
|
||||||
></ha-label-badge>
|
></ha-label-badge>
|
||||||
</template>
|
</template>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
class HaStateLabelBadge extends window.hassMixins.EventsMixin(Polymer.Element) {
|
/*
|
||||||
|
* @appliesMixin window.hassMixins.LocalizeMixin
|
||||||
|
* @appliesMixin window.hassMixins.EventsMixin
|
||||||
|
*/
|
||||||
|
class HaStateLabelBadge extends
|
||||||
|
window.hassMixins.LocalizeMixin(window.hassMixins.EventsMixin(Polymer.Element)) {
|
||||||
static get is() { return 'ha-state-label-badge'; }
|
static get is() { return 'ha-state-label-badge'; }
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {
|
hass: Object,
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
|
|
||||||
state: {
|
state: {
|
||||||
type: Object,
|
type: Object,
|
||||||
observer: 'stateChanged',
|
observer: 'stateChanged',
|
||||||
@ -101,7 +103,7 @@ class HaStateLabelBadge extends window.hassMixins.EventsMixin(Polymer.Element) {
|
|||||||
if (state.state === 'unavailable') {
|
if (state.state === 'unavailable') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var domain = window.hassUtil.computeDomain(state);
|
const domain = window.hassUtil.computeDomain(state);
|
||||||
switch (domain) {
|
switch (domain) {
|
||||||
case 'alarm_control_panel':
|
case 'alarm_control_panel':
|
||||||
if (state.state === 'pending') {
|
if (state.state === 'pending') {
|
||||||
@ -131,26 +133,16 @@ class HaStateLabelBadge extends window.hassMixins.EventsMixin(Polymer.Element) {
|
|||||||
return state.attributes.entity_picture || null;
|
return state.attributes.entity_picture || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
computeLabel(state) {
|
computeLabel(haLocalize, state) {
|
||||||
if (state.state === 'unavailable') {
|
const domain = window.hassUtil.computeDomain(state);
|
||||||
return 'unavai';
|
if (state.state === 'unavailable' ||
|
||||||
}
|
['device_tracker', 'alarm_control_panel'].includes(domain)) {
|
||||||
switch (window.hassUtil.computeDomain(state)) {
|
// Localize the state with a special state_badge namespace, which has variations of
|
||||||
case 'device_tracker':
|
// the state translations that are truncated to fit within the badge label. Translations
|
||||||
return state.state === 'not_home' ? 'Away' : state.state;
|
// are only added for device_tracker and alarm_control_panel.
|
||||||
case 'alarm_control_panel':
|
return haLocalize(`state_badge.${domain}`, state.state) || haLocalize('state_badge.default', state.state) || state.state;
|
||||||
if (state.state === 'pending') {
|
|
||||||
return 'pend';
|
|
||||||
} else if (state.state === 'armed_away' || state.state === 'armed_home') {
|
|
||||||
return 'armed';
|
|
||||||
} else if (state.state === 'triggered') {
|
|
||||||
return 'trig';
|
|
||||||
}
|
|
||||||
// state == 'disarmed'
|
|
||||||
return 'disarm';
|
|
||||||
default:
|
|
||||||
return state.attributes.unit_of_measurement || null;
|
|
||||||
}
|
}
|
||||||
|
return state.attributes.unit_of_measurement || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
computeDescription(state) {
|
computeDescription(state) {
|
||||||
|
@ -106,19 +106,19 @@
|
|||||||
<paper-listbox attr-for-selected='data-panel' selected='[[route.panel]]'>
|
<paper-listbox attr-for-selected='data-panel' selected='[[route.panel]]'>
|
||||||
<paper-icon-item on-tap='menuClicked' data-panel='states'>
|
<paper-icon-item on-tap='menuClicked' data-panel='states'>
|
||||||
<iron-icon slot="item-icon" icon='mdi:apps'></iron-icon>
|
<iron-icon slot="item-icon" icon='mdi:apps'></iron-icon>
|
||||||
<span class='item-text'>{{localize('panel', 'states')}}</span>
|
<span class='item-text'>[[haLocalize('panel', 'states')]]</span>
|
||||||
</paper-icon-item>
|
</paper-icon-item>
|
||||||
|
|
||||||
<template is='dom-repeat' items='[[panels]]'>
|
<template is='dom-repeat' items='[[panels]]'>
|
||||||
<paper-icon-item on-tap='menuClicked' data-panel$='[[item.url_path]]'>
|
<paper-icon-item on-tap='menuClicked' data-panel$='[[item.url_path]]'>
|
||||||
<iron-icon slot="item-icon" icon='[[item.icon]]'></iron-icon>
|
<iron-icon slot="item-icon" icon='[[item.icon]]'></iron-icon>
|
||||||
<span class='item-text'>{{localize('panel', item.title)}}</span>
|
<span class='item-text'>[[computePanelName(haLocalize, item)]]</span>
|
||||||
</paper-icon-item>
|
</paper-icon-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<paper-icon-item on-tap='menuClicked' data-panel='logout' class='logout'>
|
<paper-icon-item on-tap='menuClicked' data-panel='logout' class='logout'>
|
||||||
<iron-icon slot="item-icon" icon='mdi:exit-to-app'></iron-icon>
|
<iron-icon slot="item-icon" icon='mdi:exit-to-app'></iron-icon>
|
||||||
<span class='item-text'>{{localize('panel', 'log_out')}}</span>
|
<span class='item-text'>[[haLocalize('panel', 'log_out')]]</span>
|
||||||
</paper-icon-item>
|
</paper-icon-item>
|
||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
|
|
||||||
@ -214,6 +214,10 @@ class HaSidebar extends
|
|||||||
return hass.config.core.components.indexOf('mqtt') !== -1;
|
return hass.config.core.components.indexOf('mqtt') !== -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
computePanelName(haLocalize, panel) {
|
||||||
|
return haLocalize('panel', panel.title) || panel.title;
|
||||||
|
}
|
||||||
|
|
||||||
computePanels(hass) {
|
computePanels(hass) {
|
||||||
var panels = hass.config.panels;
|
var panels = hass.config.panels;
|
||||||
var sortValue = {
|
var sortValue = {
|
||||||
|
@ -20,17 +20,18 @@
|
|||||||
|
|
||||||
<div class='horizontal justified layout'>
|
<div class='horizontal justified layout'>
|
||||||
<state-info state-obj="[[stateObj]]" in-dialog='[[inDialog]]'></state-info>
|
<state-info state-obj="[[stateObj]]" in-dialog='[[inDialog]]'></state-info>
|
||||||
<div class='state'>[[computeStateDisplay(stateObj)]]</div>
|
<div class='state'>[[computeStateDisplay(haLocalize, stateObj)]]</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
class StateCardDisplay extends Polymer.Element {
|
class StateCardDisplay extends window.hassMixins.LocalizeMixin(Polymer.Element) {
|
||||||
static get is() { return 'state-card-display'; }
|
static get is() { return 'state-card-display'; }
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
|
hass: Object,
|
||||||
stateObj: Object,
|
stateObj: Object,
|
||||||
inDialog: {
|
inDialog: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@ -39,8 +40,55 @@ class StateCardDisplay extends Polymer.Element {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
computeStateDisplay(stateObj) {
|
computeStateDisplay(haLocalize, stateObj) {
|
||||||
return window.hassUtil.computeStateState(stateObj);
|
if (!stateObj._stateDisplay) {
|
||||||
|
const domain = window.hassUtil.computeDomain(stateObj);
|
||||||
|
if (domain === 'binary_sensor') {
|
||||||
|
// Try device class translation, then default binary sensor translation
|
||||||
|
stateObj._stateDisplay =
|
||||||
|
haLocalize(`state.${domain}.${stateObj.attributes.device_class}`, stateObj.state)
|
||||||
|
|| haLocalize(`state.${domain}.default`, stateObj.state);
|
||||||
|
} else if (stateObj.attributes.unit_of_measurement) {
|
||||||
|
stateObj._stateDisplay = stateObj.state + ' ' + stateObj.attributes.unit_of_measurement;
|
||||||
|
} else if (domain === 'input_datetime') {
|
||||||
|
let date;
|
||||||
|
if (!stateObj.attributes.has_time) {
|
||||||
|
date = new Date(
|
||||||
|
stateObj.attributes.year,
|
||||||
|
stateObj.attributes.month - 1,
|
||||||
|
stateObj.attributes.day
|
||||||
|
);
|
||||||
|
stateObj._stateDisplay = window.hassUtil.formatDate(date);
|
||||||
|
} else if (!stateObj.attributes.has_date) {
|
||||||
|
date = new Date(
|
||||||
|
1970, 0, 1,
|
||||||
|
stateObj.attributes.hour,
|
||||||
|
stateObj.attributes.minute
|
||||||
|
);
|
||||||
|
stateObj._stateDisplay = window.hassUtil.formatTime(date);
|
||||||
|
} else {
|
||||||
|
date = new Date(
|
||||||
|
stateObj.attributes.year, stateObj.attributes.month - 1,
|
||||||
|
stateObj.attributes.day, stateObj.attributes.hour,
|
||||||
|
stateObj.attributes.minute
|
||||||
|
);
|
||||||
|
stateObj._stateDisplay = window.hassUtil.formatDateTime(date);
|
||||||
|
}
|
||||||
|
} else if (domain === 'zwave') {
|
||||||
|
if (['initializing', 'dead'].includes(stateObj.state)) {
|
||||||
|
stateObj._stateDisplay = haLocalize('state.zwave.query_stage', stateObj.state, 'query_stage', stateObj.attributes.query_stage);
|
||||||
|
} else {
|
||||||
|
stateObj._stateDisplay = haLocalize('state.zwave.default', stateObj.state);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stateObj._stateDisplay = haLocalize(`state.${domain}`, stateObj.state);
|
||||||
|
}
|
||||||
|
// Fall back to default or raw state if nothing else matches.
|
||||||
|
stateObj._stateDisplay = stateObj._stateDisplay
|
||||||
|
|| haLocalize('state.default', stateObj.state) || stateObj.state;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateObj._stateDisplay;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define(StateCardDisplay.is, StateCardDisplay);
|
customElements.define(StateCardDisplay.is, StateCardDisplay);
|
||||||
|
@ -38,15 +38,15 @@
|
|||||||
<div class='horizontal justified layout'>
|
<div class='horizontal justified layout'>
|
||||||
<state-info state-obj="[[stateObj]]" in-dialog='[[inDialog]]'></state-info>
|
<state-info state-obj="[[stateObj]]" in-dialog='[[inDialog]]'></state-info>
|
||||||
<div class='state'>
|
<div class='state'>
|
||||||
<div class='main-text' take-height$='[[!playerObj.secondaryText]]'>[[playerObj.primaryText]]</div>
|
<div class='main-text' take-height$='[[!playerObj.secondaryTitle]]'>[[computePrimaryText(haLocalize, playerObj)]]</div>
|
||||||
<div class='secondary-text'>[[playerObj.secondaryText]]</div>
|
<div class='secondary-text'>[[playerObj.secondaryTitle]]</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
class StateCardMediaPlayer extends Polymer.Element {
|
class StateCardMediaPlayer extends window.hassMixins.LocalizeMixin(Polymer.Element) {
|
||||||
static get is() { return 'state-card-media_player'; }
|
static get is() { return 'state-card-media_player'; }
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@ -67,6 +67,12 @@ class StateCardMediaPlayer extends Polymer.Element {
|
|||||||
computePlayerObj(hass, stateObj) {
|
computePlayerObj(hass, stateObj) {
|
||||||
return new window.MediaPlayerEntity(hass, stateObj);
|
return new window.MediaPlayerEntity(hass, stateObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
computePrimaryText(haLocalize, playerObj) {
|
||||||
|
return playerObj.primaryTitle
|
||||||
|
|| haLocalize('state.media_player', playerObj.stateObj.state)
|
||||||
|
|| haLocalize('state.default', playerObj.stateObj.state) || playerObj.stateObj.state;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
customElements.define(StateCardMediaPlayer.is, StateCardMediaPlayer);
|
customElements.define(StateCardMediaPlayer.is, StateCardMediaPlayer);
|
||||||
</script>
|
</script>
|
||||||
|
@ -8,5 +8,209 @@
|
|||||||
"log_out": "Log out",
|
"log_out": "Log out",
|
||||||
"mailbox": "Mailbox",
|
"mailbox": "Mailbox",
|
||||||
"shopping_list": "Shopping list"
|
"shopping_list": "Shopping list"
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"default": {
|
||||||
|
"off": "Off",
|
||||||
|
"on": "On",
|
||||||
|
"unknown": "Unknown",
|
||||||
|
"unavailable": "Unavailable"
|
||||||
|
},
|
||||||
|
"alarm_control_panel": {
|
||||||
|
"armed": "Armed",
|
||||||
|
"disarmed": "Disarmed",
|
||||||
|
"armed_home": "Armed home",
|
||||||
|
"armed_away": "Armed away",
|
||||||
|
"armed_night": "Armed night",
|
||||||
|
"pending": "Pending",
|
||||||
|
"arming": "Arming",
|
||||||
|
"disarming": "Disarming",
|
||||||
|
"triggered": "Triggered"
|
||||||
|
},
|
||||||
|
"automation": {
|
||||||
|
"off": "[%key:state::default::off%]",
|
||||||
|
"on": "[%key:state::default::on%]"
|
||||||
|
},
|
||||||
|
"binary_sensor": {
|
||||||
|
"default": {
|
||||||
|
"off": "[%key:state::default::off%]",
|
||||||
|
"on": "[%key:state::default::on%]"
|
||||||
|
},
|
||||||
|
"moisture": {
|
||||||
|
"off": "Dry",
|
||||||
|
"on": "Wet"
|
||||||
|
},
|
||||||
|
"gas": {
|
||||||
|
"off": "Clear",
|
||||||
|
"on": "Detected"
|
||||||
|
},
|
||||||
|
"motion": {
|
||||||
|
"off": "[%key:state::binary_sensor::gas::off%]",
|
||||||
|
"on": "[%key:state::binary_sensor::gas::on%]"
|
||||||
|
},
|
||||||
|
"occupancy": {
|
||||||
|
"off": "[%key:state::binary_sensor::gas::off%]",
|
||||||
|
"on": "[%key:state::binary_sensor::gas::on%]"
|
||||||
|
},
|
||||||
|
"smoke": {
|
||||||
|
"off": "[%key:state::binary_sensor::gas::off%]",
|
||||||
|
"on": "[%key:state::binary_sensor::gas::on%]"
|
||||||
|
},
|
||||||
|
"sound": {
|
||||||
|
"off": "[%key:state::binary_sensor::gas::off%]",
|
||||||
|
"on": "[%key:state::binary_sensor::gas::on%]"
|
||||||
|
},
|
||||||
|
"vibration": {
|
||||||
|
"off": "[%key:state::binary_sensor::gas::off%]",
|
||||||
|
"on": "[%key:state::binary_sensor::gas::on%]"
|
||||||
|
},
|
||||||
|
"opening": {
|
||||||
|
"off": "Closed",
|
||||||
|
"on": "Open"
|
||||||
|
},
|
||||||
|
"safety": {
|
||||||
|
"off": "Safe",
|
||||||
|
"on": "Unsafe"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"calendar": {
|
||||||
|
"off": "[%key:state::default::off%]",
|
||||||
|
"on": "[%key:state::default::on%]"
|
||||||
|
},
|
||||||
|
"camera": {
|
||||||
|
"recording": "Recording",
|
||||||
|
"streaming": "Streaming",
|
||||||
|
"idle": "Idle"
|
||||||
|
},
|
||||||
|
"climate": {
|
||||||
|
"off": "[%key:state::default::off%]",
|
||||||
|
"on": "[%key:state::default::on%]",
|
||||||
|
"heat": "Heat",
|
||||||
|
"cool": "Cool",
|
||||||
|
"idle": "Idle",
|
||||||
|
"auto": "Auto",
|
||||||
|
"dry": "Dry",
|
||||||
|
"fan_only": "Fan only",
|
||||||
|
"eco": "Eco",
|
||||||
|
"electric": "Electric",
|
||||||
|
"performance": "Performance",
|
||||||
|
"high_demand": "High demand",
|
||||||
|
"heat_pump": "Heat pump",
|
||||||
|
"gas": "Gas"
|
||||||
|
},
|
||||||
|
"configurator": {
|
||||||
|
"configure": "Configure",
|
||||||
|
"configured": "Configured"
|
||||||
|
},
|
||||||
|
"cover": {
|
||||||
|
"open": "Open",
|
||||||
|
"opening": "Opening",
|
||||||
|
"closed": "Closed",
|
||||||
|
"closing": "Closing",
|
||||||
|
"stopped": "Stopped"
|
||||||
|
},
|
||||||
|
"device_tracker": {
|
||||||
|
"home": "Home",
|
||||||
|
"not_home": "Away"
|
||||||
|
},
|
||||||
|
"fan": {
|
||||||
|
"off": "[%key:state::default::off%]",
|
||||||
|
"on": "[%key:state::default::on%]"
|
||||||
|
},
|
||||||
|
"group": {
|
||||||
|
"off": "[%key:state::default::off%]",
|
||||||
|
"on": "[%key:state::default::on%]",
|
||||||
|
"home": "[%key:state::device_tracker::home%]",
|
||||||
|
"not_home": "[%key:state::device_tracker::not_home%]",
|
||||||
|
"open": "[%key:state::cover::open%]",
|
||||||
|
"opening": "[%key:state::cover::opening%]",
|
||||||
|
"closed": "[%key:state::cover::closed%]",
|
||||||
|
"closing": "[%key:state::cover::closing%]",
|
||||||
|
"stopped": "[%key:state::cover::stopped%]",
|
||||||
|
"locked": "[%key:state::lock::locked%]",
|
||||||
|
"unlocked": "[%key:state::lock::unlocked%]",
|
||||||
|
"ok": "[%key:state::plant::ok%]",
|
||||||
|
"problem": "[%key:state::plant::problem%]"
|
||||||
|
},
|
||||||
|
"input_boolean": {
|
||||||
|
"off": "[%key:state::default::off%]",
|
||||||
|
"on": "[%key:state::default::on%]"
|
||||||
|
},
|
||||||
|
"light": {
|
||||||
|
"off": "[%key:state::default::off%]",
|
||||||
|
"on": "[%key:state::default::on%]"
|
||||||
|
},
|
||||||
|
"lock": {
|
||||||
|
"locked": "Locked",
|
||||||
|
"unlocked": "Unlocked"
|
||||||
|
},
|
||||||
|
"media_player": {
|
||||||
|
"off": "[%key:state::default::off%]",
|
||||||
|
"on": "[%key:state::default::on%]",
|
||||||
|
"playing": "Playing",
|
||||||
|
"paused": "Paused",
|
||||||
|
"idle": "Idle",
|
||||||
|
"standby": "Standby"
|
||||||
|
},
|
||||||
|
"plant": {
|
||||||
|
"ok": "OK",
|
||||||
|
"problem": "Problem"
|
||||||
|
},
|
||||||
|
"remote": {
|
||||||
|
"off": "[%key:state::default::off%]",
|
||||||
|
"on": "[%key:state::default::on%]"
|
||||||
|
},
|
||||||
|
"scene": {
|
||||||
|
"scening": "Scening"
|
||||||
|
},
|
||||||
|
"script": {
|
||||||
|
"off": "[%key:state::default::off%]",
|
||||||
|
"on": "[%key:state::default::on%]"
|
||||||
|
},
|
||||||
|
"sensor": {
|
||||||
|
"off": "[%key:state::default::off%]",
|
||||||
|
"on": "[%key:state::default::on%]"
|
||||||
|
},
|
||||||
|
"sun": {
|
||||||
|
"above_horizon": "Above horizon",
|
||||||
|
"below_horizon": "Below horizon"
|
||||||
|
},
|
||||||
|
"switch": {
|
||||||
|
"off": "[%key:state::default::off%]",
|
||||||
|
"on": "[%key:state::default::on%]"
|
||||||
|
},
|
||||||
|
"zwave": {
|
||||||
|
"default": {
|
||||||
|
"initializing": "Initializing",
|
||||||
|
"dead": "Dead",
|
||||||
|
"sleeping": "Sleeping",
|
||||||
|
"ready": "Ready"
|
||||||
|
},
|
||||||
|
"query_stage": {
|
||||||
|
"initializing": "[%key:state::zwave::default::initializing%] ({query_stage})",
|
||||||
|
"dead": "[%key:state::zwave::default::dead%] ({query_stage})"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"state_badge": {
|
||||||
|
"default": {
|
||||||
|
"unknown": "Unk",
|
||||||
|
"unavailable": "Unavai"
|
||||||
|
},
|
||||||
|
"alarm_control_panel": {
|
||||||
|
"armed": "Armed",
|
||||||
|
"disarmed": "Disarm",
|
||||||
|
"armed_home": "Armed",
|
||||||
|
"armed_away": "Armed",
|
||||||
|
"armed_night": "Armed",
|
||||||
|
"pending": "Pend",
|
||||||
|
"arming": "Arming",
|
||||||
|
"disarming": "Disarm",
|
||||||
|
"triggered": "Trig"
|
||||||
|
},
|
||||||
|
"device_tracker": {
|
||||||
|
"home": "[%key:state::device_tracker::home%]",
|
||||||
|
"not_home": "[%key:state::device_tracker::not_home%]"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,9 +82,7 @@ window.hassMixins.LocalizeMixin = Polymer.dedupingMixin(superClass =>
|
|||||||
class extends Polymer.mixinBehaviors([Polymer.AppLocalizeBehavior], superClass) {
|
class extends Polymer.mixinBehaviors([Polymer.AppLocalizeBehavior], superClass) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {
|
hass: Object,
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
language: {
|
language: {
|
||||||
type: String,
|
type: String,
|
||||||
computed: 'computeLanguage(hass)',
|
computed: 'computeLanguage(hass)',
|
||||||
@ -93,6 +91,10 @@ window.hassMixins.LocalizeMixin = Polymer.dedupingMixin(superClass =>
|
|||||||
type: Object,
|
type: Object,
|
||||||
computed: 'computeResources(hass)',
|
computed: 'computeResources(hass)',
|
||||||
},
|
},
|
||||||
|
haLocalize: {
|
||||||
|
type: Function,
|
||||||
|
computed: 'computeHaLocalize(localize)',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,9 +106,8 @@ window.hassMixins.LocalizeMixin = Polymer.dedupingMixin(superClass =>
|
|||||||
return hass && hass.resources;
|
return hass && hass.resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
localize(namespace, message, ...args) {
|
computeHaLocalize(localize) {
|
||||||
// Return the input message if no translation is found
|
return (namespace, message, ...args) => localize(namespace + '.' + message, ...args);
|
||||||
return super.localize(namespace + '.' + message, ...args) || message;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -471,77 +471,6 @@ window.hassUtil.sortByName = function (entityA, entityB) {
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
window.hassUtil.computeStateState = function (stateObj) {
|
|
||||||
if (!stateObj._stateDisplay) {
|
|
||||||
stateObj._stateDisplay = stateObj.state.replace(/_/g, ' ');
|
|
||||||
const domain = window.hassUtil.computeDomain(stateObj);
|
|
||||||
|
|
||||||
if (stateObj.attributes.unit_of_measurement) {
|
|
||||||
stateObj._stateDisplay += ' ' + stateObj.attributes.unit_of_measurement;
|
|
||||||
}
|
|
||||||
if (domain === 'binary_sensor') {
|
|
||||||
switch (stateObj.attributes.device_class) {
|
|
||||||
case 'moisture':
|
|
||||||
stateObj._stateDisplay = (stateObj._stateDisplay === 'off') ? 'dry' : 'wet';
|
|
||||||
break;
|
|
||||||
case 'gas':
|
|
||||||
case 'motion':
|
|
||||||
case 'occupancy':
|
|
||||||
case 'smoke':
|
|
||||||
case 'sound':
|
|
||||||
case 'vibration':
|
|
||||||
stateObj._stateDisplay = (stateObj._stateDisplay === 'off') ? 'clear' : 'detected';
|
|
||||||
break;
|
|
||||||
case 'opening':
|
|
||||||
stateObj._stateDisplay = (stateObj._stateDisplay === 'off') ? 'closed' : 'open';
|
|
||||||
break;
|
|
||||||
case 'safety':
|
|
||||||
stateObj._stateDisplay = (stateObj._stateDisplay === 'off') ? 'safe' : 'unsafe';
|
|
||||||
break;
|
|
||||||
case 'cold':
|
|
||||||
case 'connectivity':
|
|
||||||
case 'heat':
|
|
||||||
case 'light':
|
|
||||||
case 'moving':
|
|
||||||
case 'power':
|
|
||||||
case 'plug':
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
} else if (domain === 'input_datetime') {
|
|
||||||
let date;
|
|
||||||
if (!stateObj.attributes.has_time) {
|
|
||||||
date = new Date(
|
|
||||||
stateObj.attributes.year,
|
|
||||||
stateObj.attributes.month - 1,
|
|
||||||
stateObj.attributes.day
|
|
||||||
);
|
|
||||||
stateObj._stateDisplay = window.hassUtil.formatDate(date);
|
|
||||||
} else if (!stateObj.attributes.has_date) {
|
|
||||||
date = new Date(
|
|
||||||
1970, 0, 1,
|
|
||||||
stateObj.attributes.hour,
|
|
||||||
stateObj.attributes.minute
|
|
||||||
);
|
|
||||||
stateObj._stateDisplay = window.hassUtil.formatTime(date);
|
|
||||||
} else {
|
|
||||||
date = new Date(
|
|
||||||
stateObj.attributes.year, stateObj.attributes.month - 1,
|
|
||||||
stateObj.attributes.day, stateObj.attributes.hour,
|
|
||||||
stateObj.attributes.minute
|
|
||||||
);
|
|
||||||
stateObj._stateDisplay = window.hassUtil.formatDateTime(date);
|
|
||||||
}
|
|
||||||
} else if (domain === 'zwave') {
|
|
||||||
if (['initializing', 'dead'].includes(stateObj.state) && stateObj.attributes && 'query_stage' in stateObj.attributes) {
|
|
||||||
return stateObj._stateDisplay + ' (' + stateObj.attributes.query_stage + ')';
|
|
||||||
}
|
|
||||||
return stateObj._stateDisplay;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return stateObj._stateDisplay;
|
|
||||||
};
|
|
||||||
|
|
||||||
window.hassUtil.isComponentLoaded = function (hass, component) {
|
window.hassUtil.isComponentLoaded = function (hass, component) {
|
||||||
return hass && hass.config.core.components.indexOf(component) !== -1;
|
return hass && hass.config.core.components.indexOf(component) !== -1;
|
||||||
};
|
};
|
||||||
|
@ -109,12 +109,11 @@
|
|||||||
|
|
||||||
/* eslint-enable no-bitwise */
|
/* eslint-enable no-bitwise */
|
||||||
|
|
||||||
addGetter('primaryText', function () {
|
addGetter('primaryTitle', function () {
|
||||||
return this.stateObj.attributes.media_title ||
|
return this.stateObj.attributes.media_title;
|
||||||
window.hassUtil.computeStateState(this.stateObj);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
addGetter('secondaryText', function () {
|
addGetter('secondaryTitle', function () {
|
||||||
if (this.isMusic) {
|
if (this.isMusic) {
|
||||||
return this.stateObj.attributes.media_artist;
|
return this.stateObj.attributes.media_artist;
|
||||||
} else if (this.isTVShow) {
|
} else if (this.isTVShow) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user