Add date card feature (#26531)

* Add date card feature

* Rename from "date" to "date-set"

* Better label

* Fix

* Parse date for datetime
This commit is contained in:
Aidan Timson
2025-08-18 10:57:09 +01:00
committed by GitHub
parent 03a628cfe2
commit d528ab06d9
5 changed files with 159 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import "../../../components/ha-control-slider";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature } from "../types";
import { cardFeatureStyles } from "./common/card-feature-styles";
import type {
LovelaceCardFeatureContext,
DateSetCardFeatureConfig,
} from "./types";
import { fireEvent } from "../../../common/dom/fire_event";
import type { DatePickerDialogParams } from "../../../components/ha-date-input";
import { firstWeekdayIndex } from "../../../common/datetime/first_weekday";
import "../../../components/ha-control-button";
import "../../../components/ha-control-button-group";
const loadDatePickerDialog = () =>
import("../../../components/ha-dialog-date-picker");
export const supportsDateSetCardFeature = (
hass: HomeAssistant,
context: LovelaceCardFeatureContext
) => {
const stateObj = context.entity_id
? hass.states[context.entity_id]
: undefined;
if (!stateObj) return false;
const domain = computeDomain(stateObj.entity_id);
return (
(domain === "input_datetime" && stateObj.attributes.has_date) ||
["datetime", "date"].includes(domain)
);
};
@customElement("hui-date-set-card-feature")
class HuiDateSetCardFeature extends LitElement implements LovelaceCardFeature {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
@property({ attribute: false }) public color?: string;
@state() private _config?: DateSetCardFeatureConfig;
private get _stateObj() {
if (!this.hass || !this.context || !this.context.entity_id) {
return undefined;
}
return this.hass.states[this.context.entity_id!] ?? undefined;
}
private _pressButton() {
if (!this.hass || !this._stateObj) return;
const dialogParams: DatePickerDialogParams = {
min: "1970-01-01",
value: this._stateObj.state,
onChange: (value) => this._dateChanged(value),
locale: this.hass.locale.language,
firstWeekday: firstWeekdayIndex(this.hass.locale),
};
fireEvent(this, "show-dialog", {
dialogTag: "ha-dialog-date-picker",
dialogImport: loadDatePickerDialog,
dialogParams,
});
}
private _dateChanged(value: string | undefined) {
if (!this.hass || !this._stateObj || !value) return;
const domain = computeDomain(this._stateObj.entity_id);
const service = domain === "input_datetime" ? "set_datetime" : "set_value";
// datetime requires a full datetime string
if (domain === "datetime") {
const dateObj = new Date(this._stateObj.state);
const selectedDate = new Date(`${value}T00:00:00`);
dateObj.setFullYear(
selectedDate.getFullYear(),
selectedDate.getMonth(),
selectedDate.getDate()
);
this.hass.callService(domain, service, {
entity_id: this._stateObj.entity_id,
datetime: dateObj.toISOString(),
});
} else {
this.hass.callService(domain, service, {
entity_id: this._stateObj.entity_id,
date: value,
});
}
}
static getStubConfig(): DateSetCardFeatureConfig {
return {
type: "date-set",
};
}
public setConfig(config: DateSetCardFeatureConfig): void {
if (!config) {
throw new Error("Invalid configuration");
}
this._config = config;
}
protected render() {
if (
!this._config ||
!this.hass ||
!this.context ||
!this._stateObj ||
!supportsDateSetCardFeature(this.hass, this.context)
) {
return nothing;
}
return html`
<ha-control-button-group>
<ha-control-button
.disabled=${["unavailable", "unknown"].includes(this._stateObj.state)}
class="press-button"
@click=${this._pressButton}
>
${this.hass.localize("ui.card.date.set_date")}
</ha-control-button>
</ha-control-button-group>
`;
}
static styles = cardFeatureStyles;
}
declare global {
interface HTMLElementTagNameMap {
"hui-date-set-card-feature": HuiDateSetCardFeature;
}
}

View File

@@ -101,6 +101,10 @@ export interface CounterActionsCardFeatureConfig {
actions?: CounterActions[];
}
export interface DateSetCardFeatureConfig {
type: "date-set";
}
export interface SelectOptionsCardFeatureConfig {
type: "select-options";
options?: string[];
@@ -213,6 +217,7 @@ export type LovelaceCardFeatureConfig =
| CoverPositionCardFeatureConfig
| CoverTiltPositionCardFeatureConfig
| CoverTiltCardFeatureConfig
| DateSetCardFeatureConfig
| FanDirectionCardFeatureConfig
| FanPresetModesCardFeatureConfig
| FanSpeedCardFeatureConfig

View File

@@ -10,6 +10,7 @@ import "../card-features/hui-cover-open-close-card-feature";
import "../card-features/hui-cover-position-card-feature";
import "../card-features/hui-cover-tilt-card-feature";
import "../card-features/hui-cover-tilt-position-card-feature";
import "../card-features/hui-date-set-card-feature";
import "../card-features/hui-fan-direction-card-feature";
import "../card-features/hui-fan-preset-modes-card-feature";
import "../card-features/hui-fan-speed-card-feature";
@@ -53,6 +54,7 @@ const TYPES = new Set<LovelaceCardFeatureConfig["type"]>([
"cover-position",
"cover-tilt-position",
"cover-tilt",
"date-set",
"fan-direction",
"fan-preset-modes",
"fan-speed",

View File

@@ -30,6 +30,7 @@ import { supportsCoverOpenCloseCardFeature } from "../../card-features/hui-cover
import { supportsCoverPositionCardFeature } from "../../card-features/hui-cover-position-card-feature";
import { supportsCoverTiltCardFeature } from "../../card-features/hui-cover-tilt-card-feature";
import { supportsCoverTiltPositionCardFeature } from "../../card-features/hui-cover-tilt-position-card-feature";
import { supportsDateSetCardFeature } from "../../card-features/hui-date-set-card-feature";
import { supportsFanDirectionCardFeature } from "../../card-features/hui-fan-direction-card-feature";
import { supportsFanPresetModesCardFeature } from "../../card-features/hui-fan-preset-modes-card-feature";
import { supportsFanSpeedCardFeature } from "../../card-features/hui-fan-speed-card-feature";
@@ -78,6 +79,7 @@ const UI_FEATURE_TYPES = [
"cover-position",
"cover-tilt-position",
"cover-tilt",
"date-set",
"fan-direction",
"fan-preset-modes",
"fan-speed",
@@ -141,6 +143,7 @@ const SUPPORTS_FEATURE_TYPES: Record<
"cover-position": supportsCoverPositionCardFeature,
"cover-tilt-position": supportsCoverTiltPositionCardFeature,
"cover-tilt": supportsCoverTiltCardFeature,
"date-set": supportsDateSetCardFeature,
"fan-direction": supportsFanDirectionCardFeature,
"fan-preset-modes": supportsFanPresetModesCardFeature,
"fan-speed": supportsFanSpeedCardFeature,

View File

@@ -147,6 +147,9 @@
"close_tilt_cover": "Close cover tilt",
"stop_cover": "Stop cover"
},
"date": {
"set_date": "Set date"
},
"fan": {
"preset_mode": "Preset mode",
"oscillate": "Oscillate",
@@ -7861,6 +7864,9 @@
"cover-tilt-position": {
"label": "Cover tilt position"
},
"date-set": {
"label": "Set date"
},
"fan-direction": {
"label": "Fan direction"
},