mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 07:16:39 +00:00
Move all of hassUtil to JS (#1153)
* Move all of hassUtil to JS * Fix tests
This commit is contained in:
parent
9116f5733d
commit
912969111f
@ -12,7 +12,6 @@
|
||||
"app-localize-behavior": "PolymerElements/app-localize-behavior#~2.0.0",
|
||||
"app-route": "PolymerElements/app-route#^2.0.0",
|
||||
"app-storage": "^2.0.2",
|
||||
"fecha": "~2.3.0",
|
||||
"font-roboto-local": "~1.0.1",
|
||||
"font-roboto": "PolymerElements/font-roboto-local#~1.0.1",
|
||||
"iron-autogrow-textarea": "PolymerElements/iron-autogrow-textarea#^2.0.0",
|
||||
|
4
js/common/config/is_component_loaded.js
Normal file
4
js/common/config/is_component_loaded.js
Normal file
@ -0,0 +1,4 @@
|
||||
/** Return if a component is loaded. */
|
||||
export default function isComponentLoaded(hass, component) {
|
||||
return hass && hass.config.core.components.indexOf(component) !== -1;
|
||||
}
|
4
js/common/config/location_name.js
Normal file
4
js/common/config/location_name.js
Normal file
@ -0,0 +1,4 @@
|
||||
/** Get the location name from a hass object. */
|
||||
export default function computeLocationName(hass) {
|
||||
return hass && hass.config.core.location_name;
|
||||
}
|
42
js/common/const.js
Normal file
42
js/common/const.js
Normal file
@ -0,0 +1,42 @@
|
||||
/** Constants to be used in the frontend. */
|
||||
|
||||
// Constants should be alphabetically sorted by name.
|
||||
// Arrays with values should be alphabetically sorted if order doesn't matter.
|
||||
// Each constant should have a description what it is supposed to be used for.
|
||||
|
||||
/** Icon to use when no icon specified for domain. */
|
||||
export const DEFAULT_DOMAIN_ICON = 'mdi:bookmark';
|
||||
|
||||
/** Domains that have a state card. */
|
||||
export const DOMAINS_WITH_CARD = [
|
||||
'climate',
|
||||
'cover',
|
||||
'configurator',
|
||||
'input_select',
|
||||
'input_number',
|
||||
'input_text',
|
||||
'media_player',
|
||||
'scene',
|
||||
'script',
|
||||
'timer',
|
||||
'weblink',
|
||||
];
|
||||
|
||||
/** Domains that should have the history hidden in the more info dialog. */
|
||||
export const DOMAINS_MORE_INFO_NO_HISTORY = [
|
||||
'camera',
|
||||
'configurator',
|
||||
'history_graph',
|
||||
'scene',
|
||||
];
|
||||
|
||||
/** States that we consider "off". */
|
||||
export const STATES_OFF = [
|
||||
'closed',
|
||||
'off',
|
||||
'unlocked',
|
||||
];
|
||||
|
||||
/** Temperature units. */
|
||||
export const UNIT_C = '°C';
|
||||
export const UNIT_F = '°F';
|
@ -1,3 +1,5 @@
|
||||
import fecha from 'fecha';
|
||||
|
||||
// Check for support of native locale string options
|
||||
function toLocaleDateStringSupportsOptions() {
|
||||
try {
|
||||
@ -15,5 +17,5 @@ export default (toLocaleDateStringSupportsOptions() ?
|
||||
{ year: 'numeric', month: 'long', day: 'numeric' },
|
||||
);
|
||||
} : function (dateObj, locales) { // eslint-disable-line no-unused-vars
|
||||
return window.fecha.format(dateObj, 'mediumDate');
|
||||
return fecha.format(dateObj, 'mediumDate');
|
||||
});
|
@ -1,3 +1,5 @@
|
||||
import fecha from 'fecha';
|
||||
|
||||
// Check for support of native locale string options
|
||||
function toLocaleStringSupportsOptions() {
|
||||
try {
|
||||
@ -18,5 +20,5 @@ export default (toLocaleStringSupportsOptions() ?
|
||||
minute: '2-digit',
|
||||
});
|
||||
} : function (dateObj, locales) { // eslint-disable-line no-unused-vars
|
||||
return window.fecha.format(dateObj, 'haDateTime');
|
||||
return fecha.format(dateObj, 'haDateTime');
|
||||
});
|
@ -1,3 +1,5 @@
|
||||
import fecha from 'fecha';
|
||||
|
||||
// Check for support of native locale string options
|
||||
function toLocaleTimeStringSupportsOptions() {
|
||||
try {
|
||||
@ -15,5 +17,5 @@ export default (toLocaleTimeStringSupportsOptions() ?
|
||||
{ hour: 'numeric', minute: '2-digit' }
|
||||
);
|
||||
} : function (dateObj, locales) { // eslint-disable-line no-unused-vars
|
||||
return window.fecha.format(dateObj, 'shortTime');
|
||||
return fecha.format(dateObj, 'shortTime');
|
||||
});
|
30
js/common/datetime/relative_time.js
Normal file
30
js/common/datetime/relative_time.js
Normal file
@ -0,0 +1,30 @@
|
||||
/** Calculate a string representing a date object as relative time from now.
|
||||
*
|
||||
* Example output: 5 minutes ago, in 3 days.
|
||||
*/
|
||||
const tests = [
|
||||
60, 'second',
|
||||
60, 'minute',
|
||||
24, 'hour',
|
||||
7, 'day',
|
||||
];
|
||||
|
||||
export default function relativeTime(dateObj) {
|
||||
let delta = Math.abs((new Date() - dateObj) / 1000);
|
||||
const format = delta >= 0 ? '%s ago' : 'in %s';
|
||||
|
||||
for (let i = 0; i < tests.length; i += 2) {
|
||||
if (delta < tests[i]) {
|
||||
delta = Math.floor(delta);
|
||||
return format.replace(
|
||||
'%s',
|
||||
delta === 1 ? '1 ' + tests[i + 1] : delta + ' ' + tests[i + 1] + 's'
|
||||
);
|
||||
}
|
||||
|
||||
delta /= tests[i];
|
||||
}
|
||||
|
||||
delta = Math.floor(delta);
|
||||
return format.replace('%s', delta === 1 ? '1 week' : delta + ' weeks');
|
||||
}
|
41
js/common/dom/apply_themes_on_element.js
Normal file
41
js/common/dom/apply_themes_on_element.js
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Apply a theme to an element by setting the CSS variables on it.
|
||||
*
|
||||
* element: Element to apply theme on.
|
||||
* themes: HASS Theme information
|
||||
* localTheme: selected theme.
|
||||
* updateMeta: boolean if we should update the theme-color meta element.
|
||||
*/
|
||||
export default function applyThemesOnElement(element, themes, localTheme, updateMeta = false) {
|
||||
if (!element._themes) {
|
||||
element._themes = {};
|
||||
}
|
||||
let themeName = themes.default_theme;
|
||||
if (localTheme === 'default' || (localTheme && themes.themes[localTheme])) {
|
||||
themeName = localTheme;
|
||||
}
|
||||
const styles = Object.assign({}, element._themes);
|
||||
if (themeName !== 'default') {
|
||||
var theme = themes.themes[themeName];
|
||||
Object.keys(theme).forEach((key) => {
|
||||
var prefixedKey = '--' + key;
|
||||
element._themes[prefixedKey] = '';
|
||||
styles[prefixedKey] = theme[key];
|
||||
});
|
||||
}
|
||||
// implement updateStyles() method of Polemer elements
|
||||
if (window.ShadyCSS) {
|
||||
window.ShadyCSS.styleSubtree(/** @type {!HTMLElement} */(element), styles);
|
||||
}
|
||||
|
||||
if (!updateMeta) return;
|
||||
|
||||
const meta = document.querySelector('meta[name=theme-color]');
|
||||
if (meta) {
|
||||
if (!meta.hasAttribute('default-content')) {
|
||||
meta.setAttribute('default-content', meta.getAttribute('content'));
|
||||
}
|
||||
const themeColor = styles['--primary-color'] || meta.getAttribute('default-content');
|
||||
meta.setAttribute('content', themeColor);
|
||||
}
|
||||
}
|
33
js/common/dom/dynamic_content_updater.js
Normal file
33
js/common/dom/dynamic_content_updater.js
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Update root's child element to be newElementTag replacing another existing child if any.
|
||||
* Copy attributes into the child element.
|
||||
*/
|
||||
export default function dynamicContentUpdater(root, newElementTag, attributes) {
|
||||
const rootEl = root;
|
||||
let customEl;
|
||||
|
||||
if (rootEl.lastChild && rootEl.lastChild.tagName === newElementTag) {
|
||||
customEl = rootEl.lastChild;
|
||||
} else {
|
||||
if (rootEl.lastChild) {
|
||||
rootEl.removeChild(rootEl.lastChild);
|
||||
}
|
||||
// Creating an element with upper case works fine in Chrome, but in FF it doesn't immediately
|
||||
// become a defined Custom Element. Polymer does that in some later pass.
|
||||
customEl = document.createElement(newElementTag.toLowerCase());
|
||||
}
|
||||
|
||||
if (customEl.setProperties) {
|
||||
customEl.setProperties(attributes);
|
||||
} else {
|
||||
// If custom element definition wasn't loaded yet - setProperties would be
|
||||
// missing, but no harm in setting attributes one-by-one then.
|
||||
Object.keys(attributes).forEach((key) => {
|
||||
customEl[key] = attributes[key];
|
||||
});
|
||||
}
|
||||
|
||||
if (customEl.parentNode === null) {
|
||||
rootEl.appendChild(customEl);
|
||||
}
|
||||
}
|
49
js/common/entity/binary_sensor_icon.js
Normal file
49
js/common/entity/binary_sensor_icon.js
Normal file
@ -0,0 +1,49 @@
|
||||
/** Return an icon representing a binary sensor state. */
|
||||
|
||||
export default function binarySensorIcon(state) {
|
||||
var activated = state.state && state.state === 'off';
|
||||
switch (state.attributes.device_class) {
|
||||
case 'battery':
|
||||
return activated ? 'mdi:battery' : 'mdi:battery-outline';
|
||||
case 'cold':
|
||||
return activated ? 'mdi:thermometer' : 'mdi:snowflake';
|
||||
case 'connectivity':
|
||||
return activated ? 'mdi:server-network-off' : 'mdi:server-network';
|
||||
case 'door':
|
||||
return activated ? 'mdi:door-closed' : 'mdi:door-open';
|
||||
case 'garage_door':
|
||||
return activated ? 'mdi:garage' : 'mdi:garage-open';
|
||||
case 'gas':
|
||||
case 'power':
|
||||
case 'problem':
|
||||
case 'safety':
|
||||
case 'smoke':
|
||||
return activated ? 'mdi:verified' : 'mdi:alert';
|
||||
case 'heat':
|
||||
return activated ? 'mdi:thermometer' : 'mdi:fire';
|
||||
case 'light':
|
||||
return activated ? 'mdi:brightness-5' : 'mdi:brightness-7';
|
||||
case 'lock':
|
||||
return activated ? 'mdi:lock' : 'mdi:lock-open';
|
||||
case 'moisture':
|
||||
return activated ? 'mdi:water-off' : 'mdi:water';
|
||||
case 'motion':
|
||||
return activated ? 'mdi:walk' : 'mdi:run';
|
||||
case 'occupancy':
|
||||
return activated ? 'mdi:home-outline' : 'mdi:home';
|
||||
case 'opening':
|
||||
return activated ? 'mdi:square' : 'mdi:square-outline';
|
||||
case 'plug':
|
||||
return activated ? 'mdi:power-plug-off' : 'mdi:power-plug';
|
||||
case 'presence':
|
||||
return activated ? 'mdi:home-outline' : 'mdi:home';
|
||||
case 'sound':
|
||||
return activated ? 'mdi:music-note-off' : 'mdi:music-note';
|
||||
case 'vibration':
|
||||
return activated ? 'mdi:crop-portrait' : 'mdi:vibrate';
|
||||
case 'window':
|
||||
return activated ? 'mdi:window-closed' : 'mdi:window-open';
|
||||
default:
|
||||
return activated ? 'mdi:radiobox-blank' : 'mdi:checkbox-marked-circle';
|
||||
}
|
||||
}
|
4
js/common/entity/compute_object_id.js
Normal file
4
js/common/entity/compute_object_id.js
Normal file
@ -0,0 +1,4 @@
|
||||
/** Compute the object ID of a state. */
|
||||
export default function computeObjectId(entityId) {
|
||||
return entityId.substr(entityId.indexOf('.') + 1);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import computeStateDomain from './compute_state_domain.js';
|
||||
import formatDateTime from './format_date_time.js';
|
||||
import formatDate from './format_date.js';
|
||||
import formatTime from './format_time.js';
|
||||
import formatDateTime from '../datetime/format_date_time.js';
|
||||
import formatDate from '../datetime/format_date.js';
|
||||
import formatTime from '../datetime/format_time.js';
|
||||
|
||||
export default function computeStateDisplay(localize, stateObj, language) {
|
||||
if (!stateObj._stateDisplay) {
|
11
js/common/entity/compute_state_name.js
Normal file
11
js/common/entity/compute_state_name.js
Normal file
@ -0,0 +1,11 @@
|
||||
import computeObjectId from './compute_object_id';
|
||||
|
||||
export default function computeStateName(stateObj) {
|
||||
if (stateObj._entityDisplay === undefined) {
|
||||
stateObj._entityDisplay = (
|
||||
stateObj.attributes.friendly_name ||
|
||||
computeObjectId(stateObj.entity_id).replace(/_/g, ' '));
|
||||
}
|
||||
|
||||
return stateObj._entityDisplay;
|
||||
}
|
12
js/common/entity/cover_icon.js
Normal file
12
js/common/entity/cover_icon.js
Normal file
@ -0,0 +1,12 @@
|
||||
/** Return an icon representing a cover state. */
|
||||
import domainIcon from './domain_icon.js';
|
||||
|
||||
export default function coverIcon(state) {
|
||||
var open = state.state && state.state !== 'closed';
|
||||
switch (state.attributes.device_class) {
|
||||
case 'garage':
|
||||
return open ? 'mdi:garage-open' : 'mdi:garage';
|
||||
default:
|
||||
return domainIcon('cover', state.state);
|
||||
}
|
||||
}
|
95
js/common/entity/domain_icon.js
Normal file
95
js/common/entity/domain_icon.js
Normal file
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Return the icon to be used for a domain.
|
||||
*
|
||||
* Optionally pass in a state to influence the domain icon.
|
||||
*/
|
||||
import { DEFAULT_DOMAIN_ICON } from '../const.js';
|
||||
|
||||
const fixedIcons = {
|
||||
automation: 'mdi:playlist-play',
|
||||
calendar: 'mdi:calendar',
|
||||
camera: 'mdi:video',
|
||||
climate: 'mdi:thermostat',
|
||||
configurator: 'mdi:settings',
|
||||
conversation: 'mdi:text-to-speech',
|
||||
device_tracker: 'mdi:account',
|
||||
fan: 'mdi:fan',
|
||||
group: 'mdi:google-circles-communities',
|
||||
history_graph: 'mdi:chart-line',
|
||||
homeassistant: 'mdi:home-assistant',
|
||||
image_processing: 'mdi:image-filter-frames',
|
||||
input_boolean: 'mdi:drawing',
|
||||
input_datetime: 'mdi:calendar-clock',
|
||||
input_number: 'mdi:ray-vertex',
|
||||
input_select: 'mdi:format-list-bulleted',
|
||||
input_text: 'mdi:textbox',
|
||||
light: 'mdi:lightbulb',
|
||||
mailbox: 'mdi:mailbox',
|
||||
notify: 'mdi:comment-alert',
|
||||
plant: 'mdi:flower',
|
||||
proximity: 'mdi:apple-safari',
|
||||
remote: 'mdi:remote',
|
||||
scene: 'mdi:google-pages',
|
||||
script: 'mdi:file-document',
|
||||
sensor: 'mdi:eye',
|
||||
simple_alarm: 'mdi:bell',
|
||||
sun: 'mdi:white-balance-sunny',
|
||||
switch: 'mdi:flash',
|
||||
timer: 'mdi:timer',
|
||||
updater: 'mdi:cloud-upload',
|
||||
vacuum: 'mdi:robot-vacuum',
|
||||
weblink: 'mdi:open-in-new',
|
||||
};
|
||||
|
||||
export default function domainIcon(domain, state) {
|
||||
if (domain in fixedIcons) {
|
||||
return fixedIcons[domain];
|
||||
}
|
||||
|
||||
switch (domain) {
|
||||
case 'alarm_control_panel':
|
||||
switch (state) {
|
||||
case 'armed_home':
|
||||
return 'mdi:bell-plus';
|
||||
case 'armed_night':
|
||||
return 'mdi:bell-sleep';
|
||||
case 'disarmed':
|
||||
return 'mdi:bell-outline';
|
||||
case 'triggered':
|
||||
return 'mdi:bell-ring';
|
||||
default:
|
||||
return 'mdi:bell';
|
||||
}
|
||||
|
||||
case 'binary_sensor':
|
||||
return state && state === 'off' ? 'mdi:radiobox-blank' : 'mdi:checkbox-marked-circle';
|
||||
|
||||
case 'cover':
|
||||
return state && state === 'open' ? 'mdi:window-open' : 'mdi:window-closed';
|
||||
|
||||
case 'lock':
|
||||
return state && state === 'unlocked' ? 'mdi:lock-open' : 'mdi:lock';
|
||||
|
||||
case 'media_player':
|
||||
return state && state !== 'off' && state !== 'idle' ?
|
||||
'mdi:cast-connected' : 'mdi:cast';
|
||||
|
||||
case 'zwave':
|
||||
switch (state) {
|
||||
case 'dead':
|
||||
return 'mdi:emoticon-dead';
|
||||
case 'sleeping':
|
||||
return 'mdi:sleep';
|
||||
case 'initializing':
|
||||
return 'mdi:timer-sand';
|
||||
default:
|
||||
return 'mdi:nfc';
|
||||
}
|
||||
|
||||
default:
|
||||
/* eslint-disable no-console */
|
||||
console.warn('Unable to find icon for domain ' + domain + ' (' + state + ')');
|
||||
/* eslint-enable no-console */
|
||||
return DEFAULT_DOMAIN_ICON;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
export function hasLocation(stateObj) {
|
||||
export default function hasLocation(stateObj) {
|
||||
return ('latitude' in stateObj.attributes &&
|
||||
'longitude' in stateObj.attributes);
|
||||
}
|
11
js/common/entity/input_dateteime_icon.js
Normal file
11
js/common/entity/input_dateteime_icon.js
Normal file
@ -0,0 +1,11 @@
|
||||
/** Return an icon representing an input datetime state. */
|
||||
import domainIcon from './domain_icon.js';
|
||||
|
||||
export default function inputDateTimeIcon(state) {
|
||||
if (!state.attributes.has_date) {
|
||||
return 'mdi:clock';
|
||||
} else if (!state.attributes.has_time) {
|
||||
return 'mdi:calendar';
|
||||
}
|
||||
return domainIcon('input_datetime');
|
||||
}
|
35
js/common/entity/sensor_icon.js
Normal file
35
js/common/entity/sensor_icon.js
Normal file
@ -0,0 +1,35 @@
|
||||
/** Return an icon representing a sensor state. */
|
||||
import { UNIT_C, UNIT_F } from '../const.js';
|
||||
import domainIcon from './domain_icon.js';
|
||||
|
||||
const fixedDeviceClassIcons = {
|
||||
humidity: 'mdi:water-percent',
|
||||
illuminance: 'mdi:brightness-5',
|
||||
temperature: 'mdi:thermometer',
|
||||
};
|
||||
|
||||
export default function sensorIcon(state) {
|
||||
const dclass = state.attributes.device_class;
|
||||
|
||||
if (dclass in fixedDeviceClassIcons) {
|
||||
return fixedDeviceClassIcons[dclass];
|
||||
} else if (dclass === 'battery') {
|
||||
if (isNaN(state.state)) {
|
||||
return 'mdi:battery-unknown';
|
||||
}
|
||||
const batteryRound = Math.round(state.state / 10) * 10;
|
||||
if (batteryRound >= 100) {
|
||||
return 'mdi:battery';
|
||||
}
|
||||
if (batteryRound <= 0) {
|
||||
return 'mdi:battery-alert';
|
||||
}
|
||||
return `mdi:battery-${batteryRound}`;
|
||||
}
|
||||
|
||||
const unit = state.attributes.unit_of_measurement;
|
||||
if (unit === UNIT_C || unit === UNIT_F) {
|
||||
return 'mdi:thermometer';
|
||||
}
|
||||
return domainIcon('sensor');
|
||||
}
|
@ -1,19 +1,6 @@
|
||||
import canToggleState from './can_toggle_state.js';
|
||||
import computeStateDomain from './compute_state_domain.js';
|
||||
|
||||
const DOMAINS_WITH_CARD = [
|
||||
'climate',
|
||||
'cover',
|
||||
'configurator',
|
||||
'input_select',
|
||||
'input_number',
|
||||
'input_text',
|
||||
'media_player',
|
||||
'scene',
|
||||
'script',
|
||||
'timer',
|
||||
'weblink',
|
||||
];
|
||||
import { DOMAINS_WITH_CARD } from '../const.js';
|
||||
|
||||
export default function stateCardType(hass, stateObj) {
|
||||
if (stateObj.state === 'unavailable') {
|
32
js/common/entity/state_icon.js
Normal file
32
js/common/entity/state_icon.js
Normal file
@ -0,0 +1,32 @@
|
||||
/** Return an icon representing a state. */
|
||||
import { DEFAULT_DOMAIN_ICON } from '../const.js';
|
||||
|
||||
import computeDomain from './compute_domain.js';
|
||||
import domainIcon from './domain_icon.js';
|
||||
|
||||
import binarySensorIcon from './binary_sensor_icon.js';
|
||||
import coverIcon from './cover_icon.js';
|
||||
import sensorIcon from './sensor_icon.js';
|
||||
import inputDateTimeIcon from './input_dateteime_icon.js';
|
||||
|
||||
const domainIcons = {
|
||||
binary_sensor: binarySensorIcon,
|
||||
cover: coverIcon,
|
||||
sensor: sensorIcon,
|
||||
input_datetime: inputDateTimeIcon,
|
||||
};
|
||||
|
||||
export default function stateIcon(state) {
|
||||
if (!state) {
|
||||
return DEFAULT_DOMAIN_ICON;
|
||||
} else if (state.attributes.icon) {
|
||||
return state.attributes.icon;
|
||||
}
|
||||
|
||||
const domain = computeDomain(state.entity_id);
|
||||
|
||||
if (domain in domainIcons) {
|
||||
return domainIcons[domain](state);
|
||||
}
|
||||
return domainIcon(domain, state.state);
|
||||
}
|
20
js/common/entity/states_sort_by_name.js
Normal file
20
js/common/entity/states_sort_by_name.js
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Sort function to help sort states by name
|
||||
*
|
||||
* Usage:
|
||||
* const states = [state1, state2]
|
||||
* states.sort(statesSortByName);
|
||||
*/
|
||||
import computeStateName from './compute_state_name.js';
|
||||
|
||||
export default function sortStatesByName(entityA, entityB) {
|
||||
const nameA = computeStateName(entityA);
|
||||
const nameB = computeStateName(entityB);
|
||||
if (nameA < nameB) {
|
||||
return -1;
|
||||
}
|
||||
if (nameA > nameB) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import durationToSeconds from './duration_to_seconds.js';
|
||||
import durationToSeconds from '../datetime/duration_to_seconds.js';
|
||||
|
||||
export default function timerTimeRemaining(stateObj) {
|
||||
let timeRemaining = durationToSeconds(stateObj.attributes.remaining);
|
3
js/common/entity/valid_entity_id.js
Normal file
3
js/common/entity/valid_entity_id.js
Normal file
@ -0,0 +1,3 @@
|
||||
export default function validEntityId(entityId) {
|
||||
return /^(\w+)\.(\w+)$/.test(entityId);
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export function validEntityId(entityId) {
|
||||
return /^(\w+)\.(\w+)$/.test(entityId);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
|
||||
export default class NumericStateCondition extends Component {
|
||||
constructor() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
|
||||
export default class StateCondition extends Component {
|
||||
constructor() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
|
||||
export default class SunCondition extends Component {
|
||||
constructor() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
|
||||
export default class TemplateCondition extends Component {
|
||||
constructor() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
|
||||
export default class TimeCondition extends Component {
|
||||
constructor() {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { hasLocation } from '../../common/util/location.js';
|
||||
import computeStateDomain from '../../common/util/compute_state_domain.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
import hasLocation from '../../common/entity/has_location.js';
|
||||
import computeStateDomain from '../../common/entity/compute_state_domain.js';
|
||||
|
||||
function zoneAndLocationFilter(stateObj) {
|
||||
return hasLocation(stateObj) && computeStateDomain(stateObj) !== 'zone';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { h, Component } from 'preact';
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
|
||||
export default class DelayAction extends Component {
|
||||
constructor() {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import JSONTextArea from '../json_textarea.js';
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
|
||||
export default class EventAction extends Component {
|
||||
constructor() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { h, Component } from 'preact';
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
|
||||
export default class WaitAction extends Component {
|
||||
constructor() {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import JSONTextArea from '../json_textarea.js';
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
|
||||
export default class EventTrigger extends Component {
|
||||
constructor() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
|
||||
export default class MQTTTrigger extends Component {
|
||||
constructor() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
|
||||
export default class NumericStateTrigger extends Component {
|
||||
constructor() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
|
||||
export default class StateTrigger extends Component {
|
||||
constructor() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
|
||||
export default class SunTrigger extends Component {
|
||||
constructor() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
|
||||
export default class TemplateTrigger extends Component {
|
||||
constructor() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
|
||||
export default class TimeTrigger extends Component {
|
||||
constructor() {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChangeEvent } from '../../common/util/event.js';
|
||||
import { hasLocation } from '../../common/util/location.js';
|
||||
import computeStateDomain from '../../common/util/compute_state_domain.js';
|
||||
import { onChangeEvent } from '../../common/preact/event.js';
|
||||
import hasLocation from '../../common/entity/has_location.js';
|
||||
import computeStateDomain from '../../common/entity/compute_state_domain.js';
|
||||
|
||||
function zoneAndLocationFilter(stateObj) {
|
||||
return hasLocation(stateObj) && computeStateDomain(stateObj) !== 'zone';
|
||||
|
107
js/util.js
107
js/util.js
@ -5,42 +5,93 @@
|
||||
* ES6 JS imports. Once we move to Polymer 3, we should be able to simply
|
||||
* import these functions where we need them.
|
||||
*/
|
||||
import fecha from 'fecha';
|
||||
|
||||
import attributeClassNames from './common/util/attribute_class_names.js';
|
||||
import canToggleDomain from './common/util/can_toggle_domain.js';
|
||||
import canToggleState from './common/util/can_toggle_state.js';
|
||||
import computeStateDisplay from './common/util/compute_state_display.js';
|
||||
import computeDomain from './common/util/compute_state_domain.js';
|
||||
import durationToSeconds from './common/util/duration_to_seconds.js';
|
||||
import featureClassNames from './common/util/feature_class_names.js';
|
||||
import formatDate from './common/util/format_date.js';
|
||||
import formatDateTime from './common/util/format_date_time.js';
|
||||
import formatTime from './common/util/format_time.js';
|
||||
import secondsToDuration from './common/util/seconds_to_duration.js';
|
||||
import stateCardType from './common/util/state_card_type.js';
|
||||
import stateMoreInfoType from './common/util/state_more_info_type.js';
|
||||
import timerTimeRemaining from './common/util/timer_time_remaining.js';
|
||||
// const
|
||||
import {
|
||||
DEFAULT_DOMAIN_ICON,
|
||||
DOMAINS_MORE_INFO_NO_HISTORY,
|
||||
STATES_OFF,
|
||||
} from './common/const.js';
|
||||
|
||||
window.hassUtil = window.hassUtil || {};
|
||||
// config
|
||||
import computeLocationName from './common/config/location_name';
|
||||
import isComponentLoaded from './common/config/is_component_loaded.js';
|
||||
|
||||
// dom
|
||||
import applyThemesOnElement from './common/dom/apply_themes_on_element.js';
|
||||
import dynamicContentUpdater from './common/dom/dynamic_content_updater.js';
|
||||
|
||||
// datetime
|
||||
import durationToSeconds from './common/datetime/duration_to_seconds.js';
|
||||
import formatDate from './common/datetime/format_date.js';
|
||||
import formatDateTime from './common/datetime/format_date_time.js';
|
||||
import formatTime from './common/datetime/format_time.js';
|
||||
import relativeTime from './common/datetime/relative_time.js';
|
||||
import secondsToDuration from './common/datetime/seconds_to_duration.js';
|
||||
|
||||
// entity
|
||||
import attributeClassNames from './common/entity/attribute_class_names.js';
|
||||
import binarySensorIcon from './common/entity/binary_sensor_icon.js';
|
||||
import canToggleDomain from './common/entity/can_toggle_domain.js';
|
||||
import canToggleState from './common/entity/can_toggle_state.js';
|
||||
import computeDomain from './common/entity/compute_state_domain.js';
|
||||
import computeObjectId from './common/entity/compute_object_id.js';
|
||||
import computeStateDisplay from './common/entity/compute_state_display.js';
|
||||
import computeStateName from './common/entity/compute_state_name.js';
|
||||
import coverIcon from './common/entity/cover_icon.js';
|
||||
import domainIcon from './common/entity/domain_icon.js';
|
||||
import featureClassNames from './common/entity/feature_class_names.js';
|
||||
import sensorIcon from './common/entity/sensor_icon.js';
|
||||
import sortByName from './common/entity/states_sort_by_name.js';
|
||||
import stateCardType from './common/entity/state_card_type.js';
|
||||
import stateIcon from './common/entity/state_icon.js';
|
||||
import stateMoreInfoType from './common/entity/state_more_info_type.js';
|
||||
import timerTimeRemaining from './common/entity/timer_time_remaining.js';
|
||||
|
||||
const language = navigator.languages ?
|
||||
navigator.languages[0] : navigator.language || navigator.userLanguage;
|
||||
|
||||
window.fecha.masks.haDateTime = window.fecha.masks.shortTime + ' ' + window.fecha.masks.mediumDate;
|
||||
fecha.masks.haDateTime = `${fecha.masks.shortTime} ${fecha.masks.mediumDate}`;
|
||||
|
||||
Object.assign(window.hassUtil, {
|
||||
attributeClassNames,
|
||||
canToggleDomain,
|
||||
canToggleState,
|
||||
computeDomain,
|
||||
computeStateDisplay,
|
||||
window.hassUtil = {
|
||||
// const
|
||||
DEFAULT_ICON: DEFAULT_DOMAIN_ICON,
|
||||
OFF_STATES: STATES_OFF,
|
||||
DOMAINS_WITH_NO_HISTORY: DOMAINS_MORE_INFO_NO_HISTORY,
|
||||
|
||||
// config
|
||||
computeLocationName,
|
||||
isComponentLoaded,
|
||||
|
||||
// datetime
|
||||
durationToSeconds,
|
||||
featureClassNames,
|
||||
secondsToDuration,
|
||||
stateCardType,
|
||||
stateMoreInfoType,
|
||||
timerTimeRemaining,
|
||||
formatDate: dateObj => formatDate(dateObj, language),
|
||||
formatDateTime: dateObj => formatDateTime(dateObj, language),
|
||||
formatTime: dateObj => formatTime(dateObj, language),
|
||||
});
|
||||
relativeTime,
|
||||
|
||||
// dom
|
||||
applyThemesOnElement,
|
||||
dynamicContentUpdater,
|
||||
|
||||
// entity
|
||||
attributeClassNames,
|
||||
binarySensorIcon,
|
||||
canToggleDomain,
|
||||
canToggleState,
|
||||
computeDomain,
|
||||
computeObjectId,
|
||||
computeStateDisplay,
|
||||
computeStateName,
|
||||
coverIcon,
|
||||
domainIcon,
|
||||
featureClassNames,
|
||||
secondsToDuration,
|
||||
sensorIcon,
|
||||
sortByName,
|
||||
stateCardType,
|
||||
stateIcon,
|
||||
stateMoreInfoType,
|
||||
timerTimeRemaining,
|
||||
};
|
||||
|
@ -24,6 +24,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"es6-object-assign": "^1.1.0",
|
||||
"fecha": "^2.3.3",
|
||||
"home-assistant-js-websocket": "^1.1.2",
|
||||
"mdn-polyfills": "^5.5.0",
|
||||
"preact": "^8.2.6",
|
||||
|
@ -209,7 +209,7 @@ class HaScriptEditor extends window.hassMixins.LocalizeMixin(Polymer.Element) {
|
||||
if (oldVal && oldVal.entity_id === newVal.entity_id) {
|
||||
return;
|
||||
}
|
||||
this.hass.callApi('get', 'config/script/config/' + window.hassUtil.computeObjectId(newVal))
|
||||
this.hass.callApi('get', 'config/script/config/' + window.hassUtil.computeObjectId(newVal.entity_id))
|
||||
.then((config) => {
|
||||
// Normalize data: ensure sequence is a list
|
||||
// Happens when people copy paste their scripts into the config
|
||||
@ -266,7 +266,8 @@ class HaScriptEditor extends window.hassMixins.LocalizeMixin(Polymer.Element) {
|
||||
}
|
||||
|
||||
saveScript() {
|
||||
var id = this.creatingNew ? '' + Date.now() : window.hassUtil.computeObjectId(this.script);
|
||||
var id = this.creatingNew ?
|
||||
'' + Date.now() : window.hassUtil.computeObjectId(this.script.entity_id);
|
||||
this.hass.callApi('post', 'config/script/config/' + id, this.config).then(() => {
|
||||
this.dirty = false;
|
||||
|
||||
|
@ -1,415 +1 @@
|
||||
<script src='../../bower_components/fecha/fecha.min.js'></script>
|
||||
<script src='../../build-temp/util.js'></script>
|
||||
|
||||
<!--
|
||||
collection of utility functions.
|
||||
-->
|
||||
<script>
|
||||
window.hassUtil = window.hassUtil || {};
|
||||
|
||||
window.hassUtil.DEFAULT_ICON = 'mdi:bookmark';
|
||||
|
||||
window.hassUtil.OFF_STATES = ['off', 'closed', 'unlocked'];
|
||||
|
||||
window.hassUtil.DOMAINS_WITH_NO_HISTORY = ['camera', 'configurator', 'history_graph', 'scene'];
|
||||
|
||||
// Update root's child element to be newElementTag replacing another existing child if any.
|
||||
// Copy attributes into the child element.
|
||||
window.hassUtil.dynamicContentUpdater = function (root, newElementTag, attributes) {
|
||||
var rootEl = Polymer.dom(root);
|
||||
var customEl;
|
||||
|
||||
if (rootEl.lastChild && rootEl.lastChild.tagName === newElementTag) {
|
||||
customEl = rootEl.lastChild;
|
||||
} else {
|
||||
if (rootEl.lastChild) {
|
||||
rootEl.removeChild(rootEl.lastChild);
|
||||
}
|
||||
// Creating an element with upper case works fine in Chrome, but in FF it doesn't immediately
|
||||
// become a defined Custom Element. Polymer does that in some later pass.
|
||||
customEl = document.createElement(newElementTag.toLowerCase());
|
||||
}
|
||||
|
||||
if (customEl.setProperties) {
|
||||
customEl.setProperties(attributes);
|
||||
} else {
|
||||
// If custom element definition wasn't loaded yet - setProperties would be
|
||||
// missing, but no harm in setting attributes one-by-one then.
|
||||
Object.keys(attributes).forEach((key) => {
|
||||
customEl[key] = attributes[key];
|
||||
});
|
||||
}
|
||||
|
||||
if (customEl.parentNode === null) {
|
||||
rootEl.appendChild(customEl);
|
||||
}
|
||||
};
|
||||
|
||||
window.hassUtil.relativeTime = function (dateObj) {
|
||||
var delta = (new Date() - dateObj) / 1000;
|
||||
var format = delta >= 0 ? '%s ago' : 'in %s';
|
||||
delta = Math.abs(delta);
|
||||
var tests = window.hassUtil.relativeTime.tests;
|
||||
var i;
|
||||
|
||||
for (i = 0; i < tests.length; i += 2) {
|
||||
if (delta < tests[i]) {
|
||||
delta = Math.floor(delta);
|
||||
return format.replace(
|
||||
'%s',
|
||||
delta === 1 ? '1 ' + tests[i + 1] : delta + ' ' + tests[i + 1] + 's'
|
||||
);
|
||||
}
|
||||
|
||||
delta /= tests[i];
|
||||
}
|
||||
|
||||
delta = Math.floor(delta);
|
||||
return format.replace('%s', delta === 1 ? '1 week' : delta + ' weeks');
|
||||
};
|
||||
|
||||
window.hassUtil.relativeTime.tests = [
|
||||
60, 'second',
|
||||
60, 'minute',
|
||||
24, 'hour',
|
||||
7, 'day',
|
||||
];
|
||||
|
||||
window.hassUtil.domainIcon = function (domain, state) {
|
||||
switch (domain) {
|
||||
case 'alarm_control_panel':
|
||||
switch (state) {
|
||||
case 'armed_home':
|
||||
return 'mdi:bell-plus';
|
||||
case 'armed_night':
|
||||
return 'mdi:bell-sleep';
|
||||
case 'disarmed':
|
||||
return 'mdi:bell-outline';
|
||||
case 'triggered':
|
||||
return 'mdi:bell-ring';
|
||||
default:
|
||||
return 'mdi:bell';
|
||||
}
|
||||
|
||||
case 'automation':
|
||||
return 'mdi:playlist-play';
|
||||
|
||||
case 'binary_sensor':
|
||||
return state && state === 'off' ? 'mdi:radiobox-blank' : 'mdi:checkbox-marked-circle';
|
||||
|
||||
case 'calendar':
|
||||
return 'mdi:calendar';
|
||||
|
||||
case 'camera':
|
||||
return 'mdi:video';
|
||||
|
||||
case 'climate':
|
||||
return 'mdi:thermostat';
|
||||
|
||||
case 'configurator':
|
||||
return 'mdi:settings';
|
||||
|
||||
case 'conversation':
|
||||
return 'mdi:text-to-speech';
|
||||
|
||||
case 'cover':
|
||||
return state && state === 'open' ? 'mdi:window-open' : 'mdi:window-closed';
|
||||
|
||||
case 'device_tracker':
|
||||
return 'mdi:account';
|
||||
|
||||
case 'fan':
|
||||
return 'mdi:fan';
|
||||
|
||||
case 'history_graph':
|
||||
return 'mdi:chart-line';
|
||||
|
||||
case 'group':
|
||||
return 'mdi:google-circles-communities';
|
||||
|
||||
case 'homeassistant':
|
||||
return 'mdi:home-assistant';
|
||||
|
||||
case 'image_processing':
|
||||
return 'mdi:image-filter-frames';
|
||||
|
||||
case 'input_boolean':
|
||||
return 'mdi:drawing';
|
||||
|
||||
case 'input_datetime':
|
||||
return 'mdi:calendar-clock';
|
||||
|
||||
case 'input_select':
|
||||
return 'mdi:format-list-bulleted';
|
||||
|
||||
case 'input_number':
|
||||
return 'mdi:ray-vertex';
|
||||
|
||||
case 'input_text':
|
||||
return 'mdi:textbox';
|
||||
|
||||
case 'light':
|
||||
return 'mdi:lightbulb';
|
||||
|
||||
case 'lock':
|
||||
return state && state === 'unlocked' ? 'mdi:lock-open' : 'mdi:lock';
|
||||
|
||||
case 'mailbox':
|
||||
return 'mdi:mailbox';
|
||||
|
||||
case 'media_player':
|
||||
return state && state !== 'off' && state !== 'idle' ?
|
||||
'mdi:cast-connected' : 'mdi:cast';
|
||||
|
||||
case 'notify':
|
||||
return 'mdi:comment-alert';
|
||||
|
||||
case 'plant':
|
||||
return 'mdi:flower';
|
||||
|
||||
case 'proximity':
|
||||
return 'mdi:apple-safari';
|
||||
|
||||
case 'remote':
|
||||
return 'mdi:remote';
|
||||
|
||||
case 'scene':
|
||||
return 'mdi:google-pages';
|
||||
|
||||
case 'script':
|
||||
return 'mdi:file-document';
|
||||
|
||||
case 'sensor':
|
||||
return 'mdi:eye';
|
||||
|
||||
case 'simple_alarm':
|
||||
return 'mdi:bell';
|
||||
|
||||
case 'sun':
|
||||
return 'mdi:white-balance-sunny';
|
||||
|
||||
case 'switch':
|
||||
return 'mdi:flash';
|
||||
|
||||
case 'timer':
|
||||
return 'mdi:timer';
|
||||
|
||||
case 'updater':
|
||||
return 'mdi:cloud-upload';
|
||||
|
||||
case 'vacuum':
|
||||
return 'mdi:robot-vacuum';
|
||||
|
||||
case 'weblink':
|
||||
return 'mdi:open-in-new';
|
||||
|
||||
case 'zwave':
|
||||
switch (state) {
|
||||
case 'dead':
|
||||
return 'mdi:emoticon-dead';
|
||||
case 'sleeping':
|
||||
return 'mdi:sleep';
|
||||
case 'initializing':
|
||||
return 'mdi:timer-sand';
|
||||
default:
|
||||
return 'mdi:nfc';
|
||||
}
|
||||
|
||||
default:
|
||||
/* eslint-disable no-console */
|
||||
console.warn('Unable to find icon for domain ' + domain + ' (' + state + ')');
|
||||
/* eslint-enable no-console */
|
||||
return window.hassUtil.DEFAULT_ICON;
|
||||
}
|
||||
};
|
||||
|
||||
window.hassUtil.binarySensorIcon = function (state) {
|
||||
var activated = state.state && state.state === 'off';
|
||||
switch (state.attributes.device_class) {
|
||||
case 'battery':
|
||||
return activated ? 'mdi:battery' : 'mdi:battery-outline';
|
||||
case 'cold':
|
||||
return activated ? 'mdi:thermometer' : 'mdi:snowflake';
|
||||
case 'connectivity':
|
||||
return activated ? 'mdi:server-network-off' : 'mdi:server-network';
|
||||
case 'door':
|
||||
return activated ? 'mdi:door-closed' : 'mdi:door-open';
|
||||
case 'garage_door':
|
||||
return activated ? 'mdi:garage' : 'mdi:garage-open';
|
||||
case 'gas':
|
||||
case 'power':
|
||||
case 'problem':
|
||||
case 'safety':
|
||||
case 'smoke':
|
||||
return activated ? 'mdi:verified' : 'mdi:alert';
|
||||
case 'heat':
|
||||
return activated ? 'mdi:thermometer' : 'mdi:fire';
|
||||
case 'light':
|
||||
return activated ? 'mdi:brightness-5' : 'mdi:brightness-7';
|
||||
case 'lock':
|
||||
return activated ? 'mdi:lock' : 'mdi:lock-open';
|
||||
case 'moisture':
|
||||
return activated ? 'mdi:water-off' : 'mdi:water';
|
||||
case 'motion':
|
||||
return activated ? 'mdi:walk' : 'mdi:run';
|
||||
case 'occupancy':
|
||||
return activated ? 'mdi:home-outline' : 'mdi:home';
|
||||
case 'opening':
|
||||
return activated ? 'mdi:square' : 'mdi:square-outline';
|
||||
case 'plug':
|
||||
return activated ? 'mdi:power-plug-off' : 'mdi:power-plug';
|
||||
case 'presence':
|
||||
return activated ? 'mdi:home-outline' : 'mdi:home';
|
||||
case 'sound':
|
||||
return activated ? 'mdi:music-note-off' : 'mdi:music-note';
|
||||
case 'vibration':
|
||||
return activated ? 'mdi:crop-portrait' : 'mdi:vibrate';
|
||||
case 'window':
|
||||
return activated ? 'mdi:window-closed' : 'mdi:window-open';
|
||||
default:
|
||||
return activated ? 'mdi:radiobox-blank' : 'mdi:checkbox-marked-circle';
|
||||
}
|
||||
};
|
||||
|
||||
window.hassUtil.coverIcon = function (state) {
|
||||
var open = state.state && state.state !== 'closed';
|
||||
switch (state.attributes.device_class) {
|
||||
case 'garage':
|
||||
return open ? 'mdi:garage-open' : 'mdi:garage';
|
||||
default:
|
||||
return open ? 'mdi:window-open' : 'mdi:window-closed';
|
||||
}
|
||||
};
|
||||
|
||||
window.hassUtil.sensorIcon = (state) => {
|
||||
switch (state.attributes.device_class) {
|
||||
case 'battery': {
|
||||
if (isNaN(state.state)) {
|
||||
return 'mdi:battery-unknown';
|
||||
}
|
||||
const batteryRound = Math.round(state.state / 10) * 10;
|
||||
if (batteryRound >= 100) {
|
||||
return 'mdi:battery';
|
||||
}
|
||||
if (batteryRound <= 0) {
|
||||
return 'mdi:battery-alert';
|
||||
}
|
||||
return `mdi:battery-${batteryRound}`;
|
||||
}
|
||||
case 'humidity':
|
||||
return 'mdi:water-percent';
|
||||
case 'illuminance':
|
||||
return 'mdi:brightness-5';
|
||||
case 'temperature':
|
||||
return 'mdi:thermometer';
|
||||
default:
|
||||
return 'mdi:eye';
|
||||
}
|
||||
};
|
||||
|
||||
window.hassUtil.stateIcon = function (state) {
|
||||
if (!state) {
|
||||
return window.hassUtil.DEFAULT_ICON;
|
||||
} else if (state.attributes.icon) {
|
||||
return state.attributes.icon;
|
||||
}
|
||||
|
||||
const unit = state.attributes.unit_of_measurement;
|
||||
const domain = window.hassUtil.computeDomain(state);
|
||||
|
||||
if (domain === 'sensor' && state.attributes.device_class) {
|
||||
return window.hassUtil.sensorIcon(state);
|
||||
} else if (domain === 'sensor' && unit) {
|
||||
if (unit === '°C' || unit === '°F') {
|
||||
return 'mdi:thermometer';
|
||||
} else if (unit === 'Mice') {
|
||||
return 'mdi:mouse-variant';
|
||||
}
|
||||
} else if (domain === 'binary_sensor') {
|
||||
return window.hassUtil.binarySensorIcon(state);
|
||||
} else if (domain === 'cover') {
|
||||
return window.hassUtil.coverIcon(state);
|
||||
} else if (domain === 'input_datetime') {
|
||||
if (!state.attributes.has_date) {
|
||||
return 'mdi:clock';
|
||||
} else if (!state.attributes.has_time) {
|
||||
return 'mdi:calendar';
|
||||
}
|
||||
}
|
||||
|
||||
return window.hassUtil.domainIcon(domain, state.state);
|
||||
};
|
||||
|
||||
window.hassUtil.computeObjectId = function (stateObj) {
|
||||
if (!stateObj._object_id) {
|
||||
stateObj._object_id = window.HAWS.extractObjectId(stateObj.entity_id);
|
||||
}
|
||||
|
||||
return stateObj._object_id;
|
||||
};
|
||||
|
||||
window.hassUtil.computeStateName = function (stateObj) {
|
||||
if (stateObj._entityDisplay === undefined) {
|
||||
stateObj._entityDisplay = (
|
||||
stateObj.attributes.friendly_name ||
|
||||
window.HAWS.extractObjectId(stateObj.entity_id)
|
||||
.replace(/_/g, ' '));
|
||||
}
|
||||
|
||||
return stateObj._entityDisplay;
|
||||
};
|
||||
|
||||
window.hassUtil.sortByName = function (entityA, entityB) {
|
||||
var nameA = window.hassUtil.computeStateName(entityA);
|
||||
var nameB = window.hassUtil.computeStateName(entityB);
|
||||
if (nameA < nameB) {
|
||||
return -1;
|
||||
}
|
||||
if (nameA > nameB) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
window.hassUtil.isComponentLoaded = function (hass, component) {
|
||||
return hass && hass.config.core.components.indexOf(component) !== -1;
|
||||
};
|
||||
|
||||
window.hassUtil.computeLocationName = function (hass) {
|
||||
return hass && hass.config.core.location_name;
|
||||
};
|
||||
|
||||
window.hassUtil.applyThemesOnElement = function (element, themes, localTheme, updateMeta) {
|
||||
if (!element._themes) {
|
||||
element._themes = {};
|
||||
}
|
||||
let themeName = themes.default_theme;
|
||||
if (localTheme === 'default' || (localTheme && themes.themes[localTheme])) {
|
||||
themeName = localTheme;
|
||||
}
|
||||
const styles = Object.assign({}, element._themes);
|
||||
if (themeName !== 'default') {
|
||||
var theme = themes.themes[themeName];
|
||||
Object.keys(theme).forEach((key) => {
|
||||
var prefixedKey = '--' + key;
|
||||
element._themes[prefixedKey] = '';
|
||||
styles[prefixedKey] = theme[key];
|
||||
});
|
||||
}
|
||||
// implement updateStyles() method of Polemer elements
|
||||
if (window.ShadyCSS) {
|
||||
window.ShadyCSS.styleSubtree(/** @type {!HTMLElement} */(element), styles);
|
||||
}
|
||||
|
||||
if (!updateMeta) return;
|
||||
|
||||
const meta = document.querySelector('meta[name=theme-color]');
|
||||
if (meta) {
|
||||
if (!meta.hasAttribute('default-content')) {
|
||||
meta.setAttribute('default-content', meta.getAttribute('content'));
|
||||
}
|
||||
const themeColor = styles['--primary-color'] || meta.getAttribute('default-content');
|
||||
meta.setAttribute('content', themeColor);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import durationToSeconds from '../../../js/common/util/duration_to_seconds.js';
|
||||
import durationToSeconds from '../../../js/common/datetime/duration_to_seconds.js';
|
||||
|
||||
describe('durationToSeconds', () => {
|
||||
it('works', () => {
|
@ -1,6 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import formatDate from '../../../js/common/util/format_date';
|
||||
import formatDate from '../../../js/common/datetime/format_date';
|
||||
|
||||
describe('formatDate', () => {
|
||||
const dateObj = new Date(
|
@ -1,6 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import formatDateTime from '../../../js/common/util/format_date_time';
|
||||
import formatDateTime from '../../../js/common/datetime/format_date_time';
|
||||
|
||||
describe('formatDateTime', () => {
|
||||
const dateObj = new Date(
|
@ -1,6 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import formatTime from '../../../js/common/util/format_time';
|
||||
import formatTime from '../../../js/common/datetime/format_time';
|
||||
|
||||
describe('formatTime', () => {
|
||||
const dateObj = new Date(
|
@ -1,6 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import secondsToDuration from '../../../js/common/util/seconds_to_duration.js';
|
||||
import secondsToDuration from '../../../js/common/datetime/seconds_to_duration.js';
|
||||
|
||||
describe('secondsToDuration', () => {
|
||||
it('works', () => {
|
@ -1,6 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import attributeClassNames from '../../../js/common/util/attribute_class_names';
|
||||
import attributeClassNames from '../../../js/common/entity/attribute_class_names';
|
||||
|
||||
describe('attributeClassNames', () => {
|
||||
const attrs = ['mock_attr1', 'mock_attr2'];
|
@ -1,6 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import canToggleDomain from '../../../js/common/util/can_toggle_domain';
|
||||
import canToggleDomain from '../../../js/common/entity/can_toggle_domain';
|
||||
|
||||
describe('canToggleDomain', () => {
|
||||
const hass = {
|
@ -1,6 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import canToggleState from '../../../js/common/util/can_toggle_state';
|
||||
import canToggleState from '../../../js/common/entity/can_toggle_state';
|
||||
|
||||
describe('canToggleState', () => {
|
||||
const hass = {
|
@ -1,6 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import computeDomain from '../../../js/common/util/compute_domain';
|
||||
import computeDomain from '../../../js/common/entity/compute_domain';
|
||||
|
||||
describe('computeDomain', () => {
|
||||
it('Returns domains', () => {
|
@ -1,6 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import computeStateDisplay from '../../../js/common/util/compute_state_display';
|
||||
import computeStateDisplay from '../../../js/common/entity/compute_state_display';
|
||||
|
||||
describe('computeStateDisplay', () => {
|
||||
const localize = function (message, ...args) {
|
@ -1,6 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import computeStateDomain from '../../../js/common/util/compute_state_domain.js';
|
||||
import computeStateDomain from '../../../js/common/entity/compute_state_domain.js';
|
||||
|
||||
describe('computeStateDomain', () => {
|
||||
it('Detects sensor domain', () => {
|
@ -1,6 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import featureClassNames from '../../../js/common/util/feature_class_names';
|
||||
import featureClassNames from '../../../js/common/entity/feature_class_names';
|
||||
|
||||
describe('featureClassNames', () => {
|
||||
const classNames = {
|
@ -1,6 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import { hasLocation } from '../../../js/common/util/location';
|
||||
import hasLocation from '../../../js/common/entity/has_location.js';
|
||||
|
||||
describe('hasLocation', () => {
|
||||
it('flags states with location', () => {
|
@ -1,6 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import stateCardType from '../../../js/common/util/state_card_type';
|
||||
import stateCardType from '../../../js/common/entity/state_card_type.js';
|
||||
|
||||
describe('stateCardType', () => {
|
||||
const hass = {
|
@ -1,6 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import stateMoreInfoType from '../../../js/common/util/state_more_info_type';
|
||||
import stateMoreInfoType from '../../../js/common/entity/state_more_info_type.js';
|
||||
|
||||
describe('stateMoreInfoType', () => {
|
||||
it('Returns media_player for media_player states', () => {
|
@ -1,7 +1,7 @@
|
||||
import { assert } from 'chai';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import timerTimeRemaining from '../../../js/common/util/timer_time_remaining.js';
|
||||
import timerTimeRemaining from '../../../js/common/entity/timer_time_remaining.js';
|
||||
|
||||
describe('timerTimeRemaining', () => {
|
||||
it('works with idle timers', () => {
|
@ -3277,6 +3277,10 @@ feature-detect-es6@^1.3.1:
|
||||
dependencies:
|
||||
array-back "^1.0.3"
|
||||
|
||||
fecha@^2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/fecha/-/fecha-2.3.3.tgz#948e74157df1a32fd1b12c3a3c3cdcb6ec9d96cd"
|
||||
|
||||
figures@^1.3.5:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
|
||||
|
Loading…
x
Reference in New Issue
Block a user