Skip to content

Commit

Permalink
Add TimerDuration interface and ts-lit-plugin dependency, update Time…
Browse files Browse the repository at this point in the history
…r Card configuration
  • Loading branch information
YR-ZR0 committed Feb 13, 2024
1 parent 7d015d4 commit 5448364
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 69 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ to configure the card add the following to your dashboard
## TODO
- [ ] Better styling
- [ ] Use the entity picker to select your timer entity (Couldn't get the Home Assistant examples to work)
- [x] Use the entity picker to select your timer entity
- [x] Fix editor issues with state not updating
- [ ] Handle user pressing enter to accept the dialog
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"devDependencies": {
"@parcel/transformer-inline-string": "^2.8.3",
"parcel": "^2.8.3",
"ts-lit-plugin": "^2.0.2",
"typescript": "^5.3.3"
},
"dependencies": {
Expand Down
169 changes: 110 additions & 59 deletions src/card.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import {
html,
CSSResultGroup,
LitElement,
TemplateResult,
nothing,
PropertyValues,
CSSResultGroup,
css,
html,
nothing,
} from "lit";
import { state, property } from "lit/decorators.js";
import { property, state } from "lit/decorators.js";

import { HomeAssistant, LovelaceCardConfig } from "custom-card-helpers";
import { HassEntity } from "home-assistant-js-websocket";
Expand All @@ -22,9 +21,11 @@ export class TimerCard extends LitElement {
@state() private _entity: string;
@state() private _name: string;
@state() private _state: HassEntity;
@property({ type: Number }) hours = 0;
@property({ type: Number }) minutes = 0;
@property({ type: Number }) seconds = 0;
@property({ type: Object }) timerDuration: TimerDuration = {
hours: 0,
minutes: 0,
seconds: 0,
};
@property({ type: Boolean }) isTimerActive = false;
@property({ type: String }) remainingTime = "";

Expand All @@ -47,17 +48,19 @@ export class TimerCard extends LitElement {
this._name = fn ? fn : this._entity;
}
}
handleInput(e) {
const inputId = e.target.id;
const inputValue = e.target.value;
this[inputId] = inputValue;

updateTime(unit: "hours" | "minutes" | "seconds", value: string) {
this.timerDuration[unit] = parseInt(value, 10);
}

updateTimerDuration() {
const newDuration = `${String(this.hours).padStart(2, "0")}:${String(
this.minutes
).padStart(2, "0")}:${String(this.seconds).padStart(2, "0")}`;

const newDuration = `${this.timerDuration.hours
.toString()
.padStart(2, "0")}:${this.timerDuration.minutes
.toString()
.padStart(2, "0")}:${this.timerDuration.seconds
.toString()
.padStart(2, "0")}`;
const serviceData = {
entity_id: this._entity,
duration: newDuration,
Expand All @@ -72,37 +75,62 @@ export class TimerCard extends LitElement {
content = html` entity: ${this._entity} not available <br />`;
} else {
content = html`
<div>Timer: ${this._name}</div>
<div class="horizontal layout">
<div class="input-container">
<input
id="hours"
type="number"
.value=${this.hours}
@input=${this.handleInput}
/>
</div>
<div class="input-container">
<input
id="minutes"
type="number"
.value=${this.minutes}
@input=${this.handleInput}
/>
</div>
<div class="input-container">
<input
id="seconds"
type="number"
.value=${this.seconds}
@input=${this.handleInput}
/>
<div class="card-content">
<div class="timer-duration">
<div>Timer: ${this._name}</div>
${this.isTimerActive
? html`<div>Remaining: ${this.remainingTime}</div>`
: ""}
<form id="time-inputs">
<div>
<label>Hours</label>
<input
type="number"
min="00"
.value=${this.timerDuration.hours}
@input=${(e: Event) =>
this.updateTime(
"hours",
(e.target as HTMLInputElement).value
)}
/>
</div>
<span>:</span>
<div>
<label>Minutes</label>
<input
type="number"
min="00"
max="59"
.value=${this.timerDuration.minutes}
@input=${(e: Event) =>
this.updateTime(
"minutes",
(e.target as HTMLInputElement).value
)}
/>
</div>
<span>:</span>
<div>
<label>Seconds</label>
<input
type="number"
min="00"
max="59"
.value=${this.timerDuration.seconds}
@input=${(e: Event) =>
this.updateTime(
"seconds",
(e.target as HTMLInputElement).value
)}
/>
</div>
</form>
<div class="timer-controls">
<mwc-button @click=${this.updateTimerDuration}>Start</mwc-button>
</div>
</div>
</div>
${this.isTimerActive
? html`<div>Remaining Time: ${this.remainingTime}</div>`
: ""}
<button @click=${this.updateTimerDuration}>Apply</button>
`;
}
return html`
Expand Down Expand Up @@ -169,31 +197,54 @@ export class TimerCard extends LitElement {
static get styles(): CSSResultGroup {
return [
css`
.timer-duration {
align-items: center;
display: flex;
justify-content: space-between;
}
#time-inputs {
align-items: center;
display: flex;
justify-content: space-between;
}
#time-inputs > div {
align-items: center;
display: flex;
flex-direction: column-reverse;
margin: 0 10px;
}
.timer-controls mwc-button {
--mdc-theme-primary: white; /* This will change the text color */
--mdc-theme-on-primary: green; /* This will change the background color */
}
input {
width: 60px;
padding: 4px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
text-align: center;
}
button {
margin-top: 5px;
padding: 8px;
background-color: #4caf50;
color: white;
#time-inputs > div > input {
border: none;
border-radius: 4px;
cursor: pointer;
border-bottom: 1px solid black;
}
.layout.horizontal,
.layout.vertical {
display: flex;
/* Hide arrows on number inputs for Chrome, Safari, Edge, Opera */
#time-inputs > div > input[type="number"]::-webkit-inner-spin-button,
#time-inputs > div > input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
.layout.horizontal {
flex-direction: row;
/* Hide arrows on number inputs for Firefox */
#time-inputs > div > input[type="number"] {
-moz-appearance: textfield;
}
button:hover {
Expand All @@ -202,9 +253,9 @@ export class TimerCard extends LitElement {
`,
];
}

//TODO: make this dynamic
getCardSize() {
return 3;
return 4;
}

static getConfigElement() {
Expand Down
18 changes: 10 additions & 8 deletions src/editor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { HomeAssistant } from "custom-card-helpers";
import { css, html, LitElement } from "lit";
import { html, LitElement } from "lit";
import { property } from "lit/decorators";
import { state } from "lit/decorators/state.js";

Expand All @@ -26,27 +26,29 @@ export class TimerCardEditor extends LitElement {
></ha-textfield>
</div>
<div>
<ha-textfield
<ha-selector
id="entity"
label="Entity"
.configValue=${"entity"}
.hass=${this.hass}
.selector=${{ entity: { domain: "timer" } }}
.configValue="${"entity"}"
.value="${this._config.entity}"
@change="${this.handleChangedEvent}"
></ha-textfield>
.required="${true}"
@value-changed="${this.handleChangedEvent}"
></ha-selector>
</div>
</div>
`;
}

handleChangedEvent(changedEvent: Event) {
handleChangedEvent(changedEvent: CustomEvent) {
const target = changedEvent.target as HTMLInputElement;
// this._config is readonly, copy needed
const newConfig = Object.assign({}, this._config);
console.log(newConfig);
if (target.id == "header") {
newConfig.header = target.value;
} else if (target.id == "entity") {
newConfig.entity = target.value;
newConfig.entity = changedEvent.detail.value;
}
const messageEvent = new CustomEvent("config-changed", {
detail: { config: newConfig },
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ window.customCards = window.customCards || [];
window.customCards.push({
type: "timer-card",
name: "Timer Card",
configurable: true,
description: "A simple card to control a timer entity",
});
5 changes: 5 additions & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
interface TimerDuration {
hours: number;
minutes: number;
seconds: number;
}
32 changes: 31 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,37 @@
"strictNullChecks": true,
"strictFunctionTypes": true,
"experimentalDecorators": true,
"useDefineForClassFields": false
"useDefineForClassFields": false,
"plugins": [
{
"name": "ts-lit-plugin",
"strict": false,
// These tags are provided by the HA frontend and [almost all] cannot be
// imported by a custom card directly.
"globalTags": [
"ha-card",
"ha-combo-box",
"ha-icon",
"ha-icon-button",
"ha-button-menu",
"ha-circular-progress",
"ha-selector",
"ha-svg-icon",
"mwc-button",
"mwc-list-item",
"paper-toast"
],
"rules": {
"no-unknown-tag-name": "error",
"no-missing-import": "error",
"no-unclosed-tag": "error",
"no-incompatible-type-binding": "warning",
"no-invalid-css": "warning",
"no-missing-element-type-definition": "warning",
"no-property-visibility-mismatch": "error"
}
}
]
},
"exclude": [
"node_modules",
Expand Down

0 comments on commit 5448364

Please sign in to comment.