1
0
mirror of https://github.com/home-assistant/frontend.git synced 2026-02-15 07:25:54 +00:00

Analog style for clock card (#26557)

Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Aidan Timson
2025-08-27 10:15:06 +01:00
committed by GitHub
parent 2de605d97a
commit edaaa00038
6 changed files with 739 additions and 205 deletions

View File

@@ -0,0 +1,300 @@
import { css, html, LitElement, nothing } from "lit";
import type { PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import type { ClockCardConfig } from "../types";
import type { HomeAssistant } from "../../../../types";
import { INTERVAL } from "../hui-clock-card";
import { resolveTimeZone } from "../../../../common/datetime/resolve-time-zone";
@customElement("hui-clock-card-analog")
export class HuiClockCardAnalog extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public config?: ClockCardConfig;
@state() private _dateTimeFormat?: Intl.DateTimeFormat;
@state() private _hourDeg?: number;
@state() private _minuteDeg?: number;
@state() private _secondDeg?: number;
private _tickInterval?: undefined | number;
private _initDate() {
if (!this.config || !this.hass) {
return;
}
let locale = this.hass.locale;
if (this.config.time_format) {
locale = { ...locale, time_format: this.config.time_format };
}
this._dateTimeFormat = new Intl.DateTimeFormat(this.hass.locale.language, {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hourCycle: "h12",
timeZone:
this.config.time_zone ||
resolveTimeZone(locale.time_zone, this.hass.config?.time_zone),
});
this._tick();
}
protected updated(changedProps: PropertyValues) {
if (changedProps.has("hass")) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.locale !== this.hass?.locale) {
this._initDate();
}
}
}
public connectedCallback() {
super.connectedCallback();
this._startTick();
}
public disconnectedCallback() {
super.disconnectedCallback();
this._stopTick();
}
private _startTick() {
this._tickInterval = window.setInterval(() => this._tick(), INTERVAL);
this._tick();
}
private _stopTick() {
if (this._tickInterval) {
clearInterval(this._tickInterval);
this._tickInterval = undefined;
}
}
private _tick() {
if (!this._dateTimeFormat) return;
const parts = this._dateTimeFormat.formatToParts();
const hourStr = parts.find((p) => p.type === "hour")?.value;
const minuteStr = parts.find((p) => p.type === "minute")?.value;
const secondStr = parts.find((p) => p.type === "second")?.value;
const hour = hourStr ? parseInt(hourStr, 10) : 0;
const minute = minuteStr ? parseInt(minuteStr, 10) : 0;
const second = secondStr ? parseInt(secondStr, 10) : 0;
this._hourDeg = hour * 30 + minute * 0.5; // 30deg per hour + 0.5deg per minute
this._minuteDeg = minute * 6 + second * 0.1; // 6deg per minute + 0.1deg per second
this._secondDeg = this.config?.show_seconds ? second * 6 : undefined; // 6deg per second
}
render() {
if (!this.config) return nothing;
const sizeClass = this.config.clock_size
? `size-${this.config.clock_size}`
: "";
return html`
<div
class="analog-clock ${sizeClass}"
role="img"
aria-label="Analog clock"
>
<div
class=${classMap({
dial: true,
"dial-border": this.config.border ?? false,
})}
>
${this.config.ticks === "quarter"
? Array.from({ length: 4 }, (_, i) => i).map(
(i) =>
// 4 ticks
html`
<div
aria-hidden
class="tick hour"
style=${`--tick-rotation: ${i * 90}deg;`}
>
<div class="line"></div>
</div>
`
)
: !this.config.ticks || // Default to hour ticks
this.config.ticks === "hour"
? Array.from({ length: 12 }, (_, i) => i).map(
(i) =>
// 12 ticks
html`
<div
aria-hidden
class="tick hour"
style=${`--tick-rotation: ${i * 30}deg;`}
>
<div class="line"></div>
</div>
`
)
: this.config.ticks === "minute"
? Array.from({ length: 60 }, (_, i) => i).map(
(i) =>
// 60 ticks
html`
<div
aria-hidden
class="tick ${i % 5 === 0 ? "hour" : "minute"}"
style=${`--tick-rotation: ${i * 6}deg;`}
>
<div class="line"></div>
</div>
`
)
: nothing}
<div class="center-dot"></div>
<div
class="hand hour"
style=${`--hand-rotation: ${this._hourDeg ?? 0}deg;`}
></div>
<div
class="hand minute"
style=${`--hand-rotation: ${this._minuteDeg ?? 0}deg;`}
></div>
${this.config.show_seconds
? html`<div
class="hand second"
style=${`--hand-rotation: ${this._secondDeg ?? 0}deg;`}
></div>`
: nothing}
</div>
</div>
`;
}
static styles = css`
:host {
display: flex;
align-items: center;
justify-content: center;
}
.analog-clock {
--clock-size: 100px;
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--clock-size);
height: var(--clock-size);
}
.analog-clock.size-medium {
--clock-size: 160px;
}
.analog-clock.size-large {
--clock-size: 220px;
}
.dial {
position: relative;
width: 100%;
height: 100%;
box-sizing: border-box;
}
.dial-border {
border: 2px solid var(--divider-color);
border-radius: 50%;
}
.tick {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: rotate(var(--tick-rotation));
pointer-events: none;
z-index: 0;
}
.tick .line {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 1px;
height: calc(var(--clock-size) * 0.04);
background: var(--primary-text-color);
opacity: 0.5;
border-radius: 1px;
}
.tick.hour .line {
width: 2px;
height: calc(var(--clock-size) * 0.07);
opacity: 0.8;
}
.center-dot {
position: absolute;
top: 50%;
left: 50%;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--primary-text-color);
transform: translate(-50%, -50%);
z-index: 3;
}
.hand {
position: absolute;
left: 50%;
bottom: 50%;
transform-origin: 50% 100%;
transform: translate(-50%, 0) rotate(var(--hand-rotation, 0deg));
background: var(--primary-text-color);
border-radius: 2px;
will-change: transform;
}
.hand.hour {
width: 4px;
height: calc(var(--clock-size) * 0.25); /* 25% of the clock size */
background: var(--primary-text-color);
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.2);
z-index: 1;
}
.hand.minute {
width: 3px;
height: calc(var(--clock-size) * 0.35); /* 35% of the clock size */
background: var(--primary-text-color);
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.2);
opacity: 0.9;
z-index: 3;
}
.hand.second {
width: 2px;
height: calc(var(--clock-size) * 0.42); /* 42% of the clock size */
background: var(--ha-color-border-danger-normal);
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2);
opacity: 0.8;
z-index: 2;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"hui-clock-card-analog": HuiClockCardAnalog;
}
}

View File

@@ -0,0 +1,196 @@
import { css, html, LitElement, nothing } from "lit";
import type { PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import type { ClockCardConfig } from "../types";
import type { HomeAssistant } from "../../../../types";
import { INTERVAL } from "../hui-clock-card";
import { useAmPm } from "../../../../common/datetime/use_am_pm";
import { resolveTimeZone } from "../../../../common/datetime/resolve-time-zone";
@customElement("hui-clock-card-digital")
export class HuiClockCardDigital extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public config?: ClockCardConfig;
@state() private _dateTimeFormat?: Intl.DateTimeFormat;
@state() private _timeHour?: string;
@state() private _timeMinute?: string;
@state() private _timeSecond?: string;
@state() private _timeAmPm?: string;
private _tickInterval?: undefined | number;
private _initDate() {
if (!this.config || !this.hass) {
return;
}
let locale = this.hass?.locale;
if (this.config?.time_format) {
locale = { ...locale, time_format: this.config.time_format };
}
this._dateTimeFormat = new Intl.DateTimeFormat(this.hass.locale.language, {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hourCycle: useAmPm(locale) ? "h12" : "h23",
timeZone:
this.config?.time_zone ||
resolveTimeZone(locale.time_zone, this.hass.config?.time_zone),
});
this._tick();
}
protected updated(changedProps: PropertyValues) {
if (changedProps.has("hass")) {
const oldHass = changedProps.get("hass");
if (!oldHass || oldHass.locale !== this.hass?.locale) {
this._initDate();
}
}
}
public connectedCallback() {
super.connectedCallback();
this._startTick();
}
public disconnectedCallback() {
super.disconnectedCallback();
this._stopTick();
}
private _startTick() {
this._tickInterval = window.setInterval(() => this._tick(), INTERVAL);
this._tick();
}
private _stopTick() {
if (this._tickInterval) {
clearInterval(this._tickInterval);
this._tickInterval = undefined;
}
}
private _tick() {
if (!this._dateTimeFormat) return;
const parts = this._dateTimeFormat.formatToParts();
this._timeHour = parts.find((part) => part.type === "hour")?.value;
this._timeMinute = parts.find((part) => part.type === "minute")?.value;
this._timeSecond = this.config?.show_seconds
? parts.find((part) => part.type === "second")?.value
: undefined;
this._timeAmPm = parts.find((part) => part.type === "dayPeriod")?.value;
}
render() {
if (!this.config) return nothing;
const sizeClass = this.config.clock_size
? `size-${this.config.clock_size}`
: "";
return html`
<div class="time-parts ${sizeClass}">
<div class="time-part hour">${this._timeHour}</div>
<div class="time-part minute">${this._timeMinute}</div>
${this._timeSecond !== undefined
? html`<div class="time-part second">${this._timeSecond}</div>`
: nothing}
${this._timeAmPm !== undefined
? html`<div class="time-part am-pm">${this._timeAmPm}</div>`
: nothing}
</div>
`;
}
static styles = css`
:host {
display: block;
}
.time-parts {
align-items: center;
display: grid;
grid-template-areas:
"hour minute second"
"hour minute am-pm";
font-size: 1.5rem;
font-weight: var(--ha-font-weight-medium);
line-height: 0.8;
direction: ltr;
}
.time-title + .time-parts {
font-size: 1.5rem;
}
.time-parts.size-medium {
font-size: 3rem;
}
.time-parts.size-large {
font-size: 4rem;
}
.time-parts.size-medium .time-part.second,
.time-parts.size-medium .time-part.am-pm {
font-size: var(--ha-font-size-l);
margin-left: 6px;
}
.time-parts.size-large .time-part.second,
.time-parts.size-large .time-part.am-pm {
font-size: var(--ha-font-size-2xl);
margin-left: 8px;
}
.time-parts .time-part.hour {
grid-area: hour;
}
.time-parts .time-part.minute {
grid-area: minute;
}
.time-parts .time-part.second {
grid-area: second;
line-height: 0.9;
opacity: 0.4;
}
.time-parts .time-part.am-pm {
grid-area: am-pm;
line-height: 0.9;
opacity: 0.6;
}
.time-parts .time-part.second,
.time-parts .time-part.am-pm {
font-size: var(--ha-font-size-xs);
margin-left: 4px;
}
.time-parts .time-part.hour:after {
content: ":";
margin: 0 2px;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"hui-clock-card-digital": HuiClockCardDigital;
}
}

View File

@@ -1,4 +1,3 @@
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";
@@ -11,10 +10,8 @@ import type {
LovelaceGridOptions,
} from "../types";
import type { ClockCardConfig } from "./types";
import { useAmPm } from "../../../common/datetime/use_am_pm";
import { resolveTimeZone } from "../../../common/datetime/resolve-time-zone";
const INTERVAL = 1000;
export const INTERVAL = 1000;
@customElement("hui-clock-card")
export class HuiClockCard extends LitElement implements LovelaceCard {
@@ -33,45 +30,14 @@ export class HuiClockCard extends LitElement implements LovelaceCard {
@state() private _config?: ClockCardConfig;
@state() private _dateTimeFormat?: Intl.DateTimeFormat;
@state() private _timeHour?: string;
@state() private _timeMinute?: string;
@state() private _timeSecond?: string;
@state() private _timeAmPm?: string;
private _tickInterval?: undefined | number;
public setConfig(config: ClockCardConfig): void {
this._config = config;
this._initDate();
}
private _initDate() {
if (!this._config || !this.hass) {
return;
// Dynamically import the clock type based on the configuration
if (config.clock_style === "analog") {
import("./clock/hui-clock-card-analog");
} else {
import("./clock/hui-clock-card-digital");
}
let locale = this.hass?.locale;
if (this._config?.time_format) {
locale = { ...locale, time_format: this._config.time_format };
}
this._dateTimeFormat = new Intl.DateTimeFormat(this.hass.locale.language, {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hourCycle: useAmPm(locale) ? "h12" : "h23",
timeZone:
this._config?.time_zone ||
resolveTimeZone(locale.time_zone, this.hass.config?.time_zone),
});
this._tick();
}
public getCardSize(): number {
@@ -80,77 +46,59 @@ export class HuiClockCard extends LitElement implements LovelaceCard {
}
public getGridOptions(): LovelaceGridOptions {
if (this._config?.clock_size === "medium") {
return {
min_rows: this._config?.title ? 2 : 1,
rows: 2,
max_rows: 4,
min_columns: 4,
columns: 6,
};
switch (this._config?.clock_style) {
case "analog":
switch (this._config?.clock_size) {
case "medium":
return {
min_rows: this._config?.title ? 4 : 3,
rows: 3,
min_columns: 5,
columns: 6,
};
case "large":
return {
min_rows: this._config?.title ? 5 : 4,
rows: 4,
min_columns: 6,
columns: 6,
};
default:
return {
min_rows: this._config?.title ? 3 : 2,
rows: 2,
min_columns: 2,
columns: 6,
};
}
default:
switch (this._config?.clock_size) {
case "medium":
return {
min_rows: this._config?.title ? 2 : 1,
rows: 2,
max_rows: 4,
min_columns: 4,
columns: 6,
};
case "large":
return {
min_rows: 2,
rows: 2,
max_rows: 4,
min_columns: 6,
columns: 6,
};
default:
return {
min_rows: 1,
rows: 1,
max_rows: 4,
min_columns: 3,
columns: 6,
};
}
}
if (this._config?.clock_size === "large") {
return {
min_rows: 2,
rows: 2,
max_rows: 4,
min_columns: 6,
columns: 6,
};
}
return {
min_rows: 1,
rows: 1,
max_rows: 4,
min_columns: 3,
columns: 6,
};
}
protected updated(changedProps: PropertyValues) {
if (changedProps.has("hass")) {
const oldHass = changedProps.get("hass");
if (!oldHass || oldHass.locale !== this.hass?.locale) {
this._initDate();
}
}
}
public connectedCallback() {
super.connectedCallback();
this._startTick();
}
public disconnectedCallback() {
super.disconnectedCallback();
this._stopTick();
}
private _startTick() {
this._tickInterval = window.setInterval(() => this._tick(), INTERVAL);
this._tick();
}
private _stopTick() {
if (this._tickInterval) {
clearInterval(this._tickInterval);
this._tickInterval = undefined;
}
}
private _tick() {
if (!this._dateTimeFormat) return;
const parts = this._dateTimeFormat.formatToParts();
this._timeHour = parts.find((part) => part.type === "hour")?.value;
this._timeMinute = parts.find((part) => part.type === "minute")?.value;
this._timeSecond = this._config?.show_seconds
? parts.find((part) => part.type === "second")?.value
: undefined;
this._timeAmPm = parts.find((part) => part.type === "dayPeriod")?.value;
}
protected render() {
@@ -170,16 +118,19 @@ export class HuiClockCard extends LitElement implements LovelaceCard {
${this._config.title !== undefined
? html`<div class="time-title">${this._config.title}</div>`
: nothing}
<div class="time-parts">
<div class="time-part hour">${this._timeHour}</div>
<div class="time-part minute">${this._timeMinute}</div>
${this._timeSecond !== undefined
? html`<div class="time-part second">${this._timeSecond}</div>`
: nothing}
${this._timeAmPm !== undefined
? html`<div class="time-part am-pm">${this._timeAmPm}</div>`
: nothing}
</div>
${this._config.clock_style === "analog"
? html`
<hui-clock-card-analog
.hass=${this.hass}
.config=${this._config}
></hui-clock-card-analog>
`
: html`
<hui-clock-card-digital
.hass=${this.hass}
.config=${this._config}
></hui-clock-card-digital>
`}
</div>
</ha-card>
`;
@@ -234,74 +185,6 @@ export class HuiClockCard extends LitElement implements LovelaceCard {
font-size: var(--ha-font-size-2xl);
line-height: var(--ha-line-height-condensed);
}
.time-parts {
align-items: center;
display: grid;
grid-template-areas:
"hour minute second"
"hour minute am-pm";
font-size: 2rem;
font-weight: var(--ha-font-weight-medium);
line-height: 0.8;
direction: ltr;
}
.time-title + .time-parts {
font-size: 1.5rem;
}
.time-wrapper.size-medium .time-parts {
font-size: 3rem;
}
.time-wrapper.size-large .time-parts {
font-size: 4rem;
}
.time-wrapper.size-medium .time-parts .time-part.second,
.time-wrapper.size-medium .time-parts .time-part.am-pm {
font-size: var(--ha-font-size-l);
margin-left: 6px;
}
.time-wrapper.size-large .time-parts .time-part.second,
.time-wrapper.size-large .time-parts .time-part.am-pm {
font-size: var(--ha-font-size-2xl);
margin-left: 8px;
}
.time-parts .time-part.hour {
grid-area: hour;
}
.time-parts .time-part.minute {
grid-area: minute;
}
.time-parts .time-part.second {
grid-area: second;
line-height: 0.9;
opacity: 0.4;
}
.time-parts .time-part.am-pm {
grid-area: am-pm;
line-height: 0.9;
opacity: 0.6;
}
.time-parts .time-part.second,
.time-parts .time-part.am-pm {
font-size: var(--ha-font-size-xs);
margin-left: 4px;
}
.time-parts .time-part.hour:after {
content: ":";
margin: 0 2px;
}
`;
}

View File

@@ -371,11 +371,15 @@ export interface MarkdownCardConfig extends LovelaceCardConfig {
export interface ClockCardConfig extends LovelaceCardConfig {
type: "clock";
title?: string;
clock_style?: "digital" | "analog";
clock_size?: "small" | "medium" | "large";
show_seconds?: boolean | undefined;
time_format?: TimeFormat;
time_zone?: string;
no_background?: boolean;
// Analog clock options
border?: boolean;
ticks?: "none" | "quarter" | "hour" | "minute";
}
export interface MediaControlCardConfig extends LovelaceCardConfig {

View File

@@ -6,6 +6,7 @@ import {
assert,
assign,
boolean,
defaulted,
enums,
literal,
object,
@@ -30,6 +31,7 @@ const cardConfigStruct = assign(
baseLovelaceCardConfig,
object({
title: optional(string()),
clock_style: optional(union([literal("digital"), literal("analog")])),
clock_size: optional(
union([literal("small"), literal("medium"), literal("large")])
),
@@ -37,6 +39,19 @@ const cardConfigStruct = assign(
time_zone: optional(enums(Object.keys(timezones))),
show_seconds: optional(boolean()),
no_background: optional(boolean()),
// Analog clock options
border: optional(defaulted(boolean(), false)),
ticks: optional(
defaulted(
union([
literal("none"),
literal("quarter"),
literal("hour"),
literal("minute"),
]),
literal("hour")
)
),
})
);
@@ -50,9 +65,23 @@ export class HuiClockCardEditor
@state() private _config?: ClockCardConfig;
private _schema = memoizeOne(
(localize: LocalizeFunc) =>
(localize: LocalizeFunc, clockStyle: "digital" | "analog") =>
[
{ name: "title", selector: { text: {} } },
{
name: "clock_style",
selector: {
select: {
mode: "dropdown",
options: ["digital", "analog"].map((value) => ({
value,
label: localize(
`ui.panel.lovelace.editor.card.clock.clock_styles.${value}`
),
})),
},
},
},
{
name: "clock_size",
selector: {
@@ -69,20 +98,66 @@ export class HuiClockCardEditor
},
{ name: "show_seconds", selector: { boolean: {} } },
{ name: "no_background", selector: { boolean: {} } },
{
name: "time_format",
selector: {
select: {
mode: "dropdown",
options: ["auto", ...Object.values(TimeFormat)].map((value) => ({
value,
label: localize(
`ui.panel.lovelace.editor.card.clock.time_formats.${value}`
),
})),
},
},
},
...(clockStyle === "digital"
? ([
{
name: "time_format",
selector: {
select: {
mode: "dropdown",
options: ["auto", ...Object.values(TimeFormat)].map(
(value) => ({
value,
label: localize(
`ui.panel.lovelace.editor.card.clock.time_formats.${value}`
),
})
),
},
},
},
] as const satisfies readonly HaFormSchema[])
: clockStyle === "analog"
? ([
{
name: "border",
description: {
suffix: localize(
`ui.panel.lovelace.editor.card.clock.border.description`
),
},
default: false,
selector: {
boolean: {},
},
},
{
name: "ticks",
description: {
suffix: localize(
`ui.panel.lovelace.editor.card.clock.ticks.description`
),
},
default: "hour",
selector: {
select: {
mode: "dropdown",
options: ["none", "quarter", "hour", "minute"].map(
(value) => ({
value,
label: localize(
`ui.panel.lovelace.editor.card.clock.ticks.${value}.label`
),
description: localize(
`ui.panel.lovelace.editor.card.clock.ticks.${value}.description`
),
})
),
},
},
},
] as const satisfies readonly HaFormSchema[])
: []),
{
name: "time_zone",
selector: {
@@ -107,10 +182,15 @@ export class HuiClockCardEditor
);
private _data = memoizeOne((config) => ({
clock_style: "digital",
clock_size: "small",
time_zone: "auto",
time_format: "auto",
show_seconds: false,
no_background: false,
// Analog clock options
border: false,
ticks: "hour",
...config,
}));
@@ -128,8 +208,13 @@ export class HuiClockCardEditor
<ha-form
.hass=${this.hass}
.data=${this._data(this._config)}
.schema=${this._schema(this.hass.localize)}
.schema=${this._schema(
this.hass.localize,
(this._data(this._config).clock_style as "digital" | "analog") ??
"digital"
)}
.computeLabel=${this._computeLabelCallback}
.computeHelper=${this._computeHelperCallback}
@value-changed=${this._valueChanged}
></ha-form>
`;
@@ -143,6 +228,14 @@ export class HuiClockCardEditor
delete ev.detail.value.time_format;
}
if (ev.detail.value.clock_style === "analog") {
ev.detail.value.border = ev.detail.value.border ?? false;
ev.detail.value.ticks = ev.detail.value.ticks ?? "hour";
} else {
delete ev.detail.value.border;
delete ev.detail.value.ticks;
}
fireEvent(this, "config-changed", { config: ev.detail.value });
}
@@ -154,6 +247,10 @@ export class HuiClockCardEditor
return this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.title"
);
case "clock_style":
return this.hass!.localize(
`ui.panel.lovelace.editor.card.clock.clock_style`
);
case "clock_size":
return this.hass!.localize(
`ui.panel.lovelace.editor.card.clock.clock_size`
@@ -174,6 +271,31 @@ export class HuiClockCardEditor
return this.hass!.localize(
`ui.panel.lovelace.editor.card.clock.no_background`
);
case "border":
return this.hass!.localize(
`ui.panel.lovelace.editor.card.clock.border.label`
);
case "ticks":
return this.hass!.localize(
`ui.panel.lovelace.editor.card.clock.ticks.label`
);
default:
return undefined;
}
};
private _computeHelperCallback = (
schema: SchemaUnion<ReturnType<typeof this._schema>>
) => {
switch (schema.name) {
case "border":
return this.hass!.localize(
`ui.panel.lovelace.editor.card.clock.border.description`
);
case "ticks":
return this.hass!.localize(
`ui.panel.lovelace.editor.card.clock.ticks.description`
);
default:
return undefined;
}

View File

@@ -7704,6 +7704,11 @@
"clock": {
"name": "Clock",
"description": "The Clock card displays the current time using your desired size and format.",
"clock_style": "Clock style",
"clock_styles": {
"digital": "Digital",
"analog": "Analog"
},
"clock_size": "Clock size",
"clock_sizes": {
"small": "Small",
@@ -7723,7 +7728,31 @@
"time_zones": {
"auto": "Use user settings"
},
"no_background": "No background"
"no_background": "No background",
"border": {
"label": "Border",
"description": "Whether to show a border around the clock"
},
"ticks": {
"label": "Ticks",
"description": "Whether to show ticks (indices) on the outside of the clock",
"none": {
"label": "None",
"description": "No ticks (Hands only)"
},
"quarter": {
"label": "Quarter",
"description": "4 ticks (Every 15 minutes)"
},
"hour": {
"label": "Hour",
"description": "12 ticks (Every hour)"
},
"minute": {
"label": "Minute",
"description": "60 ticks (Every minute)"
}
}
},
"media-control": {
"name": "Media control",