Compare commits

...

1 Commits

Author SHA1 Message Date
Aidan Timson
1d974635ca Setup 2026-01-06 11:45:55 +00:00
4 changed files with 81 additions and 29 deletions

View File

@@ -2,6 +2,8 @@ import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
import { resolveTimeZone } from "../../../../common/datetime/resolve-time-zone";
import type { HomeAssistant } from "../../../../types";
import type { ClockCardConfig } from "../types";
@@ -26,6 +28,12 @@ function romanize12HourClock(num: number) {
return numerals[num];
}
const QUARTER_TICKS = [12, 3, 6, 9];
const ARRAY_QUARTER_TICKS = Array.from({ length: 4 }, (_, i) => i);
const ARRAY_HOUR_TICKS = Array.from({ length: 12 }, (_, i) => i);
const ARRAY_MINUTE_TICKS = Array.from({ length: 60 }, (_, i) => i);
@customElement("hui-clock-card-analog")
export class HuiClockCardAnalog extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@@ -113,23 +121,35 @@ export class HuiClockCardAnalog extends LitElement {
this._hourOffsetSec = hour12 * 3600 + minute * 60 + secondsWithMs;
}
private _computeFaceStyle = memoizeOne((config: ClockCardConfig) => {
const faceStyleParts = config?.face_style?.split("_");
return {
sizeClass: config?.clock_size ? `size-${config.clock_size}` : "",
isFaceNumbers: faceStyleParts?.includes("numbers"),
isFaceRoman: faceStyleParts?.includes("roman"),
isFaceUpright: faceStyleParts?.includes("upright"),
isFaceQuarter: faceStyleParts?.includes("quarter"),
};
});
render() {
if (!this.config) return nothing;
const sizeClass = this.config.clock_size
? `size-${this.config.clock_size}`
: "";
const isNumbers = this.config?.face_style?.startsWith("numbers");
const isRoman = this.config?.face_style?.startsWith("roman");
const isUpright = this.config?.face_style?.endsWith("upright");
const {
sizeClass,
isFaceNumbers,
isFaceRoman,
isFaceUpright,
isFaceQuarter,
} = this._computeFaceStyle(this.config);
const indicator = (number?: number) => html`
<div
class=${classMap({
line: true,
numbers: isNumbers,
roman: isRoman,
numbers: isFaceNumbers,
roman: isFaceRoman,
quarter: isFaceQuarter,
})}
>
${number && this.config?.face_style !== "markers"
@@ -137,12 +157,12 @@ export class HuiClockCardAnalog extends LitElement {
class=${classMap({
number: true,
[this.config?.clock_size ?? ""]: true,
upright: isUpright,
upright: isFaceUpright,
})}
>
${isRoman
${isFaceRoman
? romanize12HourClock(number)
: isNumbers
: isFaceNumbers
? number
: nothing}
</div>`
@@ -163,43 +183,52 @@ export class HuiClockCardAnalog extends LitElement {
})}
>
${this.config.ticks === "quarter"
? Array.from({ length: 4 }, (_, i) => i).map(
? ARRAY_QUARTER_TICKS.map(
(i) =>
// 4 ticks (12, 3, 6, 9) at 0°, 90°, 180°, 270°
html`
<div
aria-hidden="true"
class="tick hour"
style=${`--tick-rotation: ${i * 90}deg;`}
style=${styleMap({ "--tick-rotation": `${i * 90}deg` })}
>
${indicator([12, 3, 6, 9][i])}
${indicator(QUARTER_TICKS[i])}
</div>
`
)
: !this.config.ticks || // Default to hour ticks
this.config.ticks === "hour"
? Array.from({ length: 12 }, (_, i) => i).map(
? ARRAY_HOUR_TICKS.map(
(i) =>
// 12 ticks (1-12)
html`
<div
aria-hidden="true"
class="tick hour"
style=${`--tick-rotation: ${i * 30}deg;`}
style=${styleMap({ "--tick-rotation": `${i * 30}deg` })}
>
${indicator(((i + 11) % 12) + 1)}
${isFaceQuarter
? QUARTER_TICKS.includes(((i + 11) % 12) + 1)
? indicator(((i + 11) % 12) + 1)
: nothing
: indicator(((i + 11) % 12) + 1)}
</div>
`
)
: this.config.ticks === "minute"
? Array.from({ length: 60 }, (_, i) => i).map(
? ARRAY_MINUTE_TICKS.map(
(i) =>
// 60 ticks (1-60)
html`
<div
aria-hidden="true"
class="tick ${i % 5 === 0 ? "hour" : "minute"}"
style=${`--tick-rotation: ${i * 6}deg;`}
class=${classMap({
tick: true,
[i % 5 === 0 ? "hour" : "minute"]: true,
})}
style=${styleMap({
"--tick-rotation": `${i * 6}deg`,
})}
>
${i % 5 === 0
? indicator(((i / 5 + 11) % 12) + 1)
@@ -211,11 +240,15 @@ export class HuiClockCardAnalog extends LitElement {
<div class="center-dot"></div>
<div
class="hand hour"
style=${`animation-delay: -${this._hourOffsetSec ?? 0}s;`}
style=${styleMap({
"animation-delay": `-${this._hourOffsetSec ?? 0}s`,
})}
></div>
<div
class="hand minute"
style=${`animation-delay: -${this._minuteOffsetSec ?? 0}s;`}
style=${styleMap({
"animation-delay": `-${this._minuteOffsetSec ?? 0}s`,
})}
></div>
${this.config.show_seconds
? html`<div
@@ -224,11 +257,13 @@ export class HuiClockCardAnalog extends LitElement {
second: true,
step: this.config.seconds_motion === "tick",
})}
style=${`animation-delay: -${
(this.config.seconds_motion === "tick"
? Math.floor(this._secondOffsetSec ?? 0)
: (this._secondOffsetSec ?? 0)) as number
}s;`}
style=${styleMap({
"animation-delay": `-${
(this.config.seconds_motion === "tick"
? Math.floor(this._secondOffsetSec ?? 0)
: (this._secondOffsetSec ?? 0)) as number
}s`,
})}
></div>`
: nothing}
</div>

View File

@@ -420,7 +420,12 @@ export interface ClockCardConfig extends LovelaceCardConfig {
// Analog clock options
border?: boolean;
ticks?: "none" | "quarter" | "hour" | "minute";
face_style?: "markers" | "numbers_upright" | "roman";
face_style?:
| "markers"
| "numbers_upright"
| "numbers_upright_quarter"
| "roman"
| "roman_quarter";
}
export interface MediaControlCardConfig extends LovelaceCardConfig {

View File

@@ -63,7 +63,9 @@ const cardConfigStruct = assign(
union([
literal("markers"),
literal("numbers_upright"),
literal("numbers_upright_quarter"),
literal("roman"),
literal("roman_quarter"),
]),
literal("markers")
)
@@ -220,7 +222,9 @@ export class HuiClockCardEditor
options: [
"markers",
"numbers_upright",
"numbers_upright_quarter",
"roman",
"roman_quarter",
].map((value) => ({
value,
label: localize(

View File

@@ -8271,9 +8271,17 @@
"label": "Numbers",
"description": "Show numbers around the clock face"
},
"numbers_upright_quarter": {
"label": "Numbers upright quarter",
"description": "Show numbers around the clock with quarter marks"
},
"roman": {
"label": "Roman numerals",
"description": "Show Roman numerals (I-XII) around the clock face"
},
"roman_quarter": {
"label": "Roman numerals quarter",
"description": "Show Roman numerals (I-XII) around the clock with quarter marks"
}
}
},