Compare commits

...

11 Commits

Author SHA1 Message Date
Paul Bottein
e642c80003 Fix device automations 2024-10-09 19:28:50 +02:00
Paul Bottein
cc07d51613 Add sequence support to dashboard action 2024-10-09 18:53:41 +02:00
karwosts
23a33b10a1 Allow override entity_id in more-info action (#22147) 2024-10-09 14:14:03 +02:00
Simon Lamon
67a93013c7 Revert "Fix drag and drop when using action and trigger selector" (#22296)
Revert "Fix drag and drop when using action and trigger selector (#22291)"

This reverts commit 99035cea8f.
2024-10-09 10:22:40 +00:00
Bram Kragten
1f838d7529 Update statistics issues from dev tools (#22286)
update statistics issues from dev tools
2024-10-09 09:29:30 +02:00
TJ Horner
ffc0435144 Fix erroneous addition of service: key in ha-automation-action-play_media element (#22294)
fix: no longer erroneously set 'service' in ha-automation-action-play_media
2024-10-08 21:07:38 +00:00
David F. Mulcahey
5877d69c87 Fix ZHA group dashboard display on mobile (#22279) 2024-10-08 21:20:07 +02:00
Paul Bottein
99035cea8f Fix drag and drop when using action and trigger selector (#22291)
* Fix drag and drop when using action selector

* Fix drag and drop when using trigger selector
2024-10-08 21:04:45 +02:00
__JosephAbbey
1b441a7eec Add support for relative start and end time displays in state-display (#22249)
* Add support for relative start and end time displays in state-display

* Add support for sun attributes as well

* Improve state-display code for relative-time

---------

Co-authored-by: Wendelin <w@pe8.at>
2024-10-08 10:04:16 +02:00
Paul Bottein
ad49e9f7b0 Add minimal size for badges and cards in edit mode (#22271) 2024-10-07 17:33:47 +02:00
Petar Petrov
e32b15ede2 Hide service dropdown for predefined actions in automations (#22275)
Hide service dropdown for predefined actions
2024-10-07 15:49:44 +02:00
14 changed files with 169 additions and 62 deletions

View File

@@ -3,6 +3,7 @@ import "@material/mwc-list/mwc-list-item";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { property, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { stopPropagation } from "../../common/dom/stop_propagation";
import { fullEntitiesContext } from "../../data/context";
import {
DeviceAutomation,
@@ -103,6 +104,7 @@ export abstract class HaDeviceAutomationPicker<
.label=${this.label}
.value=${value}
@selected=${this._automationChanged}
@closed=${stopPropagation}
.disabled=${this._automations.length === 0}
>
${value === NO_AUTOMATION_KEY

View File

@@ -174,6 +174,7 @@ export class HaServiceControl extends LitElement {
if (this._value && serviceData) {
const loadDefaults = this.value && !("data" in this.value);
// Set mandatory bools without a default value to false
this._value = { ...this._value };
if (!this._value.data) {
this._value.data = {};
}

View File

@@ -1,4 +1,5 @@
import type { HassServiceTarget } from "home-assistant-js-websocket";
import { Action } from "../../script";
export interface ToggleActionConfig extends BaseActionConfig {
action: "toggle";
@@ -28,6 +29,12 @@ export interface UrlActionConfig extends BaseActionConfig {
export interface MoreInfoActionConfig extends BaseActionConfig {
action: "more-info";
entity_id?: string;
}
export interface SequenceActionConfig extends BaseActionConfig {
action: "sequence";
actions?: Action[];
}
export interface AssistActionConfig extends BaseActionConfig {
@@ -66,4 +73,5 @@ export type ActionConfig =
| MoreInfoActionConfig
| AssistActionConfig
| NoActionConfig
| CustomActionConfig;
| CustomActionConfig
| SequenceActionConfig;

View File

@@ -52,7 +52,7 @@ export class HaPlayMediaAction extends LitElement implements ActionElement {
fireEvent(this, "value-changed", {
value: {
...this.action,
service: "media_player.play_media",
action: "media_player.play_media",
target: { entity_id: ev.detail.value.entity_id },
data: {
media_content_id: ev.detail.value.media_content_id,

View File

@@ -117,6 +117,7 @@ export class HaServiceAction extends LitElement implements ActionElement {
.value=${this._action}
.disabled=${this.disabled}
.showAdvanced=${this.hass.userData?.showAdvanced}
.hidePicker=${!!this._action.metadata}
@value-changed=${this._actionChanged}
></ha-service-control>
${domain && service && this.hass.services[domain]?.[service]?.response

View File

@@ -24,6 +24,7 @@ import { haStyle } from "../../../../../resources/styles";
import { HomeAssistant, Route } from "../../../../../types";
import { formatAsPaddedHex, sortZHAGroups } from "./functions";
import { zhaTabs } from "./zha-config-dashboard";
import { LocalizeFunc } from "../../../../../common/translations/localize";
export interface GroupRowData extends ZHAGroup {
group?: GroupRowData;
@@ -71,38 +72,35 @@ export class ZHAGroupsDashboard extends LitElement {
});
private _columns = memoizeOne(
(narrow: boolean): DataTableColumnContainer<GroupRowData> =>
narrow
? {
name: {
title: "Group",
sortable: true,
filterable: true,
direction: "asc",
flex: 2,
},
}
: {
name: {
title: this.hass.localize("ui.panel.config.zha.groups.groups"),
sortable: true,
filterable: true,
direction: "asc",
flex: 2,
},
group_id: {
title: this.hass.localize("ui.panel.config.zha.groups.group_id"),
type: "numeric",
template: (group) => html` ${formatAsPaddedHex(group.group_id)} `,
sortable: true,
},
members: {
title: this.hass.localize("ui.panel.config.zha.groups.members"),
type: "numeric",
template: (group) => html` ${group.members.length} `,
sortable: true,
},
}
(localize: LocalizeFunc): DataTableColumnContainer => {
const columns: DataTableColumnContainer<GroupRowData> = {
name: {
title: localize("ui.panel.config.zha.groups.groups"),
sortable: true,
filterable: true,
showNarrow: true,
main: true,
hideable: false,
moveable: false,
direction: "asc",
flex: 2,
},
group_id: {
title: localize("ui.panel.config.zha.groups.group_id"),
type: "numeric",
template: (group) => html` ${formatAsPaddedHex(group.group_id)} `,
sortable: true,
},
members: {
title: localize("ui.panel.config.zha.groups.members"),
type: "numeric",
template: (group) => html` ${group.members.length} `,
sortable: true,
},
};
return columns;
}
);
protected render(): TemplateResult {
@@ -112,7 +110,7 @@ export class ZHAGroupsDashboard extends LitElement {
.hass=${this.hass}
.narrow=${this.narrow}
.route=${this.route}
.columns=${this._columns(this.narrow)}
.columns=${this._columns(this.hass.localize)}
.data=${this._formattedGroups(this._groups)}
@row-click=${this._handleRowClicked}
clickable

View File

@@ -39,6 +39,7 @@ import {
StatisticsValidationResult,
clearStatistics,
getStatisticIds,
updateStatisticsIssues,
validateStatistics,
} from "../../../data/recorder";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
@@ -636,6 +637,8 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
validateStatistics(this.hass),
]);
updateStatisticsIssues(this.hass);
const statsIds = new Set();
this._data = statisticIds

View File

@@ -8,6 +8,7 @@ import {
PropertyValues,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { repeat } from "lit/directives/repeat";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-sortable";
@@ -124,7 +125,7 @@ export class HuiViewBadges extends LitElement {
.options=${BADGE_SORTABLE_OPTIONS}
invert-swap
>
<div class="badges">
<div class="badges ${classMap({ "edit-mode": editMode })}">
${repeat(
badges,
(badge) => this._getBadgeKey(badge),
@@ -185,6 +186,8 @@ export class HuiViewBadges extends LitElement {
hui-badge-edit-mode {
display: block;
position: relative;
min-width: 36px;
min-height: 36px;
}
.add {

View File

@@ -3,6 +3,7 @@ import { navigate } from "../../../common/navigate";
import { forwardHaptic } from "../../../data/haptics";
import { domainToName } from "../../../data/integration";
import { ActionConfig } from "../../../data/lovelace/config/action";
import { callExecuteScript } from "../../../data/service";
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
import { showVoiceCommandDialog } from "../../../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
import { HomeAssistant } from "../../../types";
@@ -94,12 +95,13 @@ export const handleAction = async (
switch (actionConfig.action) {
case "more-info": {
if (config.entity || config.camera_image || config.image_entity) {
fireEvent(node, "hass-more-info", {
entityId: (config.entity ||
config.camera_image ||
config.image_entity)!,
});
const entityId =
actionConfig.entity_id ||
config.entity ||
config.camera_image ||
config.image_entity;
if (entityId) {
fireEvent(node, "hass-more-info", { entityId });
} else {
showToast(node, {
message: hass.localize(
@@ -176,6 +178,13 @@ export const handleAction = async (
});
break;
}
case "sequence": {
if (!actionConfig.actions) {
return;
}
callExecuteScript(hass, actionConfig.actions);
break;
}
case "fire-dom-event": {
fireEvent(node, "ll-custom", actionConfig);
}

View File

@@ -1,3 +1,5 @@
import { ContextProvider } from "@lit-labs/context";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import {
css,
CSSResultGroup,
@@ -14,7 +16,10 @@ import "../../../components/ha-assist-pipeline-picker";
import { HaFormSchema, SchemaUnion } from "../../../components/ha-form/types";
import "../../../components/ha-help-tooltip";
import "../../../components/ha-navigation-picker";
import { HaSelect } from "../../../components/ha-select";
import "../../../components/ha-service-control";
import { fullEntitiesContext } from "../../../data/context";
import { subscribeEntityRegistry } from "../../../data/entity_registry";
import {
ActionConfig,
CallServiceActionConfig,
@@ -22,9 +27,9 @@ import {
UrlActionConfig,
} from "../../../data/lovelace/config/action";
import { ServiceAction } from "../../../data/script";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { HomeAssistant } from "../../../types";
import { EditorTarget } from "../editor/types";
import { HaSelect } from "../../../components/ha-select";
export type UiAction = Exclude<ActionConfig["action"], "fire-dom-event">;
@@ -34,6 +39,7 @@ const DEFAULT_ACTIONS: UiAction[] = [
"navigate",
"url",
"perform-action",
"sequence",
"assist",
"none",
];
@@ -70,8 +76,17 @@ const ASSIST_SCHEMA = [
},
] as const satisfies readonly HaFormSchema[];
const SEQUENCE_SCHEMA = [
{
name: "actions",
selector: {
action: {},
},
},
] as const satisfies readonly HaFormSchema[];
@customElement("hui-action-editor")
export class HuiActionEditor extends LitElement {
export class HuiActionEditor extends SubscribeMixin(LitElement) {
@property({ attribute: false }) public config?: ActionConfig;
@property() public label?: string;
@@ -86,6 +101,19 @@ export class HuiActionEditor extends LitElement {
@query("ha-select") private _select!: HaSelect;
private _entitiesContext = new ContextProvider(this, {
context: fullEntitiesContext,
initialValue: [],
});
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeEntityRegistry(this.hass!.connection!, (entities) => {
this._entitiesContext.setValue(entities);
}),
];
}
get _navigation_path(): string {
const config = this.config as NavigateActionConfig | undefined;
return config?.navigation_path || "";
@@ -120,6 +148,11 @@ export class HuiActionEditor extends LitElement {
}
}
protected firstUpdated(_changedProperties: PropertyValues): void {
this.hass!.loadFragmentTranslation("config");
this.hass!.loadBackendTranslation("device_automation");
}
protected render() {
if (!this.hass) {
return nothing;
@@ -218,6 +251,17 @@ export class HuiActionEditor extends LitElement {
</ha-form>
`
: nothing}
${this.config?.action === "sequence"
? html`
<ha-form
.hass=${this.hass}
.schema=${SEQUENCE_SCHEMA}
.data=${this.config}
.computeLabel=${this._computeFormLabel}
@value-changed=${this._formValueChanged}
></ha-form>
`
: nothing}
`;
}
@@ -289,7 +333,15 @@ export class HuiActionEditor extends LitElement {
});
}
private _computeFormLabel(schema: SchemaUnion<typeof ASSIST_SCHEMA>) {
private _computeFormLabel(
schema:
| SchemaUnion<typeof ASSIST_SCHEMA>
| SchemaUnion<typeof NAVIGATE_SCHEMA>
| SchemaUnion<typeof SEQUENCE_SCHEMA>
) {
if (schema.name === "actions") {
return "";
}
return this.hass?.localize(
`ui.panel.lovelace.editor.action-editor.${schema.name}`
);

View File

@@ -48,6 +48,12 @@ const actionConfigStructService = object({
confirmation: optional(actionConfigStructConfirmation),
});
const actionConfigStructSequence = object({
action: literal("sequence"),
actions: optional(array(object())),
confirmation: optional(actionConfigStructConfirmation),
});
const actionConfigStructNavigate = object({
action: literal("navigate"),
navigation_path: string(),
@@ -61,6 +67,11 @@ const actionConfigStructAssist = type({
start_listening: optional(boolean()),
});
const actionConfigStructMoreInfo = type({
action: literal("more-info"),
entity_id: optional(string()),
});
export const actionConfigStructType = object({
action: enums([
"none",
@@ -93,6 +104,12 @@ export const actionConfigStruct = dynamic<any>((value) => {
case "assist": {
return actionConfigStructAssist;
}
case "more-info": {
return actionConfigStructMoreInfo;
}
case "sequence": {
return actionConfigStructSequence;
}
}
}

View File

@@ -204,6 +204,10 @@ export class GridSection extends LitElement implements LovelaceSectionElement {
grid-column: span min(var(--column-size, 1), var(--grid-column-count));
}
.container.edit-mode .card {
min-height: calc((var(--row-height) - var(--row-gap)) / 2);
}
.card.fit-rows {
height: calc(
(var(--row-size, 1) * (var(--row-height) + var(--row-gap))) - var(

View File

@@ -99,31 +99,38 @@ class StateDisplay extends LitElement {
if (content === "name") {
return html`${this.name || stateObj.attributes.friendly_name}`;
}
let relativeDateTime: string | undefined;
// Check last-changed for backwards compatibility
if (content === "last_changed" || content === "last-changed") {
return html`
<ha-relative-time
.hass=${this.hass}
.datetime=${stateObj.last_changed}
capitalize
></ha-relative-time>
`;
relativeDateTime = stateObj.last_changed;
}
// Check last_updated for backwards compatibility
if (content === "last_updated" || content === "last-updated") {
return html`
<ha-relative-time
.hass=${this.hass}
.datetime=${stateObj.last_updated}
capitalize
></ha-relative-time>
`;
relativeDateTime = stateObj.last_updated;
}
if (content === "last_triggered") {
if (
content === "last_triggered" ||
(domain === "calendar" &&
(content === "start_time" || content === "end_time")) ||
(domain === "sun" &&
(content === "next_dawn" ||
content === "next_dusk" ||
content === "next_midnight" ||
content === "next_noon" ||
content === "next_rising" ||
content === "next_setting"))
) {
relativeDateTime = stateObj.attributes[content];
}
if (relativeDateTime) {
return html`
<ha-relative-time
.hass=${this.hass}
.datetime=${stateObj.attributes.last_triggered}
.datetime=${relativeDateTime}
capitalize
></ha-relative-time>
`;

View File

@@ -5752,10 +5752,12 @@
"more-info": "More info",
"toggle": "Toggle",
"navigate": "Navigate",
"sequence": "Sequence",
"assist": "Assist",
"url": "URL",
"none": "Nothing"
}
},
"sequence_actions": "Actions"
},
"condition-editor": {
"explanation": "The card will be shown when ALL conditions below are fulfilled.",