20240829.0 (#21836)

This commit is contained in:
Bram Kragten 2024-08-29 16:40:01 +02:00 committed by GitHub
commit 22de449dda
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
60 changed files with 267 additions and 199 deletions

View File

@ -11,7 +11,6 @@ import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervis
import type { ConditionWithShorthand } from "../../../../src/data/automation";
import "../../../../src/panels/config/automation/condition/ha-automation-condition";
import { HaDeviceCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-device";
import { HaLogicalCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-logical";
import HaNumericStateCondition from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-numeric_state";
import { HaStateCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-state";
import { HaSunCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-sun";
@ -19,62 +18,67 @@ import { HaTemplateCondition } from "../../../../src/panels/config/automation/co
import { HaTimeCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-time";
import { HaTriggerCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-trigger";
import { HaZoneCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-zone";
import { HaAndCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-and";
import { HaOrCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-or";
import { HaNotCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-not";
const SCHEMAS: { name: string; conditions: ConditionWithShorthand[] }[] = [
{
name: "State",
conditions: [{ condition: "state", ...HaStateCondition.defaultConfig }],
conditions: [{ ...HaStateCondition.defaultConfig }],
},
{
name: "Numeric State",
conditions: [
{ condition: "numeric_state", ...HaNumericStateCondition.defaultConfig },
],
conditions: [{ ...HaNumericStateCondition.defaultConfig }],
},
{
name: "Sun",
conditions: [{ condition: "sun", ...HaSunCondition.defaultConfig }],
conditions: [{ ...HaSunCondition.defaultConfig }],
},
{
name: "Zone",
conditions: [{ condition: "zone", ...HaZoneCondition.defaultConfig }],
conditions: [{ ...HaZoneCondition.defaultConfig }],
},
{
name: "Time",
conditions: [{ condition: "time", ...HaTimeCondition.defaultConfig }],
conditions: [{ ...HaTimeCondition.defaultConfig }],
},
{
name: "Template",
conditions: [
{ condition: "template", ...HaTemplateCondition.defaultConfig },
],
conditions: [{ ...HaTemplateCondition.defaultConfig }],
},
{
name: "Device",
conditions: [{ condition: "device", ...HaDeviceCondition.defaultConfig }],
conditions: [{ ...HaDeviceCondition.defaultConfig }],
},
{
name: "And",
conditions: [{ condition: "and", ...HaLogicalCondition.defaultConfig }],
conditions: [{ ...HaAndCondition.defaultConfig }],
},
{
name: "Or",
conditions: [{ condition: "or", ...HaLogicalCondition.defaultConfig }],
conditions: [{ ...HaOrCondition.defaultConfig }],
},
{
name: "Not",
conditions: [{ condition: "not", ...HaLogicalCondition.defaultConfig }],
conditions: [{ ...HaNotCondition.defaultConfig }],
},
{
name: "Trigger",
conditions: [{ condition: "trigger", ...HaTriggerCondition.defaultConfig }],
conditions: [{ ...HaTriggerCondition.defaultConfig }],
},
{
name: "Shorthand",
conditions: [
{ and: HaLogicalCondition.defaultConfig.conditions },
{ or: HaLogicalCondition.defaultConfig.conditions },
{ not: HaLogicalCondition.defaultConfig.conditions },
{
...HaAndCondition.defaultConfig,
},
{
...HaOrCondition.defaultConfig,
},
{
...HaNotCondition.defaultConfig,
},
],
},
];

View File

@ -30,55 +30,48 @@ import { HaConversationTrigger } from "../../../../src/panels/config/automation/
const SCHEMAS: { name: string; triggers: Trigger[] }[] = [
{
name: "State",
triggers: [{ platform: "state", ...HaStateTrigger.defaultConfig }],
triggers: [{ ...HaStateTrigger.defaultConfig }],
},
{
name: "MQTT",
triggers: [{ platform: "mqtt", ...HaMQTTTrigger.defaultConfig }],
triggers: [{ ...HaMQTTTrigger.defaultConfig }],
},
{
name: "GeoLocation",
triggers: [
{ platform: "geo_location", ...HaGeolocationTrigger.defaultConfig },
],
triggers: [{ ...HaGeolocationTrigger.defaultConfig }],
},
{
name: "Home Assistant",
triggers: [{ platform: "homeassistant", ...HaHassTrigger.defaultConfig }],
triggers: [{ ...HaHassTrigger.defaultConfig }],
},
{
name: "Numeric State",
triggers: [
{ platform: "numeric_state", ...HaNumericStateTrigger.defaultConfig },
],
triggers: [{ ...HaNumericStateTrigger.defaultConfig }],
},
{
name: "Sun",
triggers: [{ platform: "sun", ...HaSunTrigger.defaultConfig }],
triggers: [{ ...HaSunTrigger.defaultConfig }],
},
{
name: "Time Pattern",
triggers: [
{ platform: "time_pattern", ...HaTimePatternTrigger.defaultConfig },
],
triggers: [{ ...HaTimePatternTrigger.defaultConfig }],
},
{
name: "Webhook",
triggers: [{ platform: "webhook", ...HaWebhookTrigger.defaultConfig }],
triggers: [{ ...HaWebhookTrigger.defaultConfig }],
},
{
name: "Persistent Notification",
triggers: [
{
platform: "persistent_notification",
...HaPersistentNotificationTrigger.defaultConfig,
},
],
@ -86,37 +79,37 @@ const SCHEMAS: { name: string; triggers: Trigger[] }[] = [
{
name: "Zone",
triggers: [{ platform: "zone", ...HaZoneTrigger.defaultConfig }],
triggers: [{ ...HaZoneTrigger.defaultConfig }],
},
{
name: "Tag",
triggers: [{ platform: "tag", ...HaTagTrigger.defaultConfig }],
triggers: [{ ...HaTagTrigger.defaultConfig }],
},
{
name: "Time",
triggers: [{ platform: "time", ...HaTimeTrigger.defaultConfig }],
triggers: [{ ...HaTimeTrigger.defaultConfig }],
},
{
name: "Template",
triggers: [{ platform: "template", ...HaTemplateTrigger.defaultConfig }],
triggers: [{ ...HaTemplateTrigger.defaultConfig }],
},
{
name: "Event",
triggers: [{ platform: "event", ...HaEventTrigger.defaultConfig }],
triggers: [{ ...HaEventTrigger.defaultConfig }],
},
{
name: "Device Trigger",
triggers: [{ platform: "device", ...HaDeviceTrigger.defaultConfig }],
triggers: [{ ...HaDeviceTrigger.defaultConfig }],
},
{
name: "Sentence",
triggers: [
{ platform: "conversation", ...HaConversationTrigger.defaultConfig },
{ ...HaConversationTrigger.defaultConfig },
{
platform: "conversation",
command: ["Turn on the lights", "Turn the lights on"],

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
version = "20240828.0"
version = "20240829.0"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"

View File

@ -18,9 +18,9 @@ export class HaFormfield extends FormfieldBase {
return html` <div class="mdc-form-field ${classMap(classes)}">
<slot></slot>
<label class="mdc-label" @click=${this._labelClick}
><slot name="label">${this.label}</slot></label
>
<label class="mdc-label" @click=${this._labelClick}>
<slot name="label">${this.label}</slot>
</label>
</div>`;
}
@ -57,13 +57,13 @@ export class HaFormfield extends FormfieldBase {
}
.mdc-form-field {
align-items: var(--ha-formfield-align-items, center);
gap: 4px;
}
.mdc-form-field > label {
direction: var(--direction);
margin-inline-start: 0;
margin-inline-end: auto;
padding-inline-start: 4px;
padding-inline-end: 0;
padding: 0;
}
:host([disabled]) label {
color: var(--disabled-text-color);

View File

@ -1,4 +1,4 @@
import { css, CSSResultGroup, html, LitElement } from "lit";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { HomeAssistant } from "../../types";
@ -28,10 +28,13 @@ export class HaBooleanSelector extends LitElement {
@change=${this._handleChange}
.disabled=${this.disabled}
></ha-switch>
<span slot="label">
<p class="primary">${this.label}</p>
${this.helper
? html`<p class="secondary">${this.helper}</p>`
: nothing}
</span>
</ha-formfield>
${this.helper
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`
: ""}
`;
}
@ -47,10 +50,21 @@ export class HaBooleanSelector extends LitElement {
return css`
ha-formfield {
display: flex;
height: 56px;
min-height: 56px;
align-items: center;
--mdc-typography-body2-font-size: 1em;
}
p {
margin: 0;
}
.secondary {
direction: var(--direction);
padding-top: 4px;
box-sizing: border-box;
color: var(--secondary-text-color);
font-size: 0.875rem;
font-weight: var(--mdc-typography-body2-font-weight, 400);
}
`;
}
}

View File

@ -1,4 +1,11 @@
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import {
css,
CSSResultGroup,
html,
LitElement,
nothing,
PropertyValues,
} from "lit";
import { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../common/dom/fire_event";
@ -60,12 +67,10 @@ export class HaNumberSelector extends LitElement {
}
return html`
${this.label ? html`${this.label}${this.required ? "*" : ""}` : nothing}
<div class="input">
${!isBox
? html`
${this.label
? html`${this.label}${this.required ? "*" : ""}`
: ""}
<ha-slider
labeled
.min=${this.selector.number!.min}
@ -75,10 +80,11 @@ export class HaNumberSelector extends LitElement {
.disabled=${this.disabled}
.required=${this.required}
@change=${this._handleSliderChange}
.ticks=${this.selector.number?.slider_ticks}
>
</ha-slider>
`
: ""}
: nothing}
<ha-textfield
.inputMode=${this.selector.number?.step === "any" ||
(this.selector.number?.step ?? 1) % 1 !== 0
@ -105,7 +111,7 @@ export class HaNumberSelector extends LitElement {
</div>
${!isBox && this.helper
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`
: ""}
: nothing}
`;
}
@ -141,6 +147,9 @@ export class HaNumberSelector extends LitElement {
}
ha-slider {
flex: 1;
margin-right: 16px;
margin-inline-end: 16px;
margin-inline-start: 0;
}
ha-textfield {
--ha-textfield-input-width: 40px;

View File

@ -20,6 +20,7 @@ export class HaSlider extends MdSlider {
--md-sys-color-on-surface: var(--primary-text-color);
--md-slider-handle-width: 14px;
--md-slider-handle-height: 14px;
--md-slider-state-layer-size: 24px;
min-width: 100px;
min-inline-size: 100px;
width: 200px;

View File

@ -22,7 +22,9 @@ export interface LovelaceBaseViewConfig {
visible?: boolean | ShowViewConfig[];
subview?: boolean;
back_path?: string;
max_columns?: number; // Only used for section view, it should move to a section view config type when the views will have dedicated editor.
// Only used for section view, it should move to a section view config type when the views will have dedicated editor.
max_columns?: number;
dense_section_placement?: boolean;
}
export interface LovelaceViewConfig extends LovelaceBaseViewConfig {

View File

@ -323,6 +323,7 @@ export interface NumberSelector {
step?: number | "any";
mode?: "box" | "slider";
unit_of_measurement?: string;
slider_ticks?: boolean;
} | null;
}

View File

@ -86,7 +86,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
this._unsubMql = undefined;
}
public static get defaultConfig() {
public static get defaultConfig(): ChooseAction {
return { choose: [{ conditions: [], sequence: [] }] };
}

View File

@ -20,7 +20,7 @@ export class HaConditionAction extends LitElement implements ActionElement {
@property({ attribute: false }) public action!: Condition;
public static get defaultConfig() {
public static get defaultConfig(): Omit<Condition, "state" | "entity_id"> {
return { condition: "state" };
}
@ -87,13 +87,12 @@ export class HaConditionAction extends LitElement implements ActionElement {
const elClass = customElements.get(
`ha-automation-condition-${type}`
) as CustomElementConstructor & {
defaultConfig: Omit<Condition, "condition">;
defaultConfig: Condition;
};
if (type !== this.action.condition) {
fireEvent(this, "value-changed", {
value: {
condition: type,
...elClass.defaultConfig,
},
});

View File

@ -19,7 +19,7 @@ export class HaDelayAction extends LitElement implements ActionElement {
@state() private _timeData?: HaDurationData;
public static get defaultConfig() {
public static get defaultConfig(): DelayAction {
return { delay: "" };
}

View File

@ -36,7 +36,7 @@ export class HaDeviceAction extends LitElement {
private _origAction?: DeviceAction;
public static get defaultConfig() {
public static get defaultConfig(): DeviceAction {
return {
device_id: "",
domain: "",

View File

@ -21,7 +21,7 @@ export class HaIfAction extends LitElement implements ActionElement {
@state() private _showElse = false;
public static get defaultConfig() {
public static get defaultConfig(): IfAction {
return {
if: [],
then: [],

View File

@ -18,7 +18,7 @@ export class HaParallelAction extends LitElement implements ActionElement {
@property({ attribute: false }) public action!: ParallelAction;
public static get defaultConfig() {
public static get defaultConfig(): ParallelAction {
return {
parallel: [],
};

View File

@ -31,7 +31,7 @@ export class HaRepeatAction extends LitElement implements ActionElement {
@property({ type: Array }) public path?: ItemPath;
public static get defaultConfig() {
public static get defaultConfig(): RepeatAction {
return { repeat: { count: 2, sequence: [] } };
}

View File

@ -19,7 +19,7 @@ export class HaSequenceAction extends LitElement implements ActionElement {
@property({ attribute: false }) public action!: SequenceAction;
public static get defaultConfig() {
public static get defaultConfig(): SequenceAction {
return {
sequence: [],
};

View File

@ -52,7 +52,7 @@ export class HaServiceAction extends LitElement implements ActionElement {
}
);
public static get defaultConfig() {
public static get defaultConfig(): ServiceAction {
return { action: "", data: {} };
}

View File

@ -25,7 +25,7 @@ export class HaSetConversationResponseAction
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig() {
public static get defaultConfig(): SetConversationResponseAction {
return { set_conversation_response: "" };
}

View File

@ -14,7 +14,7 @@ export class HaStopAction extends LitElement implements ActionElement {
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig() {
public static get defaultConfig(): StopAction {
return { stop: "" };
}

View File

@ -25,7 +25,7 @@ export class HaWaitForTriggerAction
@property({ attribute: false }) public path?: ItemPath;
public static get defaultConfig() {
public static get defaultConfig(): WaitForTriggerAction {
return { wait_for_trigger: [] };
}

View File

@ -34,7 +34,7 @@ export class HaWaitAction extends LitElement implements ActionElement {
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig() {
public static get defaultConfig(): WaitAction {
return { wait_template: "", continue_on_timeout: true };
}

View File

@ -207,10 +207,9 @@ export default class HaAutomationCondition extends LitElement {
const elClass = customElements.get(
`ha-automation-condition-${condition}`
) as CustomElementConstructor & {
defaultConfig: Omit<Condition, "condition">;
defaultConfig: Condition;
};
conditions = this.conditions.concat({
condition: condition as any,
...elClass.defaultConfig,
});
}

View File

@ -1,8 +1,16 @@
import { customElement } from "lit/decorators";
import { HaLogicalCondition } from "./ha-automation-condition-logical";
import { LogicalCondition } from "../../../../../data/automation";
@customElement("ha-automation-condition-and")
export class HaAndCondition extends HaLogicalCondition {}
export class HaAndCondition extends HaLogicalCondition {
public static get defaultConfig(): LogicalCondition {
return {
condition: "and",
conditions: [],
};
}
}
declare global {
interface HTMLElementTagNameMap {

View File

@ -36,8 +36,9 @@ export class HaDeviceCondition extends LitElement {
private _origCondition?: DeviceCondition;
public static get defaultConfig() {
public static get defaultConfig(): DeviceCondition {
return {
condition: "device",
device_id: "",
domain: "",
entity_id: "",

View File

@ -7,7 +7,10 @@ import "../ha-automation-condition";
import type { ConditionElement } from "../ha-automation-condition-row";
@customElement("ha-automation-condition-logical")
export class HaLogicalCondition extends LitElement implements ConditionElement {
export abstract class HaLogicalCondition
extends LitElement
implements ConditionElement
{
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public condition!: LogicalCondition;
@ -16,12 +19,6 @@ export class HaLogicalCondition extends LitElement implements ConditionElement {
@property({ attribute: false }) public path?: ItemPath;
public static get defaultConfig() {
return {
conditions: [],
};
}
protected render() {
return html`
<ha-automation-condition

View File

@ -1,8 +1,16 @@
import { customElement } from "lit/decorators";
import { HaLogicalCondition } from "./ha-automation-condition-logical";
import { LogicalCondition } from "../../../../../data/automation";
@customElement("ha-automation-condition-not")
export class HaNotCondition extends HaLogicalCondition {}
export class HaNotCondition extends HaLogicalCondition {
public static get defaultConfig(): LogicalCondition {
return {
condition: "not",
conditions: [],
};
}
}
declare global {
interface HTMLElementTagNameMap {

View File

@ -20,8 +20,9 @@ export default class HaNumericStateCondition extends LitElement {
@state() private _inputBelowIsEntity?: boolean;
public static get defaultConfig() {
public static get defaultConfig(): NumericStateCondition {
return {
condition: "numeric_state",
entity_id: "",
};
}

View File

@ -1,8 +1,16 @@
import { customElement } from "lit/decorators";
import { HaLogicalCondition } from "./ha-automation-condition-logical";
import { LogicalCondition } from "../../../../../data/automation";
@customElement("ha-automation-condition-or")
export class HaOrCondition extends HaLogicalCondition {}
export class HaOrCondition extends HaLogicalCondition {
public static get defaultConfig(): LogicalCondition {
return {
condition: "or",
conditions: [],
};
}
}
declare global {
interface HTMLElementTagNameMap {

View File

@ -86,8 +86,8 @@ export class HaStateCondition extends LitElement implements ConditionElement {
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig() {
return { entity_id: "", state: "" };
public static get defaultConfig(): StateCondition {
return { condition: "state", entity_id: "", state: "" };
}
public shouldUpdate(changedProperties: PropertyValues) {

View File

@ -17,8 +17,8 @@ export class HaSunCondition extends LitElement implements ConditionElement {
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig() {
return {};
public static get defaultConfig(): SunCondition {
return { condition: "sun" };
}
private _schema = memoizeOne(

View File

@ -13,8 +13,8 @@ export class HaTemplateCondition extends LitElement {
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig() {
return { value_template: "" };
public static get defaultConfig(): TemplateCondition {
return { condition: "template", value_template: "" };
}
protected render() {

View File

@ -25,8 +25,8 @@ export class HaTimeCondition extends LitElement implements ConditionElement {
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig() {
return {};
public static get defaultConfig(): TimeCondition {
return { condition: "time" };
}
private _schema = memoizeOne(

View File

@ -27,8 +27,9 @@ export class HaTriggerCondition extends LitElement {
private _unsub?: UnsubscribeFunc;
public static get defaultConfig() {
public static get defaultConfig(): TriggerCondition {
return {
condition: "trigger",
id: "",
};
}

View File

@ -21,8 +21,9 @@ export class HaZoneCondition extends LitElement {
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig() {
public static get defaultConfig(): ZoneCondition {
return {
condition: "zone",
entity_id: "",
zone: "",
};

View File

@ -143,10 +143,9 @@ export default class HaAutomationTrigger extends LitElement {
const elClass = customElements.get(
`ha-automation-trigger-${platform}`
) as CustomElementConstructor & {
defaultConfig: Omit<Trigger, "platform">;
defaultConfig: Trigger;
};
triggers = this.triggers.concat({
platform: platform as any,
...elClass.defaultConfig,
});
}

View File

@ -69,10 +69,12 @@ export class HaCalendarTrigger extends LitElement implements TriggerElement {
] as const
);
public static get defaultConfig() {
public static get defaultConfig(): CalendarTrigger {
return {
platform: "calendar",
entity_id: "",
event: "start" as CalendarTrigger["event"],
offset: 0,
offset: "0",
};
}

View File

@ -25,8 +25,8 @@ export class HaConversationTrigger
@query("#option_input", true) private _optionInput?: HaTextField;
public static get defaultConfig(): Omit<ConversationTrigger, "platform"> {
return { command: "" };
public static get defaultConfig(): ConversationTrigger {
return { platform: "conversation", command: "" };
}
protected render() {

View File

@ -38,8 +38,9 @@ export class HaDeviceTrigger extends LitElement {
private _origTrigger?: DeviceTrigger;
public static get defaultConfig() {
public static get defaultConfig(): DeviceTrigger {
return {
platform: "device",
device_id: "",
domain: "",
entity_id: "",

View File

@ -19,8 +19,8 @@ export class HaEventTrigger extends LitElement implements TriggerElement {
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig() {
return { event_type: "" };
public static get defaultConfig(): EventTrigger {
return { platform: "event", event_type: "" };
}
protected render() {

View File

@ -43,8 +43,9 @@ export class HaGeolocationTrigger extends LitElement {
] as const
);
public static get defaultConfig() {
public static get defaultConfig(): GeoLocationTrigger {
return {
platform: "geo_location",
source: "",
zone: "",
event: "enter" as GeoLocationTrigger["event"],

View File

@ -41,8 +41,9 @@ export class HaHassTrigger extends LitElement {
] as const
);
public static get defaultConfig() {
public static get defaultConfig(): HassTrigger {
return {
platform: "homeassistant",
event: "start" as HassTrigger["event"],
};
}

View File

@ -20,8 +20,8 @@ export class HaMQTTTrigger extends LitElement implements TriggerElement {
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig() {
return { topic: "" };
public static get defaultConfig(): MqttTrigger {
return { platform: "mqtt", topic: "" };
}
protected render() {

View File

@ -237,8 +237,9 @@ export class HaNumericStateTrigger extends LitElement {
}
}
public static get defaultConfig() {
public static get defaultConfig(): NumericStateTrigger {
return {
platform: "numeric_state",
entity_id: [],
};
}

View File

@ -70,8 +70,9 @@ export class HaPersistentNotificationTrigger
] as const
);
public static get defaultConfig() {
public static get defaultConfig(): PersistentNotificationTrigger {
return {
platform: "persistent_notification",
update_type: [...DEFAULT_UPDATE_TYPES],
notification_id: DEFAULT_NOTIFICATION_ID,
};

View File

@ -48,8 +48,8 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig() {
return { entity_id: [] };
public static get defaultConfig(): StateTrigger {
return { platform: "state", entity_id: [] };
}
private _schema = memoizeOne(

View File

@ -43,8 +43,9 @@ export class HaSunTrigger extends LitElement implements TriggerElement {
] as const
);
public static get defaultConfig() {
public static get defaultConfig(): SunTrigger {
return {
platform: "sun",
event: "sunrise" as SunTrigger["event"],
offset: 0,
};

View File

@ -19,8 +19,8 @@ export class HaTagTrigger extends LitElement implements TriggerElement {
@state() private _tags?: Tag[];
public static get defaultConfig() {
return { tag_id: "" };
public static get defaultConfig(): TagTrigger {
return { platform: "tag", tag_id: "" };
}
protected firstUpdated(changedProperties: PropertyValues) {

View File

@ -22,8 +22,8 @@ export class HaTemplateTrigger extends LitElement {
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig() {
return { value_template: "" };
public static get defaultConfig(): TemplateTrigger {
return { platform: "template", value_template: "" };
}
public willUpdate(changedProperties: PropertyValues) {

View File

@ -19,8 +19,8 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
@state() private _inputMode?: boolean;
public static get defaultConfig() {
return { at: "" };
public static get defaultConfig(): TimeTrigger {
return { platform: "time", at: "" };
}
private _schema = memoizeOne(

View File

@ -21,8 +21,8 @@ export class HaTimePatternTrigger extends LitElement implements TriggerElement {
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig() {
return {};
public static get defaultConfig(): TimePatternTrigger {
return { platform: "time_pattern" };
}
protected render() {

View File

@ -36,8 +36,9 @@ export class HaWebhookTrigger extends LitElement {
private _unsub?: UnsubscribeFunc;
public static get defaultConfig() {
public static get defaultConfig(): WebhookTrigger {
return {
platform: "webhook",
allowed_methods: [...DEFAULT_METHODS],
local_only: true,
webhook_id: DEFAULT_WEBHOOK_ID,

View File

@ -23,8 +23,9 @@ export class HaZoneTrigger extends LitElement {
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig() {
public static get defaultConfig(): ZoneTrigger {
return {
platform: "zone",
entity_id: "",
zone: "",
event: "enter" as ZoneTrigger["event"],

View File

@ -51,7 +51,6 @@ import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { HomeAssistant, Route } from "../../types";
import { subscribeLabelRegistry } from "../../data/label_registry";
import { subscribeFloorRegistry } from "../../data/floor_registry";
import { throttle } from "../../common/util/throttle";
declare global {
// for fire event
@ -396,10 +395,6 @@ class HaPanelConfig extends SubscribeMixin(HassRouterPage) {
initialValue: [],
});
private _hassThrottler = throttle((el, hass) => {
el.hass = hass;
}, 1000);
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeEntityRegistry(this.hass.connection!, (entities) => {
@ -646,11 +641,7 @@ class HaPanelConfig extends SubscribeMixin(HassRouterPage) {
this.hass.dockedSidebar === "docked" ? this._wideSidebar : this._wide;
el.route = this.routeTail;
if (el.hass !== undefined) {
this._hassThrottler(el, this.hass);
} else {
el.hass = this.hass;
}
el.hass = this.hass;
el.showAdvanced = Boolean(this.hass.userData?.showAdvanced);
el.isWide = isWide;
el.narrow = this.narrow;

View File

@ -377,7 +377,9 @@ export class HuiEnergyDevicesDetailGraphCard
});
});
const dataset = {
label: this.hass.localize("ui.panel.energy.charts.untracked_consumption"),
label: this.hass.localize(
"ui.panel.lovelace.cards.energy.energy_devices_detail_graph.untracked_consumption"
),
hidden: this._hiddenStats.includes("untracked"),
borderColor: getEnergyColor(
computedStyle,

View File

@ -1,5 +1,5 @@
import "@polymer/paper-tabs/paper-tab";
import "@polymer/paper-tabs/paper-tabs";
import "@material/mwc-tab-bar/mwc-tab-bar";
import "@material/mwc-tab/mwc-tab";
import { css, CSSResultGroup, html, nothing, TemplateResult } from "lit";
import { customElement, state } from "lit/decorators";
import { LovelaceBadgeConfig } from "../../../../data/lovelace/config/badge";
@ -8,11 +8,11 @@ import type { LovelaceCardEditor, LovelaceConfigForm } from "../../types";
import { HuiElementEditor } from "../hui-element-editor";
import "./hui-badge-visibility-editor";
type Tab = "config" | "visibility";
const tabs = ["config", "visibility"] as const;
@customElement("hui-badge-element-editor")
export class HuiBadgeElementEditor extends HuiElementEditor<LovelaceBadgeConfig> {
@state() private _curTab: Tab = "config";
@state() private _currTab: (typeof tabs)[number] = tabs[0];
protected async getConfigElement(): Promise<LovelaceCardEditor | undefined> {
const elClass = await getBadgeElementClass(this.configElementType!);
@ -36,11 +36,12 @@ export class HuiBadgeElementEditor extends HuiElementEditor<LovelaceBadgeConfig>
return undefined;
}
private _handleTabSelected(ev: CustomEvent): void {
if (!ev.detail.value) {
private _handleTabChanged(ev: CustomEvent): void {
const newTab = tabs[ev.detail.index];
if (newTab === this._currTab) {
return;
}
this._curTab = ev.detail.value.id;
this._currTab = newTab;
}
private _configChanged(ev: CustomEvent): void {
@ -49,11 +50,9 @@ export class HuiBadgeElementEditor extends HuiElementEditor<LovelaceBadgeConfig>
}
protected renderConfigElement(): TemplateResult {
const displayedTabs: Tab[] = ["config", "visibility"];
let content: TemplateResult<1> | typeof nothing = nothing;
switch (this._curTab) {
switch (this._currTab) {
case "config":
content = html`${super.renderConfigElement()}`;
break;
@ -68,22 +67,21 @@ export class HuiBadgeElementEditor extends HuiElementEditor<LovelaceBadgeConfig>
break;
}
return html`
<paper-tabs
scrollable
hide-scroll-buttons
.selected=${displayedTabs.indexOf(this._curTab)}
@selected-item-changed=${this._handleTabSelected}
<mwc-tab-bar
.activeIndex=${tabs.indexOf(this._currTab)}
@MDCTabBar:activated=${this._handleTabChanged}
>
${displayedTabs.map(
(tab, index) => html`
<paper-tab id=${tab} .dialogInitialFocus=${index === 0}>
${this.hass.localize(
${tabs.map(
(tab) => html`
<mwc-tab
.label=${this.hass.localize(
`ui.panel.lovelace.editor.edit_badge.tab_${tab}`
)}
</paper-tab>
>
</mwc-tab>
`
)}
</paper-tabs>
</mwc-tab-bar>
${content}
`;
}
@ -92,9 +90,7 @@ export class HuiBadgeElementEditor extends HuiElementEditor<LovelaceBadgeConfig>
return [
HuiElementEditor.styles,
css`
paper-tabs {
--paper-tabs-selection-bar-color: var(--primary-color);
color: var(--primary-text-color);
mwc-tab-bar {
text-transform: uppercase;
margin-bottom: 16px;
border-bottom: 1px solid var(--divider-color);

View File

@ -2,7 +2,6 @@ import { LitElement, html } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event";
import { LocalizeFunc } from "../../../../common/translations/localize";
import {
HaFormSchema,
SchemaUnion,
@ -25,7 +24,7 @@ export class HuiDialogEditSection extends LitElement {
@property({ attribute: false }) public viewConfig!: LovelaceViewConfig;
private _schema = memoizeOne(
(localize: LocalizeFunc, maxColumns: number) =>
(maxColumns: number) =>
[
{
name: "title",
@ -37,9 +36,7 @@ export class HuiDialogEditSection extends LitElement {
number: {
min: 1,
max: maxColumns,
unit_of_measurement: localize(
`ui.panel.lovelace.editor.edit_section.settings.column_span_unit`
),
slider_ticks: true,
},
},
},
@ -52,10 +49,7 @@ export class HuiDialogEditSection extends LitElement {
column_span: this.config.column_span || 1,
};
const schema = this._schema(
this.hass.localize,
this.viewConfig.max_columns || 4
);
const schema = this._schema(this.viewConfig.max_columns || 4);
return html`
<ha-form

View File

@ -48,6 +48,12 @@ export class HuiViewEditor extends LitElement {
},
{ name: "path", selector: { text: {} } },
{ name: "theme", selector: { theme: {} } },
{
name: "subview",
selector: {
boolean: {},
},
},
{
name: "type",
selector: {
@ -71,23 +77,32 @@ export class HuiViewEditor extends LitElement {
...(viewType === SECTION_VIEW_LAYOUT
? ([
{
name: "max_columns",
selector: {
number: {
min: 1,
max: 10,
mode: "slider",
name: "section_specifics",
type: "expandable",
flatten: true,
expanded: true,
schema: [
{
name: "max_columns",
selector: {
number: {
min: 1,
max: 10,
mode: "slider",
slider_ticks: true,
},
},
},
},
{
name: "dense_section_placement",
selector: {
boolean: {},
},
},
],
},
] as const satisfies HaFormSchema[])
: []),
{
name: "subview",
selector: {
boolean: {},
},
},
] as const satisfies HaFormSchema[]
);
@ -163,12 +178,12 @@ export class HuiViewEditor extends LitElement {
case "path":
return this.hass!.localize("ui.panel.lovelace.editor.card.generic.url");
case "type":
return this.hass.localize("ui.panel.lovelace.editor.edit_view.type");
case "subview":
return this.hass.localize("ui.panel.lovelace.editor.edit_view.subview");
case "max_columns":
case "dense_section_placement":
case "section_specifics":
return this.hass.localize(
"ui.panel.lovelace.editor.edit_view.max_columns"
`ui.panel.lovelace.editor.edit_view.${schema.name}`
);
default:
return this.hass!.localize(
@ -182,9 +197,11 @@ export class HuiViewEditor extends LitElement {
) => {
switch (schema.name) {
case "subview":
case "dense_section_placement":
return this.hass.localize(
"ui.panel.lovelace.editor.edit_view.subview_helper"
`ui.panel.lovelace.editor.edit_view.${schema.name}_helper`
);
default:
return undefined;
}

View File

@ -9,6 +9,7 @@ import {
nothing,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { repeat } from "lit/directives/repeat";
import { styleMap } from "lit/directives/style-map";
import "../../../components/ha-icon-button";
@ -47,7 +48,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
@state() private _config?: LovelaceViewConfig;
@state() private _sectionCount = 0;
@state() private _sectionColumnCount = 0;
@state() _dragging = false;
@ -89,9 +90,10 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
}
private _computeSectionsCount() {
this._sectionCount = this.sections.filter(
(section) => !section.hidden
).length;
this._sectionColumnCount = this.sections
.filter((section) => !section.hidden)
.map((section) => section.config.column_span ?? 1)
.reduce((acc, val) => acc + val, 0);
}
private _sectionVisibilityChanged = () => {
@ -125,7 +127,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
const sections = this.sections;
const totalSectionCount =
this._sectionCount + (this.lovelace?.editMode ? 1 : 0);
this._sectionColumnCount + (this.lovelace?.editMode ? 1 : 0);
const editMode = this.lovelace.editMode;
const maxColumnCount = this._columnsController.value ?? 1;
@ -146,7 +148,9 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
.rollback=${false}
>
<div
class="container"
class="container ${classMap({
dense: Boolean(this._config?.dense_section_placement),
})}"
style=${styleMap({
"--total-section-count": totalSectionCount,
"--max-column-count": maxColumnCount,
@ -322,6 +326,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
align-items: start;
justify-content: center;
grid-template-columns: repeat(var(--column-count), 1fr);
grid-auto-flow: row;
gap: var(--row-gap) var(--column-gap);
padding: var(--row-gap) var(--column-gap);
box-sizing: content-box;
@ -331,6 +336,9 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
(var(--column-count) - 1) * var(--column-gap)
);
}
.container.dense {
grid-auto-flow: row dense;
}
@media (max-width: 600px) {
.container {

View File

@ -5435,6 +5435,9 @@
"energy_usage": "Energy usage",
"previous_energy_usage": "Previous energy usage"
},
"energy_devices_detail_graph": {
"untracked_consumption": "Untracked consumption"
},
"carbon_consumed_gauge": {
"card_indicates_energy_used": "This card indicates how much of the energy consumed by your home was generated using non-fossil fuels like solar, wind, and nuclear. The higher, the better!",
"low_carbon_energy_consumed": "Low-carbon energy consumed",
@ -5546,7 +5549,10 @@
"sections": "Sections (experimental)"
},
"subview": "Subview",
"max_columns": "Max number of columns",
"max_columns": "Max number of sections wide",
"section_specifics": "Sections view specifics settings",
"dense_section_placement": "Dense section placement",
"dense_section_placement_helper": "Will try to fill gaps with sections that fit the gap. This may make section placement less predictable.",
"subview_helper": "Subviews don't appear in tabs and have a back button.",
"edit_ui": "Edit in visual editor",
"edit_yaml": "Edit in YAML",
@ -5662,9 +5668,8 @@
"settings": {
"title": "Title",
"title_helper": "The title will appear at the top of section. Leave empty to hide the title.",
"column_span": "Size",
"column_span_unit": "columns",
"column_span_helper": "The size may be smaller if less columns are displayed (e.g. on mobile devices)."
"column_span": "Width",
"column_span_helper": "Larger sections will be made smaller to fit the display. (e.g. on mobile devices)"
},
"visibility": {
"explanation": "The section will be shown when ALL conditions below are fulfilled. If no conditions are set, the section will always be shown."
@ -7158,8 +7163,7 @@
"charts": {
"stat_house_energy_meter": "Total energy consumption",
"solar": "Solar",
"by_device": "Consumption by device",
"untracked_consumption": "Untracked consumption"
"by_device": "Consumption by device"
},
"cards": {
"energy_usage_graph_title": "Energy usage",