Skip to content

Commit

Permalink
Merge pull request #7 from Scoobler/master
Browse files Browse the repository at this point in the history
Bug fix + additional features
  • Loading branch information
karlis-vagalis authored Dec 29, 2023
2 parents 348dd89 + c493abc commit eaf2521
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 45 deletions.
30 changes: 19 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,25 @@ Double tap will cancel (reset) timer

All following options which support colors can also be used with hexadecimal color identifier with opacity!

| Option | Type | Requirement | Description |
|---|---|---|---|
| entity | string | **required** | Timer entity ID |
| bins | int | optional | Number of bins (arcs). Default: 36 |
| pad_angle | float | optional | Seperating angle between each individual arc in degrees. Default: 1 |
| corner_radius | float | optional | Radius for rounded arc corners. Default: 4 |
| color | array of strings | optional | Color array used for filling remaining timer arc units. If array contains only single value, that color will be used to fill the whole timer, otherwise linear gradient will be created |
| color_state | boolean | optional | If set to `true` it will color remaining time in the middle of card with current state color from gradient |
| empty_bar_color | string | optional | Color for timer arcs which are inactive, by default they gave opacity of 0, therefore they are not visible |
| secondary_info_size | string or int | optional | CSS size for secondary info (Friendly name of timer). Default: "50%" |
| layout | string | optional | Layout mode for the card. Available options are "circle" or "minimal". Minimal layout is supposed to create bar timer similar in style to Mushroom cards. Default: "circle" |
| Option | Type | Requirement | Default | Description |
|---|---|---|---|---|
| entity | string | **required** | - | Timer entity ID |
| bins | int | optional | 36 | Number of bins (arcs). |
| pad_angle | float | optional | 1 | Seperating angle between each individual arc in degrees. |
| corner_radius | float | optional | 4 | Radius for rounded arc corners. |
| color | array of strings | optional | | Color array used for filling remaining timer arc units. If array contains only single value, that color will be used to fill the whole timer, otherwise linear gradient will be created. |
| color_state | boolean | optional | | If set to `true` it will color remaining time in the middle of card with current state color from gradient. |
| empty_bar_color | string | optional | | Color for timer arcs which are inactive, by default they gave opacity of 0, therefore they are not visible. |
| secondary_info_size | string or int | optional | 50% for cicle or 80% for minimal | CSS size for secondary info (Friendly name of timer). |
| layout | string | optional | circle | Layout mode for the card. Available options are "circle" or "minimal". Minimal layout is supposed to create bar timer similar in style to Mushroom cards. |
| name | string | optional | Entity friendly name | Name to be displayed. Can be a string or "none". |
| icon | string | optional | Etity Icon | Icon to be displayed in minimal layout. Can be a standard icon (e.g. "mdi:motion-sensor") or "none". |
| direction | string | optional | countdown | Direction of the timer. Can be "countdown" or "countup". |
| primary_info | string | optional | name | Primary information to be displayed. Can be a "none", "name" or "timer". |
| secondary_info | string | optional | timer | Secondary information to be displayed. Can be a "none", "name" or "timer". |
| tap_action | string | optional | toggle | Action triggered on a single tap. Can be a "none", "toggle", "more_info" or "cancel". |
| hold_action | string | optional | more_info | Action triggered on holding. Can be a "none", "toggle", "more_info" or "cancel". |
| double_tap_action | string | optional | cancel | Action triggered on a double tap. Can be a "none", "toggle", "more_info" or "cancel". |

## Example

Expand Down
194 changes: 160 additions & 34 deletions circular-timer-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,17 @@ class CircularTimerCard extends LitElement {
this._defaultTimerFill = getComputedStyle(document.documentElement).getPropertyValue('--primary-color');
this._gradientColors = [this._defaultTimerFill, this._defaultTimerFill];
this._defaultTimerEmptyFill = "#fdfdfd00";
this._secondaryInfoSize = "50%";
this._secondaryInfoSize;
this._layout = "circle";

this._name = "use_entity_friendly_name";
this._icon = "use_entity_icon";
this._primaryInfo = "name";
this._secondaryInfo = "timer";
this._direction = "countdown";
this._tapAction = "toggle";
this._holdAction = "more_info";
this._doubleTapAction = "cancel";

this._colorState = false;
this._stateColor = getComputedStyle(document.documentElement).getPropertyValue("--primary-text-color");
Expand All @@ -29,6 +38,8 @@ class CircularTimerCard extends LitElement {
this.addEventListener("click", this._tap);

this._mouseIsDown = false;
this._mouseIsDownTriggered = false;
this._doubleClickTriggered = false;
this.addEventListener("mousedown", this._mousedown);
this.addEventListener("touchstart", this._mousedown);
this.addEventListener("mouseup", this._mouseup);
Expand Down Expand Up @@ -103,8 +114,46 @@ class CircularTimerCard extends LitElement {

if (config.secondary_info_size) {
this._secondaryInfoSize = config.secondary_info_size;
} else {
if (config.layout === "minimal") {
this._secondaryInfoSize = "80%";
} else {
this._secondaryInfoSize = "50%";
}
}

if (config.name) {
this._name = config.name;
}

if (config.icon) {
this._icon = config.icon;
}

if (config.primary_info) {
this._primaryInfo = config.primary_info;
}

if (config.secondary_info) {
this._secondaryInfo = config.secondary_info;
}

if (config.direction) {
this._direction = config.direction;
}

if (config.tap_action) {
this._tapAction = config.tap_action;
}

if (config.hold_action) {
this._holdAction = config.hold_action;
}

if (config.double_tap_action) {
this._doubleTapAction = config.double_tap_action;
}

this._colorScale = d3.scaleSequential(d3.interpolateRgbBasis(this._gradientColors));
this._arc = d3.arc()
.innerRadius(30)
Expand All @@ -131,15 +180,38 @@ class CircularTimerCard extends LitElement {
return html` <ha-card>Unknown entity: ${this._config.entity}</ha-card> `;
}

if (this._name == "use_entity_friendly_name") {
this._name = this._stateObj.attributes.friendly_name;
}

var icon;
var icon_style;
if (this._icon == "use_entity_icon") {
icon = this._stateObj.attributes.icon;
} else if (this._icon == "none") {
icon = "";
icon_style = "display:none;";
} else {
icon = this._icon;
}

var a = this._stateObj.attributes.duration.split(':');
var d_sec = (+a[0]) * 60 * 60 + (+a[1]) * 60 + (+a[2]);
var rem_sec;
if (this._stateObj.state == "active") {
rem_sec = ((Date.parse(this._stateObj.attributes.finishes_at) - new Date()) / 1000);
if (this._direction == "countup") {
rem_sec = d_sec - ((Date.parse(this._stateObj.attributes.finishes_at) - new Date()) / 1000);
} else {
rem_sec = ((Date.parse(this._stateObj.attributes.finishes_at) - new Date()) / 1000);
}
} else {
if (this._stateObj.state == "paused") {
var a1 = this._stateObj.attributes.remaining.split(':');
rem_sec = (+a1[0]) * 60 * 60 + (+a1[1]) * 60 + (+a1[2]);
if (this._direction == "countup") {
rem_sec = d_sec - ((+a1[0]) * 60 * 60 + (+a1[1]) * 60 + (+a1[2]));
} else {
rem_sec = (+a1[0]) * 60 * 60 + (+a1[1]) * 60 + (+a1[2]);
}
} else {
rem_sec = d_sec;
}
Expand All @@ -149,18 +221,38 @@ class CircularTimerCard extends LitElement {
var limitBin = Math.floor(this._bins * proc);
var colorData = this._generateArcColorData(limitBin);
var textColor = this._getTextColor(proc);

var display_rem_sec = this._getTimeString(rem_sec);

var primary_info;
if (this._primaryInfo == "none") {
primary_info = '';
} else if (this._primaryInfo == "timer") {
primary_info = display_rem_sec;
} else {
primary_info = this._name;
}

var secondary_info;
if (this._secondaryInfo == "none") {
secondary_info = '';
} else if (this._secondaryInfo == "name") {
secondary_info = this._name;
} else {
secondary_info = display_rem_sec;
}

if (this._layout === "minimal") {

return html`
<ha-card>
<div class="header">
<div class="icon">
<ha-icon icon="${this._stateObj.attributes.icon}" style="${this._colorState ? `color: ${textColor};"` : `""`};"></ha-icon>
</div>
<div class="icon" style="${icon_style}">
<ha-icon icon="${icon}" style="${this._colorState ? `color: ${textColor};"` : `""`};"></ha-icon>
</div>
<div class="info">
<span class="primary">${this._stateObj.attributes.friendly_name}</span>
<span class="secondary">${true ? this._getTimeString(rem_sec) : `${this._stateObj.state} | ${this._getTimeString(rem_sec)}`}</span>
<span class="primary">${primary_info}</span>
<span class="secondary" style="font-size:${this._secondaryInfoSize};">${secondary_info}</span>
</div>
</div>
<svg viewBox="0 0 100 10.2">
Expand Down Expand Up @@ -188,10 +280,10 @@ class CircularTimerCard extends LitElement {
)}
</g>
<g transform="translate(50,50)">
<text id="countdown" text-anchor="middle" dominant-baseline="central" fill=${textColor}>${this._getTimeString(rem_sec)}</text>
<text id="countdown" text-anchor="middle" dominant-baseline="central" fill=${textColor}>${secondary_info}</text>
</g>
<g transform="translate(50,62)">
<text id="timer-name" text-anchor="middle" dominant-baseline="central" fill="var(--secondary-text-color)" style="font-size:${this._secondaryInfoSize};">${this._stateObj.attributes.friendly_name}</text>
<text id="timer-name" text-anchor="middle" dominant-baseline="central" fill="var(--secondary-text-color)" style="font-size:${this._secondaryInfoSize};">${primary_info}</text>
</g>
</svg>
</ha-card>
Expand Down Expand Up @@ -266,42 +358,80 @@ class CircularTimerCard extends LitElement {
return `${hours}:${minutes}:${seconds}`
}

_tap(e) {

_toggle_func() {
const stateObj = this.hass.states[this._config.entity];
const service = stateObj.state === "active" ? "pause" : "start";

const service = stateObj.state === "active" ? "pause" : "start";
this.hass.callService("timer", service, { entity_id: this._config.entity });
}

_double_tap(e) {

_cancel_func() {
const stateObj = this.hass.states[this._config.entity];
this.hass.callService("timer", "cancel", { entity_id: this._config.entity });
}

_moreInfo_func() {
var event = new Event("hass-action", {
bubbles: true,
composed: true,
});
event.detail = {
config: this._actionConfig,
action: "hold",
};
this.dispatchEvent(event);
}

_tap(e) {
if(this._mouseIsDownTriggered == false) {
setTimeout(() => {
if (this._doubleClickTriggered == false) {
if (this._tapAction == "toggle") {
this._toggle_func();
} else if (this._tapAction == "more_info") {
this._moreInfo_func();
} else if (this._tapAction == "cancel") {
this._cancel_func();
}
}
}, 200);
}
}

_mousedown(e) {
_double_tap(e) {
this._doubleClickTriggered = true;
if (this._doubleTapAction == "toggle") {
this._toggle_func();
} else if (this._doubleTapAction == "more_info") {
this._moreInfo_func();
} else if (this._doubleTapAction == "cancel") {
this._cancel_func();
}
setTimeout(() => {
this._doubleClickTriggered = false;
}, 500);
}

_mousedown(e) {
this._mouseIsDown = true;
setTimeout(() => {

if(this._mouseIsDown) {
var event = new Event("hass-action", {
bubbles: true,
composed: true,
});
event.detail = {
config: this._actionConfig,
action: "hold",
};
this.dispatchEvent(event);
this._mouseIsDownTriggered = true;
if (this._holdAction == "toggle") {
this._toggle_func();
} else if (this._holdAction == "more_info") {
this._moreInfo_func();
} else if (this._holdAction == "cancel") {
this._cancel_func();
}
}
}, 1000);
}, 1000);
}

_mouseup(e) {
this._mouseIsDown = false;
setTimeout(() => {
this._mouseIsDown = false;
this._mouseIsDownTriggered = false;
}, 100);
}

static get styles() {
Expand Down Expand Up @@ -332,8 +462,6 @@ class CircularTimerCard extends LitElement {
font-size: 35%;
}
.header {
display: flex;
padding: 0px;
Expand Down Expand Up @@ -372,8 +500,6 @@ class CircularTimerCard extends LitElement {
.secondary {
color: var(--secondary-text-color);
font-size: 12px;
text-transform: capitalize;
}
Expand Down Expand Up @@ -406,4 +532,4 @@ customElements.define("circular-timer-card", CircularTimerCard);
console.info(
`%c circular-timer-card | Version 1.1 `,
"color: white; font-weight: bold; background: #FF4F00",
);
);

0 comments on commit eaf2521

Please sign in to comment.