From 912969111f7f50c1ba3c00ae6f25efe377d3e998 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 9 May 2018 21:33:31 -0400 Subject: [PATCH] Move all of hassUtil to JS (#1153) * Move all of hassUtil to JS * Fix tests --- bower.json | 1 - js/common/config/is_component_loaded.js | 4 + js/common/config/location_name.js | 4 + js/common/const.js | 42 ++ .../{util => datetime}/duration_to_seconds.js | 0 js/common/{util => datetime}/format_date.js | 4 +- .../{util => datetime}/format_date_time.js | 4 +- js/common/{util => datetime}/format_time.js | 4 +- js/common/datetime/relative_time.js | 30 ++ .../{util => datetime}/seconds_to_duration.js | 0 js/common/dom/apply_themes_on_element.js | 41 ++ js/common/dom/dynamic_content_updater.js | 33 ++ .../{util => entity}/attribute_class_names.js | 0 js/common/entity/binary_sensor_icon.js | 49 +++ .../{util => entity}/can_toggle_domain.js | 0 .../{util => entity}/can_toggle_state.js | 0 js/common/{util => entity}/compute_domain.js | 0 js/common/entity/compute_object_id.js | 4 + .../{util => entity}/compute_state_display.js | 6 +- .../{util => entity}/compute_state_domain.js | 0 js/common/entity/compute_state_name.js | 11 + js/common/entity/cover_icon.js | 12 + js/common/entity/domain_icon.js | 95 ++++ .../{util => entity}/feature_class_names.js | 0 .../location.js => entity/has_location.js} | 2 +- js/common/entity/input_dateteime_icon.js | 11 + js/common/entity/sensor_icon.js | 35 ++ js/common/{util => entity}/state_card_type.js | 15 +- js/common/entity/state_icon.js | 32 ++ .../{util => entity}/state_more_info_type.js | 0 js/common/entity/states_sort_by_name.js | 20 + .../{util => entity}/timer_time_remaining.js | 2 +- js/common/entity/valid_entity_id.js | 3 + js/common/{util => preact}/event.js | 0 js/common/util/entity.js | 3 - js/panel-config/condition/numeric_state.js | 2 +- js/panel-config/condition/state.js | 2 +- js/panel-config/condition/sun.js | 2 +- js/panel-config/condition/template.js | 2 +- js/panel-config/condition/time.js | 2 +- js/panel-config/condition/zone.js | 6 +- js/panel-config/script/delay.js | 2 +- js/panel-config/script/event.js | 2 +- js/panel-config/script/wait.js | 2 +- js/panel-config/trigger/event.js | 2 +- js/panel-config/trigger/mqtt.js | 2 +- js/panel-config/trigger/numeric_state.js | 2 +- js/panel-config/trigger/state.js | 2 +- js/panel-config/trigger/sun.js | 2 +- js/panel-config/trigger/template.js | 2 +- js/panel-config/trigger/time.js | 2 +- js/panel-config/trigger/zone.js | 6 +- js/util.js | 107 +++-- package.json | 1 + panels/config/script/ha-script-editor.html | 5 +- src/util/hass-util.html | 414 ------------------ .../duration_to_seconds_test.js | 2 +- .../common/{util => datetime}/format_date.js | 2 +- .../{util => datetime}/format_date_time.js | 2 +- .../common/{util => datetime}/format_time.js | 2 +- .../seconds_to_duration_test.js | 2 +- .../attribute_class_names_test.js | 2 +- .../can_toggle_domain_test.js | 2 +- .../{util => entity}/can_toggle_state_test.js | 2 +- .../common/{util => entity}/compute_domain.js | 2 +- .../{util => entity}/compute_state_display.js | 2 +- .../{util => entity}/compute_state_domain.js | 2 +- .../feature_class_names_test.js | 2 +- .../has_location.test.js} | 2 +- .../{util => entity}/state_card_type_test.js | 2 +- .../state_more_info_type_test.js | 2 +- .../timer_time_remaining_test.js | 2 +- yarn.lock | 4 + 73 files changed, 565 insertions(+), 507 deletions(-) create mode 100644 js/common/config/is_component_loaded.js create mode 100644 js/common/config/location_name.js create mode 100644 js/common/const.js rename js/common/{util => datetime}/duration_to_seconds.js (100%) rename js/common/{util => datetime}/format_date.js (86%) rename js/common/{util => datetime}/format_date_time.js (87%) rename js/common/{util => datetime}/format_time.js (86%) create mode 100644 js/common/datetime/relative_time.js rename js/common/{util => datetime}/seconds_to_duration.js (100%) create mode 100644 js/common/dom/apply_themes_on_element.js create mode 100644 js/common/dom/dynamic_content_updater.js rename js/common/{util => entity}/attribute_class_names.js (100%) create mode 100644 js/common/entity/binary_sensor_icon.js rename js/common/{util => entity}/can_toggle_domain.js (100%) rename js/common/{util => entity}/can_toggle_state.js (100%) rename js/common/{util => entity}/compute_domain.js (100%) create mode 100644 js/common/entity/compute_object_id.js rename js/common/{util => entity}/compute_state_display.js (94%) rename js/common/{util => entity}/compute_state_domain.js (100%) create mode 100644 js/common/entity/compute_state_name.js create mode 100644 js/common/entity/cover_icon.js create mode 100644 js/common/entity/domain_icon.js rename js/common/{util => entity}/feature_class_names.js (100%) rename js/common/{util/location.js => entity/has_location.js} (66%) create mode 100644 js/common/entity/input_dateteime_icon.js create mode 100644 js/common/entity/sensor_icon.js rename js/common/{util => entity}/state_card_type.js (72%) create mode 100644 js/common/entity/state_icon.js rename js/common/{util => entity}/state_more_info_type.js (100%) create mode 100644 js/common/entity/states_sort_by_name.js rename js/common/{util => entity}/timer_time_remaining.js (84%) create mode 100644 js/common/entity/valid_entity_id.js rename js/common/{util => preact}/event.js (100%) delete mode 100644 js/common/util/entity.js rename test-mocha/common/{util => datetime}/duration_to_seconds_test.js (72%) rename test-mocha/common/{util => datetime}/format_date.js (88%) rename test-mocha/common/{util => datetime}/format_date_time.js (87%) rename test-mocha/common/{util => datetime}/format_time.js (87%) rename test-mocha/common/{util => datetime}/seconds_to_duration_test.js (79%) rename test-mocha/common/{util => entity}/attribute_class_names_test.js (92%) rename test-mocha/common/{util => entity}/can_toggle_domain_test.js (91%) rename test-mocha/common/{util => entity}/can_toggle_state_test.js (94%) rename test-mocha/common/{util => entity}/compute_domain.js (84%) rename test-mocha/common/{util => entity}/compute_state_display.js (98%) rename test-mocha/common/{util => entity}/compute_state_domain.js (74%) rename test-mocha/common/{util => entity}/feature_class_names_test.js (93%) rename test-mocha/common/{util/location.js => entity/has_location.test.js} (91%) rename test-mocha/common/{util => entity}/state_card_type_test.js (94%) rename test-mocha/common/{util => entity}/state_more_info_type_test.js (89%) rename test-mocha/common/{util => entity}/timer_time_remaining_test.js (91%) diff --git a/bower.json b/bower.json index c1a4c8d271..bc5c123b19 100644 --- a/bower.json +++ b/bower.json @@ -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", diff --git a/js/common/config/is_component_loaded.js b/js/common/config/is_component_loaded.js new file mode 100644 index 0000000000..c371674524 --- /dev/null +++ b/js/common/config/is_component_loaded.js @@ -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; +} diff --git a/js/common/config/location_name.js b/js/common/config/location_name.js new file mode 100644 index 0000000000..f069a50ef8 --- /dev/null +++ b/js/common/config/location_name.js @@ -0,0 +1,4 @@ +/** Get the location name from a hass object. */ +export default function computeLocationName(hass) { + return hass && hass.config.core.location_name; +} diff --git a/js/common/const.js b/js/common/const.js new file mode 100644 index 0000000000..15247103e7 --- /dev/null +++ b/js/common/const.js @@ -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'; diff --git a/js/common/util/duration_to_seconds.js b/js/common/datetime/duration_to_seconds.js similarity index 100% rename from js/common/util/duration_to_seconds.js rename to js/common/datetime/duration_to_seconds.js diff --git a/js/common/util/format_date.js b/js/common/datetime/format_date.js similarity index 86% rename from js/common/util/format_date.js rename to js/common/datetime/format_date.js index 006e736f88..f0e2906985 100644 --- a/js/common/util/format_date.js +++ b/js/common/datetime/format_date.js @@ -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'); }); diff --git a/js/common/util/format_date_time.js b/js/common/datetime/format_date_time.js similarity index 87% rename from js/common/util/format_date_time.js rename to js/common/datetime/format_date_time.js index f695511b86..7fbc40c575 100644 --- a/js/common/util/format_date_time.js +++ b/js/common/datetime/format_date_time.js @@ -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'); }); diff --git a/js/common/util/format_time.js b/js/common/datetime/format_time.js similarity index 86% rename from js/common/util/format_time.js rename to js/common/datetime/format_time.js index 5ff05a67da..cab51ed534 100644 --- a/js/common/util/format_time.js +++ b/js/common/datetime/format_time.js @@ -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'); }); diff --git a/js/common/datetime/relative_time.js b/js/common/datetime/relative_time.js new file mode 100644 index 0000000000..2504017a74 --- /dev/null +++ b/js/common/datetime/relative_time.js @@ -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'); +} diff --git a/js/common/util/seconds_to_duration.js b/js/common/datetime/seconds_to_duration.js similarity index 100% rename from js/common/util/seconds_to_duration.js rename to js/common/datetime/seconds_to_duration.js diff --git a/js/common/dom/apply_themes_on_element.js b/js/common/dom/apply_themes_on_element.js new file mode 100644 index 0000000000..267b9bafd8 --- /dev/null +++ b/js/common/dom/apply_themes_on_element.js @@ -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); + } +} diff --git a/js/common/dom/dynamic_content_updater.js b/js/common/dom/dynamic_content_updater.js new file mode 100644 index 0000000000..3bdd2b0085 --- /dev/null +++ b/js/common/dom/dynamic_content_updater.js @@ -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); + } +} diff --git a/js/common/util/attribute_class_names.js b/js/common/entity/attribute_class_names.js similarity index 100% rename from js/common/util/attribute_class_names.js rename to js/common/entity/attribute_class_names.js diff --git a/js/common/entity/binary_sensor_icon.js b/js/common/entity/binary_sensor_icon.js new file mode 100644 index 0000000000..9aae304fc4 --- /dev/null +++ b/js/common/entity/binary_sensor_icon.js @@ -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'; + } +} diff --git a/js/common/util/can_toggle_domain.js b/js/common/entity/can_toggle_domain.js similarity index 100% rename from js/common/util/can_toggle_domain.js rename to js/common/entity/can_toggle_domain.js diff --git a/js/common/util/can_toggle_state.js b/js/common/entity/can_toggle_state.js similarity index 100% rename from js/common/util/can_toggle_state.js rename to js/common/entity/can_toggle_state.js diff --git a/js/common/util/compute_domain.js b/js/common/entity/compute_domain.js similarity index 100% rename from js/common/util/compute_domain.js rename to js/common/entity/compute_domain.js diff --git a/js/common/entity/compute_object_id.js b/js/common/entity/compute_object_id.js new file mode 100644 index 0000000000..bcb3f9627c --- /dev/null +++ b/js/common/entity/compute_object_id.js @@ -0,0 +1,4 @@ +/** Compute the object ID of a state. */ +export default function computeObjectId(entityId) { + return entityId.substr(entityId.indexOf('.') + 1); +} diff --git a/js/common/util/compute_state_display.js b/js/common/entity/compute_state_display.js similarity index 94% rename from js/common/util/compute_state_display.js rename to js/common/entity/compute_state_display.js index f62e1a0318..24e56393dd 100644 --- a/js/common/util/compute_state_display.js +++ b/js/common/entity/compute_state_display.js @@ -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) { diff --git a/js/common/util/compute_state_domain.js b/js/common/entity/compute_state_domain.js similarity index 100% rename from js/common/util/compute_state_domain.js rename to js/common/entity/compute_state_domain.js diff --git a/js/common/entity/compute_state_name.js b/js/common/entity/compute_state_name.js new file mode 100644 index 0000000000..c9297ef226 --- /dev/null +++ b/js/common/entity/compute_state_name.js @@ -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; +} diff --git a/js/common/entity/cover_icon.js b/js/common/entity/cover_icon.js new file mode 100644 index 0000000000..b3377a94ba --- /dev/null +++ b/js/common/entity/cover_icon.js @@ -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); + } +} diff --git a/js/common/entity/domain_icon.js b/js/common/entity/domain_icon.js new file mode 100644 index 0000000000..fb53b45cba --- /dev/null +++ b/js/common/entity/domain_icon.js @@ -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; + } +} diff --git a/js/common/util/feature_class_names.js b/js/common/entity/feature_class_names.js similarity index 100% rename from js/common/util/feature_class_names.js rename to js/common/entity/feature_class_names.js diff --git a/js/common/util/location.js b/js/common/entity/has_location.js similarity index 66% rename from js/common/util/location.js rename to js/common/entity/has_location.js index de86471ee0..6ec987d7ae 100644 --- a/js/common/util/location.js +++ b/js/common/entity/has_location.js @@ -1,4 +1,4 @@ -export function hasLocation(stateObj) { +export default function hasLocation(stateObj) { return ('latitude' in stateObj.attributes && 'longitude' in stateObj.attributes); } diff --git a/js/common/entity/input_dateteime_icon.js b/js/common/entity/input_dateteime_icon.js new file mode 100644 index 0000000000..bc892bb264 --- /dev/null +++ b/js/common/entity/input_dateteime_icon.js @@ -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'); +} diff --git a/js/common/entity/sensor_icon.js b/js/common/entity/sensor_icon.js new file mode 100644 index 0000000000..88d8992ab4 --- /dev/null +++ b/js/common/entity/sensor_icon.js @@ -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'); +} diff --git a/js/common/util/state_card_type.js b/js/common/entity/state_card_type.js similarity index 72% rename from js/common/util/state_card_type.js rename to js/common/entity/state_card_type.js index 65526a1991..b85153cdac 100644 --- a/js/common/util/state_card_type.js +++ b/js/common/entity/state_card_type.js @@ -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') { diff --git a/js/common/entity/state_icon.js b/js/common/entity/state_icon.js new file mode 100644 index 0000000000..b6fabf5a48 --- /dev/null +++ b/js/common/entity/state_icon.js @@ -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); +} diff --git a/js/common/util/state_more_info_type.js b/js/common/entity/state_more_info_type.js similarity index 100% rename from js/common/util/state_more_info_type.js rename to js/common/entity/state_more_info_type.js diff --git a/js/common/entity/states_sort_by_name.js b/js/common/entity/states_sort_by_name.js new file mode 100644 index 0000000000..3dbd2f8cce --- /dev/null +++ b/js/common/entity/states_sort_by_name.js @@ -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; +} diff --git a/js/common/util/timer_time_remaining.js b/js/common/entity/timer_time_remaining.js similarity index 84% rename from js/common/util/timer_time_remaining.js rename to js/common/entity/timer_time_remaining.js index 2d527131ea..e401264bff 100644 --- a/js/common/util/timer_time_remaining.js +++ b/js/common/entity/timer_time_remaining.js @@ -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); diff --git a/js/common/entity/valid_entity_id.js b/js/common/entity/valid_entity_id.js new file mode 100644 index 0000000000..5a4ea9e23a --- /dev/null +++ b/js/common/entity/valid_entity_id.js @@ -0,0 +1,3 @@ +export default function validEntityId(entityId) { + return /^(\w+)\.(\w+)$/.test(entityId); +} diff --git a/js/common/util/event.js b/js/common/preact/event.js similarity index 100% rename from js/common/util/event.js rename to js/common/preact/event.js diff --git a/js/common/util/entity.js b/js/common/util/entity.js deleted file mode 100644 index 50670c8a33..0000000000 --- a/js/common/util/entity.js +++ /dev/null @@ -1,3 +0,0 @@ -export function validEntityId(entityId) { - return /^(\w+)\.(\w+)$/.test(entityId); -} diff --git a/js/panel-config/condition/numeric_state.js b/js/panel-config/condition/numeric_state.js index 2e52cf11d7..b7adfad3b3 100644 --- a/js/panel-config/condition/numeric_state.js +++ b/js/panel-config/condition/numeric_state.js @@ -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() { diff --git a/js/panel-config/condition/state.js b/js/panel-config/condition/state.js index 362b6a8893..e5580c63a4 100644 --- a/js/panel-config/condition/state.js +++ b/js/panel-config/condition/state.js @@ -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() { diff --git a/js/panel-config/condition/sun.js b/js/panel-config/condition/sun.js index 087e7a00c3..2fe35c9ecd 100644 --- a/js/panel-config/condition/sun.js +++ b/js/panel-config/condition/sun.js @@ -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() { diff --git a/js/panel-config/condition/template.js b/js/panel-config/condition/template.js index 728274414d..2f6481e55c 100644 --- a/js/panel-config/condition/template.js +++ b/js/panel-config/condition/template.js @@ -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() { diff --git a/js/panel-config/condition/time.js b/js/panel-config/condition/time.js index 57f1c614df..ded00c3c75 100644 --- a/js/panel-config/condition/time.js +++ b/js/panel-config/condition/time.js @@ -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() { diff --git a/js/panel-config/condition/zone.js b/js/panel-config/condition/zone.js index ca6bc8ac23..d82c69a6c3 100644 --- a/js/panel-config/condition/zone.js +++ b/js/panel-config/condition/zone.js @@ -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'; diff --git a/js/panel-config/script/delay.js b/js/panel-config/script/delay.js index 02add9fe3f..a962f330d0 100644 --- a/js/panel-config/script/delay.js +++ b/js/panel-config/script/delay.js @@ -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() { diff --git a/js/panel-config/script/event.js b/js/panel-config/script/event.js index 1013ecf9db..643d1b57f7 100644 --- a/js/panel-config/script/event.js +++ b/js/panel-config/script/event.js @@ -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() { diff --git a/js/panel-config/script/wait.js b/js/panel-config/script/wait.js index 22ac63ba28..0032cc17f8 100644 --- a/js/panel-config/script/wait.js +++ b/js/panel-config/script/wait.js @@ -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() { diff --git a/js/panel-config/trigger/event.js b/js/panel-config/trigger/event.js index 7c8b0a8219..68a6c324ed 100644 --- a/js/panel-config/trigger/event.js +++ b/js/panel-config/trigger/event.js @@ -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() { diff --git a/js/panel-config/trigger/mqtt.js b/js/panel-config/trigger/mqtt.js index fb66a8c803..5f4c2bd012 100644 --- a/js/panel-config/trigger/mqtt.js +++ b/js/panel-config/trigger/mqtt.js @@ -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() { diff --git a/js/panel-config/trigger/numeric_state.js b/js/panel-config/trigger/numeric_state.js index ab20d86fdd..3aa85978cf 100644 --- a/js/panel-config/trigger/numeric_state.js +++ b/js/panel-config/trigger/numeric_state.js @@ -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() { diff --git a/js/panel-config/trigger/state.js b/js/panel-config/trigger/state.js index dd16e626eb..461a4a5789 100644 --- a/js/panel-config/trigger/state.js +++ b/js/panel-config/trigger/state.js @@ -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() { diff --git a/js/panel-config/trigger/sun.js b/js/panel-config/trigger/sun.js index 098940b2de..8140095e7a 100644 --- a/js/panel-config/trigger/sun.js +++ b/js/panel-config/trigger/sun.js @@ -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() { diff --git a/js/panel-config/trigger/template.js b/js/panel-config/trigger/template.js index 72f99e1613..527eb3c208 100644 --- a/js/panel-config/trigger/template.js +++ b/js/panel-config/trigger/template.js @@ -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() { diff --git a/js/panel-config/trigger/time.js b/js/panel-config/trigger/time.js index 929adec240..58c6e67ac4 100644 --- a/js/panel-config/trigger/time.js +++ b/js/panel-config/trigger/time.js @@ -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() { diff --git a/js/panel-config/trigger/zone.js b/js/panel-config/trigger/zone.js index 47996cffa3..b918cdd0c1 100644 --- a/js/panel-config/trigger/zone.js +++ b/js/panel-config/trigger/zone.js @@ -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'; diff --git a/js/util.js b/js/util.js index b970514fd4..70996c0f55 100644 --- a/js/util.js +++ b/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, +}; diff --git a/package.json b/package.json index 7f2f964a0d..846e55e2e7 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/panels/config/script/ha-script-editor.html b/panels/config/script/ha-script-editor.html index 59c7560775..6288508358 100644 --- a/panels/config/script/ha-script-editor.html +++ b/panels/config/script/ha-script-editor.html @@ -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; diff --git a/src/util/hass-util.html b/src/util/hass-util.html index 522d1dcb0a..55aa092345 100644 --- a/src/util/hass-util.html +++ b/src/util/hass-util.html @@ -1,415 +1 @@ - - - - diff --git a/test-mocha/common/util/duration_to_seconds_test.js b/test-mocha/common/datetime/duration_to_seconds_test.js similarity index 72% rename from test-mocha/common/util/duration_to_seconds_test.js rename to test-mocha/common/datetime/duration_to_seconds_test.js index 1454574e70..c4e503cee9 100644 --- a/test-mocha/common/util/duration_to_seconds_test.js +++ b/test-mocha/common/datetime/duration_to_seconds_test.js @@ -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', () => { diff --git a/test-mocha/common/util/format_date.js b/test-mocha/common/datetime/format_date.js similarity index 88% rename from test-mocha/common/util/format_date.js rename to test-mocha/common/datetime/format_date.js index 817fd9bb5a..28fe0c2680 100644 --- a/test-mocha/common/util/format_date.js +++ b/test-mocha/common/datetime/format_date.js @@ -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( diff --git a/test-mocha/common/util/format_date_time.js b/test-mocha/common/datetime/format_date_time.js similarity index 87% rename from test-mocha/common/util/format_date_time.js rename to test-mocha/common/datetime/format_date_time.js index 94540a0236..0c800d631d 100644 --- a/test-mocha/common/util/format_date_time.js +++ b/test-mocha/common/datetime/format_date_time.js @@ -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( diff --git a/test-mocha/common/util/format_time.js b/test-mocha/common/datetime/format_time.js similarity index 87% rename from test-mocha/common/util/format_time.js rename to test-mocha/common/datetime/format_time.js index 1e36c3e5d9..92f1a5513a 100644 --- a/test-mocha/common/util/format_time.js +++ b/test-mocha/common/datetime/format_time.js @@ -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( diff --git a/test-mocha/common/util/seconds_to_duration_test.js b/test-mocha/common/datetime/seconds_to_duration_test.js similarity index 79% rename from test-mocha/common/util/seconds_to_duration_test.js rename to test-mocha/common/datetime/seconds_to_duration_test.js index ba7088c467..35a9b1e457 100644 --- a/test-mocha/common/util/seconds_to_duration_test.js +++ b/test-mocha/common/datetime/seconds_to_duration_test.js @@ -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', () => { diff --git a/test-mocha/common/util/attribute_class_names_test.js b/test-mocha/common/entity/attribute_class_names_test.js similarity index 92% rename from test-mocha/common/util/attribute_class_names_test.js rename to test-mocha/common/entity/attribute_class_names_test.js index 309dd83b35..278880e56c 100644 --- a/test-mocha/common/util/attribute_class_names_test.js +++ b/test-mocha/common/entity/attribute_class_names_test.js @@ -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']; diff --git a/test-mocha/common/util/can_toggle_domain_test.js b/test-mocha/common/entity/can_toggle_domain_test.js similarity index 91% rename from test-mocha/common/util/can_toggle_domain_test.js rename to test-mocha/common/entity/can_toggle_domain_test.js index a357cd5c0e..0a422900e4 100644 --- a/test-mocha/common/util/can_toggle_domain_test.js +++ b/test-mocha/common/entity/can_toggle_domain_test.js @@ -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 = { diff --git a/test-mocha/common/util/can_toggle_state_test.js b/test-mocha/common/entity/can_toggle_state_test.js similarity index 94% rename from test-mocha/common/util/can_toggle_state_test.js rename to test-mocha/common/entity/can_toggle_state_test.js index d5159864da..9055ceb087 100644 --- a/test-mocha/common/util/can_toggle_state_test.js +++ b/test-mocha/common/entity/can_toggle_state_test.js @@ -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 = { diff --git a/test-mocha/common/util/compute_domain.js b/test-mocha/common/entity/compute_domain.js similarity index 84% rename from test-mocha/common/util/compute_domain.js rename to test-mocha/common/entity/compute_domain.js index 708f5ff215..c949c3e43e 100644 --- a/test-mocha/common/util/compute_domain.js +++ b/test-mocha/common/entity/compute_domain.js @@ -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', () => { diff --git a/test-mocha/common/util/compute_state_display.js b/test-mocha/common/entity/compute_state_display.js similarity index 98% rename from test-mocha/common/util/compute_state_display.js rename to test-mocha/common/entity/compute_state_display.js index ce925dc43e..b871a5923f 100644 --- a/test-mocha/common/util/compute_state_display.js +++ b/test-mocha/common/entity/compute_state_display.js @@ -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) { diff --git a/test-mocha/common/util/compute_state_domain.js b/test-mocha/common/entity/compute_state_domain.js similarity index 74% rename from test-mocha/common/util/compute_state_domain.js rename to test-mocha/common/entity/compute_state_domain.js index 38620bd60b..662e6e1516 100644 --- a/test-mocha/common/util/compute_state_domain.js +++ b/test-mocha/common/entity/compute_state_domain.js @@ -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', () => { diff --git a/test-mocha/common/util/feature_class_names_test.js b/test-mocha/common/entity/feature_class_names_test.js similarity index 93% rename from test-mocha/common/util/feature_class_names_test.js rename to test-mocha/common/entity/feature_class_names_test.js index 11ea64b7c8..623ff1d735 100644 --- a/test-mocha/common/util/feature_class_names_test.js +++ b/test-mocha/common/entity/feature_class_names_test.js @@ -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 = { diff --git a/test-mocha/common/util/location.js b/test-mocha/common/entity/has_location.test.js similarity index 91% rename from test-mocha/common/util/location.js rename to test-mocha/common/entity/has_location.test.js index fefc90e6af..b5ed28b651 100644 --- a/test-mocha/common/util/location.js +++ b/test-mocha/common/entity/has_location.test.js @@ -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', () => { diff --git a/test-mocha/common/util/state_card_type_test.js b/test-mocha/common/entity/state_card_type_test.js similarity index 94% rename from test-mocha/common/util/state_card_type_test.js rename to test-mocha/common/entity/state_card_type_test.js index 76fe75a526..11d149d4b3 100644 --- a/test-mocha/common/util/state_card_type_test.js +++ b/test-mocha/common/entity/state_card_type_test.js @@ -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 = { diff --git a/test-mocha/common/util/state_more_info_type_test.js b/test-mocha/common/entity/state_more_info_type_test.js similarity index 89% rename from test-mocha/common/util/state_more_info_type_test.js rename to test-mocha/common/entity/state_more_info_type_test.js index b9e5fa4fed..7d3afe770b 100644 --- a/test-mocha/common/util/state_more_info_type_test.js +++ b/test-mocha/common/entity/state_more_info_type_test.js @@ -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', () => { diff --git a/test-mocha/common/util/timer_time_remaining_test.js b/test-mocha/common/entity/timer_time_remaining_test.js similarity index 91% rename from test-mocha/common/util/timer_time_remaining_test.js rename to test-mocha/common/entity/timer_time_remaining_test.js index 8ab2678c0e..f2e69ec7a5 100644 --- a/test-mocha/common/util/timer_time_remaining_test.js +++ b/test-mocha/common/entity/timer_time_remaining_test.js @@ -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', () => { diff --git a/yarn.lock b/yarn.lock index e43d83ffa2..e223128240 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"