mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-16 13:56:35 +00:00
Add timer card and badge (#810)
* Add timer card and badge * Disable interval on disconnect * Tests! * One more test case * Remove padStart * Remove state from timer state card
This commit is contained in:
parent
85d58ba134
commit
783f356679
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-restricted-syntax": 0,
|
"import/no-extraneous-dependencies": 0,
|
||||||
"no-console": 0
|
"no-restricted-syntax": 0,
|
||||||
|
"no-console": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
js/common/util/duration_to_seconds.js
Normal file
4
js/common/util/duration_to_seconds.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export default function durationToSeconds(duration) {
|
||||||
|
const parts = duration.split(':').map(Number);
|
||||||
|
return (parts[0] * 3600) + (parts[1] * 60) + parts[2];
|
||||||
|
}
|
16
js/common/util/seconds_to_duration.js
Normal file
16
js/common/util/seconds_to_duration.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
const leftPad = number => (number < 10 ? `0${number}` : number);
|
||||||
|
|
||||||
|
export default function secondsToDuration(d) {
|
||||||
|
const h = Math.floor(d / 3600);
|
||||||
|
const m = Math.floor((d % 3600) / 60);
|
||||||
|
const s = Math.floor(d % 3600 % 60);
|
||||||
|
|
||||||
|
if (h > 0) {
|
||||||
|
return `${h}:${leftPad(m)}:${leftPad(s)}`;
|
||||||
|
} else if (m > 0) {
|
||||||
|
return `${m}:${leftPad(s)}`;
|
||||||
|
} else if (s > 0) {
|
||||||
|
return '' + s;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
@ -11,6 +11,7 @@ const DOMAINS_WITH_CARD = [
|
|||||||
'media_player',
|
'media_player',
|
||||||
'scene',
|
'scene',
|
||||||
'script',
|
'script',
|
||||||
|
'timer',
|
||||||
'weblink',
|
'weblink',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
13
js/common/util/timer_time_remaining.js
Normal file
13
js/common/util/timer_time_remaining.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import durationToSeconds from './duration_to_seconds.js';
|
||||||
|
|
||||||
|
export default function timerTimeRemaining(stateObj) {
|
||||||
|
let timeRemaining = durationToSeconds(stateObj.attributes.remaining);
|
||||||
|
|
||||||
|
if (stateObj.state === 'active') {
|
||||||
|
const now = new Date();
|
||||||
|
const madeActive = new Date(stateObj.last_changed);
|
||||||
|
timeRemaining = Math.max(timeRemaining - ((now - madeActive) / 1000), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return timeRemaining;
|
||||||
|
}
|
32
js/util.js
32
js/util.js
@ -9,14 +9,17 @@
|
|||||||
import attributeClassNames from './common/util/attribute_class_names.js';
|
import attributeClassNames from './common/util/attribute_class_names.js';
|
||||||
import canToggleDomain from './common/util/can_toggle_domain.js';
|
import canToggleDomain from './common/util/can_toggle_domain.js';
|
||||||
import canToggleState from './common/util/can_toggle_state.js';
|
import canToggleState from './common/util/can_toggle_state.js';
|
||||||
import computeStateDomain from './common/util/compute_state_domain.js';
|
|
||||||
import computeStateDisplay from './common/util/compute_state_display.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 featureClassNames from './common/util/feature_class_names.js';
|
||||||
import formatDate from './common/util/format_date.js';
|
import formatDate from './common/util/format_date.js';
|
||||||
import formatDateTime from './common/util/format_date_time.js';
|
import formatDateTime from './common/util/format_date_time.js';
|
||||||
import formatTime from './common/util/format_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 stateCardType from './common/util/state_card_type.js';
|
||||||
import stateMoreInfoType from './common/util/state_more_info_type.js';
|
import stateMoreInfoType from './common/util/state_more_info_type.js';
|
||||||
|
import timerTimeRemaining from './common/util/timer_time_remaining.js';
|
||||||
|
|
||||||
window.hassUtil = window.hassUtil || {};
|
window.hassUtil = window.hassUtil || {};
|
||||||
|
|
||||||
@ -25,14 +28,19 @@ const language = navigator.languages ?
|
|||||||
|
|
||||||
window.fecha.masks.haDateTime = window.fecha.masks.shortTime + ' ' + window.fecha.masks.mediumDate;
|
window.fecha.masks.haDateTime = window.fecha.masks.shortTime + ' ' + window.fecha.masks.mediumDate;
|
||||||
|
|
||||||
window.hassUtil.attributeClassNames = attributeClassNames;
|
Object.assign(window.hassUtil, {
|
||||||
window.hassUtil.canToggleDomain = canToggleDomain;
|
attributeClassNames,
|
||||||
window.hassUtil.canToggleState = canToggleState;
|
canToggleDomain,
|
||||||
window.hassUtil.computeDomain = computeStateDomain;
|
canToggleState,
|
||||||
window.hassUtil.computeStateDisplay = computeStateDisplay;
|
computeDomain,
|
||||||
window.hassUtil.featureClassNames = featureClassNames;
|
computeStateDisplay,
|
||||||
window.hassUtil.formatDate = dateObj => formatDate(dateObj, language);
|
durationToSeconds,
|
||||||
window.hassUtil.formatDateTime = dateObj => formatDateTime(dateObj, language);
|
featureClassNames,
|
||||||
window.hassUtil.formatTime = dateObj => formatTime(dateObj, language);
|
secondsToDuration,
|
||||||
window.hassUtil.stateCardType = stateCardType;
|
stateCardType,
|
||||||
window.hassUtil.stateMoreInfoType = stateMoreInfoType;
|
stateMoreInfoType,
|
||||||
|
timerTimeRemaining,
|
||||||
|
formatDate: dateObj => formatDate(dateObj, language),
|
||||||
|
formatDateTime: dateObj => formatDateTime(dateObj, language),
|
||||||
|
formatTime: dateObj => formatTime(dateObj, language),
|
||||||
|
});
|
||||||
|
15
package.json
15
package.json
@ -23,6 +23,13 @@
|
|||||||
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
|
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"es6-object-assign": "^1.1.0",
|
||||||
|
"home-assistant-js-websocket": "^1.1.2",
|
||||||
|
"mdn-polyfills": "^5.5.0",
|
||||||
|
"preact": "^8.2.6",
|
||||||
|
"unfetch": "^3.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
"babel-core": "^6.26.0",
|
"babel-core": "^6.26.0",
|
||||||
"babel-plugin-external-helpers": "^6.22.0",
|
"babel-plugin-external-helpers": "^6.22.0",
|
||||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||||
@ -32,7 +39,6 @@
|
|||||||
"chai": "^4.1.2",
|
"chai": "^4.1.2",
|
||||||
"css-slam": "^2.0.2",
|
"css-slam": "^2.0.2",
|
||||||
"del": "^3.0.0",
|
"del": "^3.0.0",
|
||||||
"es6-object-assign": "^1.1.0",
|
|
||||||
"eslint": "^4.11.0",
|
"eslint": "^4.11.0",
|
||||||
"eslint-config-airbnb-base": "^12.1.0",
|
"eslint-config-airbnb-base": "^12.1.0",
|
||||||
"eslint-plugin-html": "^3.2.2",
|
"eslint-plugin-html": "^3.2.2",
|
||||||
@ -58,9 +64,7 @@
|
|||||||
"gulp-util": "^3.0.8",
|
"gulp-util": "^3.0.8",
|
||||||
"gulp-vinyl-zip": "^2.1.0",
|
"gulp-vinyl-zip": "^2.1.0",
|
||||||
"gulp-zopfli": "^1.0.0",
|
"gulp-zopfli": "^1.0.0",
|
||||||
"home-assistant-js-websocket": "^1.1.2",
|
|
||||||
"html-minifier": "^3.5.6",
|
"html-minifier": "^3.5.6",
|
||||||
"mdn-polyfills": "^5.5.0",
|
|
||||||
"merge-stream": "^1.0.1",
|
"merge-stream": "^1.0.1",
|
||||||
"mocha": "^4.0.1",
|
"mocha": "^4.0.1",
|
||||||
"parse5": "^3.0.3",
|
"parse5": "^3.0.3",
|
||||||
@ -68,7 +72,6 @@
|
|||||||
"polymer-build": "^2.1.0",
|
"polymer-build": "^2.1.0",
|
||||||
"polymer-bundler": "^3.1.0",
|
"polymer-bundler": "^3.1.0",
|
||||||
"polymer-cli": "^1.5.6",
|
"polymer-cli": "^1.5.6",
|
||||||
"preact": "^8.2.6",
|
|
||||||
"pump": "^1.0.2",
|
"pump": "^1.0.2",
|
||||||
"reify": "^0.12.3",
|
"reify": "^0.12.3",
|
||||||
"require-dir": "^0.3.2",
|
"require-dir": "^0.3.2",
|
||||||
@ -79,12 +82,10 @@
|
|||||||
"rollup-plugin-replace": "^2.0.0",
|
"rollup-plugin-replace": "^2.0.0",
|
||||||
"rollup-watch": "^4.3.1",
|
"rollup-watch": "^4.3.1",
|
||||||
"run-sequence": "^2.2.0",
|
"run-sequence": "^2.2.0",
|
||||||
|
"sinon": "^4.1.6",
|
||||||
"sw-precache": "^5.2.0",
|
"sw-precache": "^5.2.0",
|
||||||
"uglify-es": "^3.1.9",
|
"uglify-es": "^3.1.9",
|
||||||
"uglify-js": "^3.1.9",
|
"uglify-js": "^3.1.9",
|
||||||
"unfetch": "^3.0.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"web-component-tester": "^6.4.0"
|
"web-component-tester": "^6.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
value='[[computeValue(state)]]'
|
value='[[computeValue(state)]]'
|
||||||
icon='[[computeIcon(state)]]'
|
icon='[[computeIcon(state)]]'
|
||||||
image='[[computeImage(state)]]'
|
image='[[computeImage(state)]]'
|
||||||
label='[[computeLabel(localize, state)]]'
|
label='[[computeLabel(localize, state, timerTimeRemaining)]]'
|
||||||
description='[[computeDescription(state)]]'
|
description='[[computeDescription(state)]]'
|
||||||
></ha-label-badge>
|
></ha-label-badge>
|
||||||
</template>
|
</template>
|
||||||
@ -62,9 +62,23 @@ class HaStateLabelBadge extends
|
|||||||
type: Object,
|
type: Object,
|
||||||
observer: 'stateChanged',
|
observer: 'stateChanged',
|
||||||
},
|
},
|
||||||
|
timerTimeRemaining: {
|
||||||
|
type: Number,
|
||||||
|
value: 0,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
this.startInterval(this.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
this.clearInterval();
|
||||||
|
}
|
||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
super.ready();
|
super.ready();
|
||||||
this.addEventListener('tap', ev => this.badgeTap(ev));
|
this.addEventListener('tap', ev => this.badgeTap(ev));
|
||||||
@ -92,6 +106,7 @@ class HaStateLabelBadge extends
|
|||||||
case 'updater':
|
case 'updater':
|
||||||
case 'sun':
|
case 'sun':
|
||||||
case 'alarm_control_panel':
|
case 'alarm_control_panel':
|
||||||
|
case 'timer':
|
||||||
return null;
|
return null;
|
||||||
case 'sensor':
|
case 'sensor':
|
||||||
default:
|
default:
|
||||||
@ -128,6 +143,8 @@ class HaStateLabelBadge extends
|
|||||||
case 'sun':
|
case 'sun':
|
||||||
return state.state === 'above_horizon' ?
|
return state.state === 'above_horizon' ?
|
||||||
window.hassUtil.domainIcon(domain) : 'mdi:brightness-3';
|
window.hassUtil.domainIcon(domain) : 'mdi:brightness-3';
|
||||||
|
case 'timer':
|
||||||
|
return state.state === 'active' ? 'mdi:timer' : 'mdi:timer-off';
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -137,7 +154,7 @@ class HaStateLabelBadge extends
|
|||||||
return state.attributes.entity_picture || null;
|
return state.attributes.entity_picture || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
computeLabel(localize, state) {
|
computeLabel(localize, state, timerTimeRemaining) {
|
||||||
const domain = window.hassUtil.computeDomain(state);
|
const domain = window.hassUtil.computeDomain(state);
|
||||||
if (state.state === 'unavailable' ||
|
if (state.state === 'unavailable' ||
|
||||||
['device_tracker', 'alarm_control_panel'].includes(domain)) {
|
['device_tracker', 'alarm_control_panel'].includes(domain)) {
|
||||||
@ -146,6 +163,9 @@ class HaStateLabelBadge extends
|
|||||||
// are only added for device_tracker and alarm_control_panel.
|
// are only added for device_tracker and alarm_control_panel.
|
||||||
return localize(`state_badge.${domain}.${state.state}`) || localize(`state_badge.default.${state.state}`) || state.state;
|
return localize(`state_badge.${domain}.${state.state}`) || localize(`state_badge.default.${state.state}`) || state.state;
|
||||||
}
|
}
|
||||||
|
if (domain === 'timer') {
|
||||||
|
return window.hassUtil.secondsToDuration(timerTimeRemaining);
|
||||||
|
}
|
||||||
return state.attributes.unit_of_measurement || null;
|
return state.attributes.unit_of_measurement || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,8 +173,31 @@ class HaStateLabelBadge extends
|
|||||||
return window.hassUtil.computeStateName(state);
|
return window.hassUtil.computeStateName(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
stateChanged() {
|
stateChanged(stateObj) {
|
||||||
this.updateStyles();
|
this.updateStyles();
|
||||||
|
this.startInterval(stateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearInterval() {
|
||||||
|
if (this._updateRemaining) {
|
||||||
|
clearInterval(this._updateRemaining);
|
||||||
|
this._updateRemaining = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startInterval(stateObj) {
|
||||||
|
this.clearInterval();
|
||||||
|
if (window.hassUtil.computeDomain(stateObj) === 'timer') {
|
||||||
|
this.calculateTimerRemaining(stateObj);
|
||||||
|
|
||||||
|
if (stateObj.state === 'active') {
|
||||||
|
this._updateRemaining = setInterval(() => this.calculateTimerRemaining(this.state), 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateTimerRemaining(stateObj) {
|
||||||
|
this.timerTimeRemaining = window.hassUtil.timerTimeRemaining(stateObj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +106,7 @@
|
|||||||
sun: 1,
|
sun: 1,
|
||||||
device_tracker: 2,
|
device_tracker: 2,
|
||||||
alarm_control_panel: 3,
|
alarm_control_panel: 3,
|
||||||
|
timer: 4,
|
||||||
sensor: 5,
|
sensor: 5,
|
||||||
binary_sensor: 6,
|
binary_sensor: 6,
|
||||||
mailbox: 7,
|
mailbox: 7,
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
<link rel="import" href="state-card-media_player.html">
|
<link rel="import" href="state-card-media_player.html">
|
||||||
<link rel="import" href="state-card-scene.html">
|
<link rel="import" href="state-card-scene.html">
|
||||||
<link rel="import" href="state-card-script.html">
|
<link rel="import" href="state-card-script.html">
|
||||||
|
<link rel="import" href="state-card-timer.html">
|
||||||
<link rel="import" href="state-card-toggle.html">
|
<link rel="import" href="state-card-toggle.html">
|
||||||
<link rel="import" href="state-card-weblink.html">
|
<link rel="import" href="state-card-weblink.html">
|
||||||
|
|
||||||
|
88
src/state-summary/state-card-timer.html
Normal file
88
src/state-summary/state-card-timer.html
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
|
||||||
|
|
||||||
|
<link rel="import" href="../../bower_components/iron-flex-layout/iron-flex-layout-classes.html">
|
||||||
|
|
||||||
|
<link rel="import" href="../components/entity/state-info.html">
|
||||||
|
<link rel="import" href="../util/hass-mixins.html">
|
||||||
|
<link rel="import" href="../util/hass-util.html">
|
||||||
|
|
||||||
|
<dom-module id="state-card-timer">
|
||||||
|
<template>
|
||||||
|
<style is="custom-style" include="iron-flex iron-flex-alignment"></style>
|
||||||
|
<style>
|
||||||
|
.state {
|
||||||
|
@apply(--paper-font-body1);
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
|
||||||
|
margin-left: 16px;
|
||||||
|
text-align: right;
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class='horizontal justified layout'>
|
||||||
|
<state-info state-obj="[[stateObj]]" in-dialog='[[inDialog]]'></state-info>
|
||||||
|
<div class='state'>[[secondsToDuration(timeRemaining)]]</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
class StateCardTimer extends window.hassMixins.LocalizeMixin(Polymer.Element) {
|
||||||
|
static get is() { return 'state-card-timer'; }
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
hass: Object,
|
||||||
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
|
observer: 'stateObjChanged',
|
||||||
|
},
|
||||||
|
timeRemaining: Number,
|
||||||
|
inDialog: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
this.startInterval(this.stateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
this.clearInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
stateObjChanged(stateObj) {
|
||||||
|
this.startInterval(stateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearInterval() {
|
||||||
|
if (this._updateRemaining) {
|
||||||
|
clearInterval(this._updateRemaining);
|
||||||
|
this._updateRemaining = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startInterval(stateObj) {
|
||||||
|
this.clearInterval();
|
||||||
|
this.calculateRemaining(stateObj);
|
||||||
|
|
||||||
|
if (stateObj.state === 'active') {
|
||||||
|
this._updateRemaining = setInterval(() => this.calculateRemaining(this.stateObj), 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateRemaining(stateObj) {
|
||||||
|
this.timeRemaining = window.hassUtil.timerTimeRemaining(stateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
secondsToDuration(time) {
|
||||||
|
return window.hassUtil.secondsToDuration(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define(StateCardTimer.is, StateCardTimer);
|
||||||
|
</script>
|
@ -187,6 +187,9 @@ window.hassUtil.domainIcon = function (domain, state) {
|
|||||||
case 'switch':
|
case 'switch':
|
||||||
return 'mdi:flash';
|
return 'mdi:flash';
|
||||||
|
|
||||||
|
case 'timer':
|
||||||
|
return 'mdi:timer';
|
||||||
|
|
||||||
case 'updater':
|
case 'updater':
|
||||||
return 'mdi:cloud-upload';
|
return 'mdi:cloud-upload';
|
||||||
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"env": {
|
"env": {
|
||||||
"mocha": true
|
"mocha": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"import/no-extraneous-dependencies": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
test-mocha/common/util/duration_to_seconds_test.js
Normal file
10
test-mocha/common/util/duration_to_seconds_test.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { assert } from 'chai';
|
||||||
|
|
||||||
|
import durationToSeconds from '../../../js/common/util/duration_to_seconds.js';
|
||||||
|
|
||||||
|
describe('durationToSeconds', () => {
|
||||||
|
it('works', () => {
|
||||||
|
assert.strictEqual(durationToSeconds('0:01:05'), 65);
|
||||||
|
assert.strictEqual(durationToSeconds('11:01:05'), 39665);
|
||||||
|
});
|
||||||
|
});
|
12
test-mocha/common/util/seconds_to_duration_test.js
Normal file
12
test-mocha/common/util/seconds_to_duration_test.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { assert } from 'chai';
|
||||||
|
|
||||||
|
import secondsToDuration from '../../../js/common/util/seconds_to_duration.js';
|
||||||
|
|
||||||
|
describe('secondsToDuration', () => {
|
||||||
|
it('works', () => {
|
||||||
|
assert.strictEqual(secondsToDuration(0), null);
|
||||||
|
assert.strictEqual(secondsToDuration(65), '1:05');
|
||||||
|
assert.strictEqual(secondsToDuration(3665), '1:01:05');
|
||||||
|
assert.strictEqual(secondsToDuration(39665), '11:01:05');
|
||||||
|
});
|
||||||
|
});
|
43
test-mocha/common/util/timer_time_remaining_test.js
Normal file
43
test-mocha/common/util/timer_time_remaining_test.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { assert } from 'chai';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
import timerTimeRemaining from '../../../js/common/util/timer_time_remaining.js';
|
||||||
|
|
||||||
|
describe('timerTimeRemaining', () => {
|
||||||
|
it('works with idle timers', () => {
|
||||||
|
assert.strictEqual(timerTimeRemaining({
|
||||||
|
state: 'idle',
|
||||||
|
attributes: {
|
||||||
|
remaining: '0:01:05'
|
||||||
|
}
|
||||||
|
}), 65);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works with paused timers', () => {
|
||||||
|
assert.strictEqual(timerTimeRemaining({
|
||||||
|
state: 'paused',
|
||||||
|
attributes: {
|
||||||
|
remaining: '0:01:05'
|
||||||
|
}
|
||||||
|
}), 65);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('active timers', () => {
|
||||||
|
let clock;
|
||||||
|
beforeEach(() => {
|
||||||
|
clock = sinon.useFakeTimers(new Date('2018-01-17T16:15:30Z'));
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
clock.restore();
|
||||||
|
});
|
||||||
|
it('works', () => {
|
||||||
|
assert.strictEqual(timerTimeRemaining({
|
||||||
|
state: 'active',
|
||||||
|
attributes: {
|
||||||
|
remaining: '0:01:05'
|
||||||
|
},
|
||||||
|
last_changed: '2018-01-17T16:15:12Z',
|
||||||
|
}), 47);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
48
yarn.lock
48
yarn.lock
@ -3275,7 +3275,7 @@ formatio@1.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
samsam "~1.1"
|
samsam "~1.1"
|
||||||
|
|
||||||
formatio@1.2.0:
|
formatio@1.2.0, formatio@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.2.0.tgz#f3b2167d9068c4698a8d51f4f760a39a54d818eb"
|
resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.2.0.tgz#f3b2167d9068c4698a8d51f4f760a39a54d818eb"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -4679,6 +4679,10 @@ jsx-ast-utils@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
array-includes "^3.0.3"
|
array-includes "^3.0.3"
|
||||||
|
|
||||||
|
just-extend@^1.1.26:
|
||||||
|
version "1.1.27"
|
||||||
|
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-1.1.27.tgz#ec6e79410ff914e472652abfa0e603c03d60e905"
|
||||||
|
|
||||||
kind-of@^3.0.2:
|
kind-of@^3.0.2:
|
||||||
version "3.2.2"
|
version "3.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
|
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
|
||||||
@ -4968,6 +4972,10 @@ lodash.escape@~2.4.1:
|
|||||||
lodash._reunescapedhtml "~2.4.1"
|
lodash._reunescapedhtml "~2.4.1"
|
||||||
lodash.keys "~2.4.1"
|
lodash.keys "~2.4.1"
|
||||||
|
|
||||||
|
lodash.get@^4.4.2:
|
||||||
|
version "4.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
|
||||||
|
|
||||||
lodash.identity@~2.4.1:
|
lodash.identity@~2.4.1:
|
||||||
version "2.4.1"
|
version "2.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.identity/-/lodash.identity-2.4.1.tgz#6694cffa65fef931f7c31ce86c74597cf560f4f1"
|
resolved "https://registry.yarnpkg.com/lodash.identity/-/lodash.identity-2.4.1.tgz#6694cffa65fef931f7c31ce86c74597cf560f4f1"
|
||||||
@ -5137,6 +5145,10 @@ lolex@^1.6.0:
|
|||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.6.0.tgz#3a9a0283452a47d7439e72731b9e07d7386e49f6"
|
resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.6.0.tgz#3a9a0283452a47d7439e72731b9e07d7386e49f6"
|
||||||
|
|
||||||
|
lolex@^2.2.0:
|
||||||
|
version "2.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.3.1.tgz#3d2319894471ea0950ef64692ead2a5318cff362"
|
||||||
|
|
||||||
longest@^1.0.1:
|
longest@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
||||||
@ -5544,6 +5556,16 @@ netrc@^0.1.4:
|
|||||||
version "0.1.4"
|
version "0.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/netrc/-/netrc-0.1.4.tgz#6be94fcaca8d77ade0a9670dc460914c94472444"
|
resolved "https://registry.yarnpkg.com/netrc/-/netrc-0.1.4.tgz#6be94fcaca8d77ade0a9670dc460914c94472444"
|
||||||
|
|
||||||
|
nise@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/nise/-/nise-1.2.0.tgz#079d6cadbbcb12ba30e38f1c999f36ad4d6baa53"
|
||||||
|
dependencies:
|
||||||
|
formatio "^1.2.0"
|
||||||
|
just-extend "^1.1.26"
|
||||||
|
lolex "^1.6.0"
|
||||||
|
path-to-regexp "^1.7.0"
|
||||||
|
text-encoding "^0.6.4"
|
||||||
|
|
||||||
no-case@^2.2.0:
|
no-case@^2.2.0:
|
||||||
version "2.3.1"
|
version "2.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.1.tgz#7aeba1c73a52184265554b7dc03baf720df80081"
|
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.1.tgz#7aeba1c73a52184265554b7dc03baf720df80081"
|
||||||
@ -7150,6 +7172,18 @@ sinon@^2.3.5:
|
|||||||
text-encoding "0.6.4"
|
text-encoding "0.6.4"
|
||||||
type-detect "^4.0.0"
|
type-detect "^4.0.0"
|
||||||
|
|
||||||
|
sinon@^4.1.6:
|
||||||
|
version "4.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.1.6.tgz#9cb346bddb180d68a804429ffe14978d7fafd629"
|
||||||
|
dependencies:
|
||||||
|
diff "^3.1.0"
|
||||||
|
formatio "1.2.0"
|
||||||
|
lodash.get "^4.4.2"
|
||||||
|
lolex "^2.2.0"
|
||||||
|
nise "^1.2.0"
|
||||||
|
supports-color "^5.1.0"
|
||||||
|
type-detect "^4.0.5"
|
||||||
|
|
||||||
slash@^1.0.0:
|
slash@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
|
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
|
||||||
@ -7556,6 +7590,12 @@ supports-color@^2.0.0:
|
|||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
|
||||||
|
|
||||||
|
supports-color@^5.1.0:
|
||||||
|
version "5.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.1.0.tgz#058a021d1b619f7ddf3980d712ea3590ce7de3d5"
|
||||||
|
dependencies:
|
||||||
|
has-flag "^2.0.0"
|
||||||
|
|
||||||
sw-precache@^5.1.1, sw-precache@^5.2.0:
|
sw-precache@^5.1.1, sw-precache@^5.2.0:
|
||||||
version "5.2.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/sw-precache/-/sw-precache-5.2.0.tgz#eb6225ce580ceaae148194578a0ad01ab7ea199c"
|
resolved "https://registry.yarnpkg.com/sw-precache/-/sw-precache-5.2.0.tgz#eb6225ce580ceaae148194578a0ad01ab7ea199c"
|
||||||
@ -7684,7 +7724,7 @@ test-value@^2.1.0:
|
|||||||
array-back "^1.0.3"
|
array-back "^1.0.3"
|
||||||
typical "^2.6.0"
|
typical "^2.6.0"
|
||||||
|
|
||||||
text-encoding@0.6.4:
|
text-encoding@0.6.4, text-encoding@^0.6.4:
|
||||||
version "0.6.4"
|
version "0.6.4"
|
||||||
resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19"
|
resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19"
|
||||||
|
|
||||||
@ -7854,6 +7894,10 @@ type-detect@^4.0.0:
|
|||||||
version "4.0.5"
|
version "4.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.5.tgz#d70e5bc81db6de2a381bcaca0c6e0cbdc7635de2"
|
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.5.tgz#d70e5bc81db6de2a381bcaca0c6e0cbdc7635de2"
|
||||||
|
|
||||||
|
type-detect@^4.0.5:
|
||||||
|
version "4.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.6.tgz#88cbce3d13bc675a63f840b3225c180f870786d7"
|
||||||
|
|
||||||
type-is@^1.6.4, type-is@~1.6.15:
|
type-is@^1.6.4, type-is@~1.6.15:
|
||||||
version "1.6.15"
|
version "1.6.15"
|
||||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410"
|
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user