Compare commits

...

36 Commits

Author SHA1 Message Date
Paul Bottein 58e968cf62 Simplify fully closed or fully open check 2024-01-04 18:32:48 +01:00
Bram Kragten fe5431976d Bumped version to 20240104.0 2024-01-04 17:43:37 +01:00
Paul Bottein 72dedc7596 Fix thermostat and humidifier card rendering when off (#19281)
* Fix thermostat and humidifier card rendering when off

* Fix action color
2024-01-04 16:18:16 +01:00
Bram Kragten 8615b799c6 Fix due date when no time in certain timezones (#19280)
* Fix due date when no time in certain timezones

* Update dialog-todo-item-editor.ts
2024-01-04 14:24:33 +01:00
Bram Kragten fd6cae48b7 Remove overflow hidden from profile (#19279) 2024-01-04 13:20:35 +01:00
Bram Kragten ecadc06b45 fix valve entities row (#19278) 2024-01-04 11:52:38 +01:00
Franck Nijhof e5d31d85dd Update getStates to support valves (#19277) 2024-01-04 10:54:20 +01:00
Bram Kragten e427ffca5f Fix turning valve on/off (#19269) 2024-01-03 21:27:30 -05:00
Bram Kragten 4f05bd9e22 Fix circular progress size + fix bug in assist pipeline debug (#19268) 2024-01-03 21:21:42 -05:00
renovate[bot] 0d8c0ac1a0 Update dependency marked to v11.1.1 (#19254) 2024-01-03 17:45:46 -05:00
karwosts 3984702d3e Fix select view dialog (#19267)
* Fix select view dialog

* add import
2024-01-03 20:36:59 +01:00
Bram Kragten 979085a9de Fix supervisor dev build (#19266) 2024-01-03 20:35:54 +01:00
Bram Kragten 7a6fc573c2 Fix version bump script (#19264) 2024-01-03 14:19:30 +00:00
Bram Kragten 6929a9c603 Bumped version to 20240103.0 2024-01-03 14:59:45 +01:00
karwosts 41bb677ebe Remove tile pointer/ripple/index when it has no action (#19137)
* Remove tile pointer/ripple/index when it has no action

* update

* Apply suggestions from code review

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>

---------

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
2024-01-03 14:58:59 +01:00
Bram Kragten 8368fb4408 Fix checking todo item that dont support due date (#19262)
* Fix checking todo item that dont support due date

* make cleaner

* Revert "make cleaner"

This reverts commit fa33b33614.

* Update dialog-todo-item-editor.ts

* do check in 1 place
2024-01-03 13:58:36 +00:00
Paul Bottein acbb7e0453 Display edit button for climate fan mode feature (#19259) 2024-01-03 14:26:40 +01:00
Bram Kragten 6224cb6994 Bumped version to 20240103.1 2024-01-03 12:28:46 +01:00
Bram Kragten 29fefa1d60 Revert conditional rendering of condition (#19257)
* Fix conditionally showing `triggered by`

* revert conditional rendering

* Update add-automation-element-dialog.ts

* Update add-automation-element-dialog.ts
2024-01-03 11:20:11 +00:00
Bram Kragten c7a98fa5a1 Bumped version to 20240103.0 2024-01-03 10:37:59 +01:00
Bram Kragten 46565715c9 Calculate used domains on open of action dialog (#19255) 2024-01-03 10:37:30 +01:00
Simon Lamon 8a8b04ed00 Set default values for required and disabled for labeled slider (#19246)
Set default values
2024-01-02 20:00:19 +01:00
Bram Kragten e721481757 Move notification services to main list (#19235) 2024-01-02 19:58:37 +01:00
renovate[bot] 2933d642ed Update dependency @bundle-stats/plugin-webpack-filter to v4.8.4 (#19230)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-02 13:55:44 -05:00
Bram Kragten 7d412a9913 Bumped version to 20240102.0 2024-01-02 18:49:31 +01:00
Bram Kragten 5eaa6dafd9 Give todo and calendar edit static header (#19233) 2024-01-02 18:48:34 +01:00
Bram Kragten f994b378f1 Update add-automation-element-dialog.ts 2024-01-02 18:47:39 +01:00
JLo 7d6dfaad15 New copy for device trigger in automation editor (#19232)
New copy for device trigger in automation editor: 
"Set of conditions provided by your device. Great way to start."
2024-01-02 17:39:55 +00:00
Josh McCarty def2eadc24 Add missing device classes for entity-registry-settings-editor (#19231)
* Add connectivity device class for binary sensors

* Add update device class

* Separate connectivity and update
2024-01-02 17:35:08 +00:00
Bram Kragten 53a25dde26 Change format of service description (#19229) 2024-01-02 18:31:18 +01:00
Bram Kragten 6f7da16d1d Use brand icons in actions (#19227) 2024-01-02 18:11:34 +01:00
Bram Kragten 5ac0360494 Remove references to "service call" from actions (#19226) 2024-01-02 16:08:26 +01:00
karwosts 1b6def4875 Localize a device action string (#19203) 2024-01-02 14:07:34 +01:00
Bram Kragten 52fcf64cfd Automation editor tweaks (#19225)
* Automation editor tweaks

* fix styling
2024-01-02 13:36:06 +01:00
JLo c650e23432 Review on automation editor text (#19223)
- Added `.` to bloc descriptions
- Changed "Other" into "OTher triggers" "Other conditions" and "Other actions"
- Adapted a few descriptions
2024-01-02 11:08:05 +00:00
renovate[bot] 428604d91d Update babel monorepo to v7.23.7 (#19220) 2024-01-01 20:15:34 -05:00
37 changed files with 481 additions and 230 deletions
+1
View File
@@ -426,6 +426,7 @@ gulp.task(
"fetch-nightly-translations",
gulp.series("clean-translations", "ensure-translations-build-dir")
),
gulp.parallel("create-test-metadata", "create-test-translation"),
"build-master-translation",
"build-merged-translations",
"build-translation-fragment-supervisor",
+7 -7
View File
@@ -25,7 +25,7 @@
"license": "Apache-2.0",
"type": "module",
"dependencies": {
"@babel/runtime": "7.23.6",
"@babel/runtime": "7.23.7",
"@braintree/sanitize-url": "7.0.0",
"@codemirror/autocomplete": "6.11.1",
"@codemirror/commands": "6.3.2",
@@ -119,7 +119,7 @@
"leaflet-draw": "1.0.4",
"lit": "2.8.0",
"luxon": "3.4.4",
"marked": "11.1.0",
"marked": "11.1.1",
"memoize-one": "6.0.0",
"node-vibrant": "3.2.1-alpha.1",
"proxy-polyfill": "0.3.2",
@@ -150,13 +150,13 @@
"xss": "1.0.14"
},
"devDependencies": {
"@babel/core": "7.23.6",
"@babel/core": "7.23.7",
"@babel/helper-define-polyfill-provider": "0.4.4",
"@babel/plugin-proposal-decorators": "7.23.6",
"@babel/plugin-transform-runtime": "7.23.6",
"@babel/preset-env": "7.23.6",
"@babel/plugin-proposal-decorators": "7.23.7",
"@babel/plugin-transform-runtime": "7.23.7",
"@babel/preset-env": "7.23.7",
"@babel/preset-typescript": "7.23.3",
"@bundle-stats/plugin-webpack-filter": "4.8.3",
"@bundle-stats/plugin-webpack-filter": "4.8.4",
"@koa/cors": "5.0.0",
"@lokalise/node-api": "12.1.0",
"@octokit/auth-oauth-device": "6.0.1",
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
version = "20240101.0"
version = "20240104.0"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"
@@ -1,7 +1,10 @@
#!/usr/bin/env node
const fs = require("fs");
const util = require("util");
const exec = util.promisify(require("child_process").exec);
/* eslint-disable no-console */
import fs from "fs";
import util from "util";
import child_process from "child_process";
const exec = util.promisify(child_process.exec);
function patch(version) {
const parts = version.split(".");
@@ -18,7 +21,7 @@ function today() {
function auto(version) {
const todayVersion = today();
if (todayVersion !== version) {
if (todayVersion.split(".")[0] !== version.split(".")[0]) {
return todayVersion;
}
return patch(version);
@@ -44,7 +47,7 @@ async function main(args) {
commit = true;
} else {
method = args.length > 0 && methods[args[0]];
commit = args.length > 1 && args[1] == "--commit";
commit = args.length > 1 && args[1] === "--commit";
}
if (!method) {
+1 -1
View File
@@ -47,7 +47,7 @@ export class HaAuthTextField extends HaTextField {
// TODO: live() directive needs casting for lit-analyzer
// https://github.com/runem/lit-analyzer/pull/91/files
// TODO: lit-analyzer labels min/max as (number|string) instead of string
return html` <input
return html`<input
aria-labelledby=${ifDefined(ariaLabelledbyOrUndef)}
aria-controls=${ifDefined(ariaControlsOrUndef)}
aria-describedby=${ifDefined(ariaDescribedbyOrUndef)}
+6
View File
@@ -29,6 +29,7 @@ import {
mdiFlash,
mdiFlower,
mdiFormatListBulleted,
mdiFormatListCheckbox,
mdiFormTextbox,
mdiGauge,
mdiGoogleAssistant,
@@ -64,6 +65,7 @@ import {
mdiTransmissionTower,
mdiWater,
mdiWaterPercent,
mdiWeatherPartlyCloudy,
mdiWeatherPouring,
mdiWeatherRainy,
mdiWeatherWindy,
@@ -128,6 +130,7 @@ export const FIXED_DOMAIN_ICONS = {
updater: mdiCloudUpload,
vacuum: mdiRobotVacuum,
wake_word: mdiChatSleep,
weather: mdiWeatherPartlyCloudy,
zone: mdiMapMarkerRadius,
};
@@ -166,6 +169,7 @@ export const FIXED_DEVICE_CLASS_ICONS = {
precipitation_intensity: mdiWeatherPouring,
pressure: mdiGauge,
reactive_power: mdiFlash,
shopping_List: mdiFormatListCheckbox,
signal_strength: mdiWifi,
sound_pressure: mdiEarHearing,
speed: mdiSpeedometer,
@@ -250,6 +254,7 @@ export const DOMAINS_INPUT_ROW = [
"text",
"time",
"vacuum",
"valve",
];
/** States that we consider "off". */
@@ -268,6 +273,7 @@ export const DOMAINS_TOGGLE = new Set([
"group",
"automation",
"humidifier",
"valve",
]);
/** Domains that have a dynamic entity image / picture. */
+1
View File
@@ -50,6 +50,7 @@ export const FIXED_DOMAIN_STATES = {
timer: ["active", "idle", "paused"],
update: ["on", "off"],
vacuum: ["cleaning", "docked", "error", "idle", "paused", "returning"],
valve: ["closed", "closing", "open", "opening"],
weather: [
"clear-night",
"cloudy",
+2 -2
View File
@@ -10,9 +10,9 @@ class HaLabeledSlider extends LitElement {
@property() public caption?: string;
@property() public disabled?: boolean;
@property({ type: Boolean }) public disabled = false;
@property() public required?: boolean;
@property({ type: Boolean }) public required = true;
@property() public min: number = 0;
+21 -7
View File
@@ -4,7 +4,14 @@ import {
HassServices,
HassServiceTarget,
} from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import {
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
nothing,
} from "lit";
import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { ensureArray } from "../common/array/ensure-array";
@@ -83,6 +90,8 @@ export class HaServiceControl extends LitElement {
@property({ type: Boolean }) public showAdvanced?: boolean;
@property({ type: Boolean, reflect: true }) public hidePicker?: boolean;
@state() private _value!: this["value"];
@state() private _checkedKeys = new Set();
@@ -363,12 +372,14 @@ export class HaServiceControl extends LitElement {
)) ||
serviceData?.description;
return html`<ha-service-picker
.hass=${this.hass}
.value=${this._value?.service}
.disabled=${this.disabled}
@value-changed=${this._serviceChanged}
></ha-service-picker>
return html`${this.hidePicker
? nothing
: html`<ha-service-picker
.hass=${this.hass}
.value=${this._value?.service}
.disabled=${this.disabled}
@value-changed=${this._serviceChanged}
></ha-service-picker>`}
<div class="description">
${description ? html`<p>${description}</p>` : ""}
${this._manifest
@@ -735,6 +746,9 @@ export class HaServiceControl extends LitElement {
margin: var(--service-control-padding, 0 16px);
padding: 16px 0;
}
:host([hidePicker]) p {
padding-top: 0;
}
.checkbox-spacer {
width: 32px;
}
@@ -53,7 +53,7 @@ class MediaUploadButton extends LitElement {
${this._uploading > 0
? html`
<ha-circular-progress
size="tiny"
size="small"
indeterminate
area-label="Uploading"
slot="icon"
+2 -2
View File
@@ -19,14 +19,14 @@ export const enum CoverEntityFeature {
}
export function isFullyOpen(stateObj: CoverEntity) {
if (stateObj.attributes.current_position !== undefined) {
if (stateObj.attributes.current_position != null) {
return stateObj.attributes.current_position === 100;
}
return stateObj.state === "open";
}
export function isFullyClosed(stateObj: CoverEntity) {
if (stateObj.attributes.current_position !== undefined) {
if (stateObj.attributes.current_position != null) {
return stateObj.attributes.current_position === 0;
}
return stateObj.state === "closed";
+2
View File
@@ -52,6 +52,7 @@ export const serviceActionStruct: Describe<ServiceAction> = assign(
target: optional(targetStruct),
data: optional(object()),
response_variable: optional(string()),
metadata: optional(object()),
})
);
@@ -133,6 +134,7 @@ export interface ServiceAction extends BaseAction {
target?: HassServiceTarget;
data?: Record<string, unknown>;
response_variable?: string;
metadata?: Record<string, unknown>;
}
export interface DeviceAction extends BaseAction {
+15 -1
View File
@@ -168,6 +168,18 @@ const tryDescribeAction = <T extends ActionType>(
const service =
hass.localize(`component.${domain}.services.${serviceName}.name`) ||
hass.services[domain][serviceName]?.name;
if (config.metadata) {
return hass.localize(
`${actionTranslationBaseKey}.service.description.service_name`,
{
domain: domainToName(hass.localize, domain),
name: service || config.service,
targets: formatListWithAnds(hass.locale, targets),
}
);
}
return hass.localize(
`${actionTranslationBaseKey}.service.description.service_based_on_name`,
{
@@ -404,7 +416,9 @@ const tryDescribeAction = <T extends ActionType>(
if (actionType === "device_action") {
const config = action as DeviceAction;
if (!config.device_id) {
return "Device action";
return hass.localize(
`${actionTranslationBaseKey}.device_id.description.no_device`
);
}
const localized = localizeDeviceAutomationAction(
hass,
+9 -3
View File
@@ -83,9 +83,12 @@ export const updateItem = (
item: item.uid,
rename: item.summary,
status: item.status,
description: item.description || null,
description: item.description,
due_datetime: item.due?.includes("T") ? item.due : undefined,
due_date: item.due?.includes("T") ? undefined : item.due || null,
due_date:
item.due === undefined || item.due?.includes("T")
? undefined
: item.due,
},
{ entity_id }
);
@@ -102,7 +105,10 @@ export const createItem = (
item: item.summary,
description: item.description || undefined,
due_datetime: item.due?.includes("T") ? item.due : undefined,
due_date: item.due?.includes("T") ? undefined : item.due,
due_date:
item.due === undefined || item.due?.includes("T")
? undefined
: item.due,
},
{ entity_id }
);
+2 -2
View File
@@ -14,14 +14,14 @@ export const enum ValveEntityFeature {
}
export function isFullyOpen(stateObj: ValveEntity) {
if (stateObj.attributes.current_position !== undefined) {
if (stateObj.attributes.current_position != null) {
return stateObj.attributes.current_position === 100;
}
return stateObj.state === "open";
}
export function isFullyClosed(stateObj: ValveEntity) {
if (stateObj.attributes.current_position !== undefined) {
if (stateObj.attributes.current_position != null) {
return stateObj.attributes.current_position === 0;
}
return stateObj.state === "closed";
@@ -144,9 +144,9 @@ class DialogCalendarEventEditor extends LitElement {
escapeKeyAction
.heading=${createCloseHeading(
this.hass,
isCreate
? this.hass.localize("ui.components.calendar.event.add")
: this._summary
this.hass.localize(
`ui.components.calendar.event.${isCreate ? "add" : "edit"}`
)
)}
>
<div class="content">
@@ -29,6 +29,8 @@ import { classMap } from "lit/directives/class-map";
import { storage } from "../../../../common/decorators/storage";
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeDomain } from "../../../../common/entity/compute_domain";
import { domainIconWithoutDefault } from "../../../../common/entity/domain_icon";
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
import { handleStructError } from "../../../../common/structs/handle-errors";
import "../../../../components/ha-alert";
@@ -190,7 +192,13 @@ export default class HaAutomationActionRow extends LitElement {
<h3 slot="header">
<ha-svg-icon
class="action-icon"
.path=${ACTION_ICONS[type!]}
.path=${type === "service" &&
"service" in this.action &&
this.action.service
? domainIconWithoutDefault(
computeDomain(this.action.service as string)
) || ACTION_ICONS[type!]
: ACTION_ICONS[type!]}
></ha-svg-icon>
${capitalizeFirstLetter(
describeAction(this.hass, this._entityReg, this.action)
@@ -191,6 +191,7 @@ export default class HaAutomationAction extends LitElement {
} else if (isService(action)) {
actions = this.actions.concat({
service: getService(action),
metadata: {},
});
} else {
const elClass = customElements.get(
@@ -1,14 +1,21 @@
import "@material/mwc-list/mwc-list";
import { mdiClose, mdiContentPaste, mdiPlus } from "@mdi/js";
import Fuse, { IFuseOptions } from "fuse.js";
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
import {
CSSResultGroup,
LitElement,
PropertyValues,
css,
html,
nothing,
} from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { repeat } from "lit/directives/repeat";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
import { domainIcon } from "../../../common/entity/domain_icon";
import { domainIconWithoutDefault } from "../../../common/entity/domain_icon";
import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
import { stringCompare } from "../../../common/string/compare";
import { LocalizeFunc } from "../../../common/translations/localize";
@@ -38,10 +45,13 @@ import { TRIGGER_GROUPS, TRIGGER_ICONS } from "../../../data/trigger";
import { HassDialog } from "../../../dialogs/make-dialog-manager";
import { haStyle, haStyleDialog } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import { brandsUrl } from "../../../util/brands-url";
import {
AddAutomationElementDialogParams,
PASTE_VALUE,
} from "./show-add-automation-element-dialog";
import { computeDomain } from "../../../common/entity/compute_domain";
import { deepEqual } from "../../../common/util/deep-equal";
const TYPES = {
trigger: { groups: TRIGGER_GROUPS, icons: TRIGGER_ICONS },
@@ -59,7 +69,8 @@ interface ListItem {
key: string;
name: string;
description: string;
icon: string;
icon?: string;
image?: string;
group: boolean;
}
@@ -79,6 +90,8 @@ const ENTITY_DOMAINS_OTHER = new Set([
"image_processing",
]);
const ENTITY_DOMAINS_MAIN = new Set(["notify"]);
@customElement("add-automation-element-dialog")
class DialogAddAutomationElement extends LitElement implements HassDialog {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -93,13 +106,15 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
@state() private _manifests?: DomainManifestLookup;
@state() private _domains?: Set<string>;
@query("ha-dialog") private _dialog?: HaDialog;
private _fullScreen = false;
private _width?: number;
@state() private _width?: number;
private _height?: number;
@state() private _height?: number;
public showDialog(params): void {
this._params = params;
@@ -107,6 +122,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
if (this._params?.type === "action") {
this.hass.loadBackendTranslation("services");
this._fetchManifests();
this._calculateUsedDomains();
}
this._fullScreen = matchMedia(
"all and (max-width: 450px), all and (max-height: 500px)"
@@ -124,8 +140,19 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
this._prev = undefined;
this._filter = "";
this._manifests = undefined;
this._domains = undefined;
}
private _getGroups = (
type: AddAutomationElementDialogParams["type"],
group: string | undefined
): AutomationElementGroup =>
group
? isService(group)
? {}
: TYPES[type].groups[group].members!
: TYPES[type].groups;
private _convertToItem = (
key: string,
options,
@@ -158,11 +185,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
services: HomeAssistant["services"],
manifests?: DomainManifestLookup
): ListItem[] => {
const groups: AutomationElementGroup = group
? isService(group)
? {}
: TYPES[type].groups[group].members!
: TYPES[type].groups;
const groups = this._getGroups(type, group);
const flattenGroups = (grp: AutomationElementGroup) =>
Object.entries(grp).map(([key, options]) =>
@@ -192,21 +215,23 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
(
type: AddAutomationElementDialogParams["type"],
group: string | undefined,
domains: Set<string> | undefined,
localize: LocalizeFunc,
services: HomeAssistant["services"],
manifests?: DomainManifestLookup
): ListItem[] => {
if (type === "action" && isService(group)) {
const result = this._services(localize, services, manifests, group);
let result = this._services(localize, services, manifests, group);
if (group === `${SERVICE_PREFIX}media_player`) {
result.unshift(this._convertToItem("play_media", {}, type, localize));
result = [
this._convertToItem("play_media", {}, type, localize),
...result,
];
}
return result;
}
const groups: AutomationElementGroup = group
? TYPES[type].groups[group].members!
: TYPES[type].groups;
const groups = this._getGroups(type, group);
const result = Object.entries(groups).map(([key, options]) =>
this._convertToItem(key, options, type, localize)
@@ -215,15 +240,33 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
if (type === "action") {
if (!this._group) {
result.unshift(
...this._serviceGroups(localize, services, manifests, undefined)
...this._serviceGroups(
localize,
services,
manifests,
domains,
undefined
)
);
} else if (this._group === "helpers") {
result.unshift(
...this._serviceGroups(localize, services, manifests, "helper")
...this._serviceGroups(
localize,
services,
manifests,
domains,
"helper"
)
);
} else if (this._group === "other") {
result.unshift(
...this._serviceGroups(localize, services, manifests, "other")
...this._serviceGroups(
localize,
services,
manifests,
domains,
"other"
)
);
}
}
@@ -243,42 +286,54 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
}
);
private _serviceGroups = memoizeOne(
(
localize: LocalizeFunc,
services: HomeAssistant["services"],
manifests: DomainManifestLookup | undefined,
type: "helper" | "other" | undefined
): ListItem[] => {
if (!services || !manifests) {
return [];
}
const result: ListItem[] = [];
Object.keys(services).forEach((domain) => {
const manifest = manifests[domain];
if (
(type === undefined &&
manifest?.integration_type === "entity" &&
!ENTITY_DOMAINS_OTHER.has(domain)) ||
(type === "helper" && manifest?.integration_type === "helper") ||
(type === "other" &&
(ENTITY_DOMAINS_OTHER.has(domain) ||
!["helper", "entity"].includes(manifest?.integration_type || "")))
) {
result.push({
group: true,
icon: domainIcon(domain),
key: `${SERVICE_PREFIX}${domain}`,
name: domainToName(localize, domain, manifest),
description: "",
});
}
});
return result.sort((a, b) =>
stringCompare(a.name, b.name, this.hass.locale.language)
);
private _serviceGroups = (
localize: LocalizeFunc,
services: HomeAssistant["services"],
manifests: DomainManifestLookup | undefined,
domains: Set<string> | undefined,
type: "helper" | "other" | undefined
): ListItem[] => {
if (!services || !manifests) {
return [];
}
);
const result: ListItem[] = [];
Object.keys(services).forEach((domain) => {
const manifest = manifests[domain];
const domainUsed = !domains ? true : domains.has(domain);
if (
(type === undefined &&
(ENTITY_DOMAINS_MAIN.has(domain) ||
(manifest?.integration_type === "entity" &&
domainUsed &&
!ENTITY_DOMAINS_OTHER.has(domain)))) ||
(type === "helper" && manifest?.integration_type === "helper") ||
(type === "other" &&
!ENTITY_DOMAINS_MAIN.has(domain) &&
(ENTITY_DOMAINS_OTHER.has(domain) ||
(!domainUsed && manifest?.integration_type === "entity") ||
!["helper", "entity"].includes(manifest?.integration_type || "")))
) {
const icon = domainIconWithoutDefault(domain);
result.push({
group: true,
icon,
image: !icon
? brandsUrl({
domain,
type: "icon",
darkOptimized: this.hass.themes?.darkMode,
})
: undefined,
key: `${SERVICE_PREFIX}${domain}`,
name: domainToName(localize, domain, manifest),
description: "",
});
}
});
return result.sort((a, b) =>
stringCompare(a.name, b.name, this.hass.locale.language)
);
};
private _services = memoizeOne(
(
@@ -302,9 +357,17 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
const services_keys = Object.keys(services[dmn]);
for (const service of services_keys) {
const icon = domainIconWithoutDefault(dmn);
result.push({
group: false,
icon: domainIcon(dmn),
icon,
image: !icon
? brandsUrl({
domain: dmn,
type: "icon",
darkOptimized: this.hass.themes?.darkMode,
})
: undefined,
key: `${SERVICE_PREFIX}${dmn}.${service}`,
name: `${domain ? "" : `${domainToName(localize, dmn)}: `}${
this.hass.localize(`component.${dmn}.services.${service}.name`) ||
@@ -360,6 +423,13 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
this._manifests = manifests;
}
private _calculateUsedDomains() {
const domains = new Set(Object.keys(this.hass.states).map(computeDomain));
if (!deepEqual(domains, this._domains)) {
this._domains = domains;
}
}
protected _opened(): void {
// Store the width and height so that when we search, box doesn't jump
const boundingRect =
@@ -368,6 +438,16 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
this._height = boundingRect?.height;
}
protected willUpdate(changedProperties: PropertyValues): void {
if (
this._params?.type === "action" &&
changedProperties.has("hass") &&
changedProperties.get("hass")?.states !== this.hass.states
) {
this._calculateUsedDomains();
}
}
protected render() {
if (!this._params) {
return nothing;
@@ -385,6 +465,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
: this._getGroupItems(
this._params.type,
this._group,
this._domains,
this.hass.localize,
this.hass.services,
this._manifests
@@ -497,7 +578,18 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
>
${item.name}
<span slot="secondary">${item.description}</span>
<ha-svg-icon slot="graphic" .path=${item.icon}></ha-svg-icon>
${item.icon
? html`<ha-svg-icon
slot="graphic"
.path=${item.icon}
></ha-svg-icon>`
: html`<img
alt=""
slot="graphic"
src=${item.image}
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>`}
${item.group
? html`<ha-icon-next slot="meta"></ha-icon-next>`
: html`<ha-svg-icon
@@ -562,6 +654,10 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
ha-icon-next {
width: 24px;
}
mwc-list {
max-height: 468px;
max-width: 100vw;
}
search-input {
display: block;
margin: 0 16px;
@@ -168,6 +168,7 @@ export class HaDeviceCondition extends LitElement {
}
ha-form {
display: block;
margin-top: 24px;
}
`;
@@ -174,6 +174,7 @@ export class HaDeviceTrigger extends LitElement {
}
ha-form {
display: block;
margin-top: 24px;
}
`;
@@ -118,6 +118,8 @@ const OVERRIDE_DEVICE_CLASSES = {
"carbon_monoxide",
"moisture",
], // Alarm
["connectivity"], // Connectivity
["update"], // Update
],
};
@@ -326,7 +326,7 @@ class DialogSystemInformation extends LitElement {
value = html`
<ha-circular-progress
indeterminate
size="tiny"
size="small"
></ha-circular-progress>
`;
} else if (info.type === "failed") {
@@ -247,7 +247,7 @@ export class AssistPipelineRunDebug extends LitElement {
}
// Play audio when we're done.
if (updatedRun.stage === "done") {
if (updatedRun.stage === "done" && !updatedRun.error) {
const url = updatedRun.tts!.tts_output!.url;
const audio = new Audio(url);
audio.addEventListener("ended", () => {
@@ -261,7 +261,10 @@ export class AssistPipelineRunDebug extends LitElement {
}
});
audio.play();
} else if (updatedRun.stage === "error") {
} else if (
(updatedRun.stage === "done" && updatedRun.error) ||
updatedRun.stage === "error"
) {
this._finished = true;
}
},
@@ -90,7 +90,7 @@ const renderProgress = (
return html``;
}
return html`
<ha-circular-progress size="tiny" indeterminate></ha-circular-progress>
<ha-circular-progress size="small" indeterminate></ha-circular-progress>
`;
}
+23 -4
View File
@@ -287,21 +287,40 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
@eventOptions({ passive: true })
private handleRippleActivate(evt?: Event) {
if (!this.hasCardAction) return;
this._rippleHandlers.startPress(evt);
}
private handleRippleDeactivate() {
if (!this.hasCardAction) return;
this._rippleHandlers.endPress();
}
private handleRippleMouseEnter() {
if (!this.hasCardAction) return;
this._rippleHandlers.startHover();
}
private handleRippleMouseLeave() {
if (!this.hasCardAction) return;
this._rippleHandlers.endHover();
}
get hasCardAction() {
return (
!this._config?.tap_action ||
hasAction(this._config?.tap_action) ||
hasAction(this._config?.hold_action) ||
hasAction(this._config?.double_tap_action)
);
}
get hasIconAction() {
return (
!this._config?.icon_tap_action || hasAction(this._config?.icon_tap_action)
);
}
protected render() {
if (!this._config || !this.hass) {
return nothing;
@@ -368,8 +387,8 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
hasHold: hasAction(this._config!.hold_action),
hasDoubleClick: hasAction(this._config!.double_tap_action),
})}
role="button"
tabindex="0"
role=${ifDefined(this.hasCardAction ? "button" : undefined)}
tabindex=${ifDefined(this.hasCardAction ? "0" : undefined)}
aria-labelledby="info"
@mousedown=${this.handleRippleActivate}
@mouseup=${this.handleRippleDeactivate}
@@ -386,8 +405,8 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
<div class="content ${classMap(contentClasses)}">
<div
class="icon-container"
role="button"
tabindex="0"
role=${ifDefined(this.hasIconAction ? "button" : undefined)}
tabindex=${ifDefined(this.hasIconAction ? "0" : undefined)}
@action=${this._handleIconAction}
.actionHandler=${actionHandler()}
>
@@ -335,7 +335,7 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard {
const due = item.due
? item.due.includes("T")
? new Date(item.due)
: endOfDay(new Date(item.due))
: endOfDay(new Date(`${item.due}T00:00:00`))
: undefined;
const today =
due && !item.due!.includes("T") && isSameDay(new Date(), due);
@@ -476,7 +476,8 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard {
return;
}
await updateItem(this.hass!, this._entityId!, {
...item,
uid: item.uid,
summary: item.summary,
status:
item.status === TodoItemStatus.NeedsAction
? TodoItemStatus.Completed
@@ -24,6 +24,9 @@ export const turnOnOffEntity = (
case "scene":
service = "turn_on";
break;
case "valve":
service = turnOn ? "open_valve" : "close_valve";
break;
default:
service = turnOn ? "turn_on" : "turn_off";
}
@@ -81,6 +81,7 @@ const EDITABLES_FEATURE_TYPES = new Set<UiFeatureTypes>([
"humidifier-modes",
"water-heater-operation-modes",
"lawn-mower-commands",
"climate-fan-modes",
"climate-preset-modes",
"numeric-input",
"update-actions",
@@ -8,6 +8,7 @@ import { stopPropagation } from "../../../../common/dom/stop_propagation";
import { createCloseHeading } from "../../../../components/ha-dialog";
import "../../../../components/ha-icon";
import "../../../../components/ha-select";
import "../../../../components/ha-alert";
import {
fetchConfig,
LovelaceConfig,
@@ -104,8 +105,15 @@ export class HuiDialogSelectView extends LitElement {
})}
</ha-select>`
: ""}
${this._config
? this._config.views.length > 1
${!this._config || (this._config.views || []).length < 1
? html`<ha-alert alert-type="error"
>${this.hass.localize(
this._config
? "ui.panel.lovelace.editor.select_view.no_views"
: "ui.panel.lovelace.editor.select_view.no_config"
)}</ha-alert
>`
: this._config.views.length > 1
? html`
<mwc-list dialogInitialFocus>
${this._config.views.map(
@@ -125,8 +133,7 @@ export class HuiDialogSelectView extends LitElement {
)}
</mwc-list>
`
: ""
: html`<div>No config found.</div>`}
: ""}
<mwc-button
slot="secondaryAction"
@click=${this.closeDialog}
@@ -134,7 +141,11 @@ export class HuiDialogSelectView extends LitElement {
>
${this.hass!.localize("ui.common.cancel")}
</mwc-button>
<mwc-button slot="primaryAction" @click=${this._selectView}>
<mwc-button
slot="primaryAction"
.disabled=${!this._config || (this._config.views || []).length < 1}
@click=${this._selectView}
>
${this._params.actionLabel || this.hass!.localize("ui.common.move")}
</mwc-button>
</ha-dialog>
-1
View File
@@ -250,7 +250,6 @@ class HaPanelProfile extends LitElement {
max-width: 600px;
margin: 0 auto;
padding-bottom: env(safe-area-inset-bottom);
overflow: hidden;
}
.content > * {
+15 -7
View File
@@ -60,8 +60,10 @@ class DialogTodoItemEditor extends LitElement {
this._checked = entry.status === TodoItemStatus.Completed;
this._summary = entry.summary;
this._description = entry.description || "";
this._due = entry.due ? new Date(entry.due) : undefined;
this._hasTime = entry.due?.includes("T") || false;
this._due = entry.due
? new Date(this._hasTime ? entry.due : `${entry.due}T00:00:00`)
: undefined;
} else {
this._hasTime = false;
this._checked = false;
@@ -101,9 +103,9 @@ class DialogTodoItemEditor extends LitElement {
scrimClickAction
.heading=${createCloseHeading(
this.hass,
isCreate
? this.hass.localize("ui.components.todo.item.add")
: this._summary
this.hass.localize(
`ui.components.todo.item.${isCreate ? "add" : "edit"}`
)
)}
>
<div class="content">
@@ -324,14 +326,20 @@ class DialogTodoItemEditor extends LitElement {
(this._todoListSupportsFeature(
TodoListEntityFeature.SET_DESCRIPTION_ON_ITEM
)
? // backend should accept null to clear the field, but it doesn't now
null
? null
: undefined),
due: this._due
? this._hasTime
? this._due.toISOString()
: this._formatDate(this._due)
: null,
: this._todoListSupportsFeature(
TodoListEntityFeature.SET_DUE_DATETIME_ON_ITEM
) ||
this._todoListSupportsFeature(
TodoListEntityFeature.SET_DUE_DATE_ON_ITEM
)
? null
: undefined,
status: this._checked
? TodoItemStatus.Completed
: TodoItemStatus.NeedsAction,
@@ -177,11 +177,20 @@ export class HaStateControlClimateTemperature extends LitElement {
const action = this.stateObj.attributes.hvac_action;
const isTemperatureDisplayed =
(this.stateObj.attributes.current_temperature != null &&
this.showCurrentAsPrimary) ||
((this._supportsTargetTemperature ||
this._supportsTargetTemperatureRange) &&
!this.showCurrentAsPrimary);
return html`
<p class="label">
${action
${action && action !== "off"
? this.hass.formatEntityAttributeValue(this.stateObj, "hvac_action")
: this.hass.formatEntityState(this.stateObj)}
: isTemperatureDisplayed
? this.hass.formatEntityState(this.stateObj)
: nothing}
</p>
`;
}
@@ -315,6 +324,14 @@ export class HaStateControlClimateTemperature extends LitElement {
`;
}
if (this.stateObj.state !== UNAVAILABLE) {
return html`
<p class="primary-state">
${this.hass.formatEntityState(this.stateObj)}
</p>
`;
}
return nothing;
}
@@ -373,6 +390,14 @@ export class HaStateControlClimateTemperature extends LitElement {
return html`<p class="label"></p>`;
}
private _renderInfo() {
return html`
<div class="info">
${this._renderLabel()}${this._renderPrimary()}${this._renderSecondary()}
</div>
`;
}
get _supportsTargetTemperature() {
return (
supportsFeature(this.stateObj, ClimateEntityFeature.TARGET_TEMPERATURE) &&
@@ -447,10 +472,7 @@ export class HaStateControlClimateTemperature extends LitElement {
@value-changing=${this._valueChanging}
>
</ha-control-circular-slider>
<div class="info">
${this._renderLabel()}${this._renderPrimary()}${this._renderSecondary()}
</div>
${this._renderTemperatureButtons("value")}
${this._renderInfo()} ${this._renderTemperatureButtons("value")}
</div>
`;
}
@@ -484,9 +506,7 @@ export class HaStateControlClimateTemperature extends LitElement {
@high-changing=${this._valueChanging}
>
</ha-control-circular-slider>
<div class="info">
${this._renderLabel()}${this._renderPrimary()}${this._renderSecondary()}
</div>
${this._renderInfo()}
${this._renderTemperatureButtons(this._selectTargetTemperature, true)}
</div>
`;
@@ -497,6 +517,7 @@ export class HaStateControlClimateTemperature extends LitElement {
class="container${containerSizeClass}"
style=${styleMap({
"--state-color": stateColor,
"--action-color": actionColor,
})}
>
<ha-control-circular-slider
@@ -510,9 +531,7 @@ export class HaStateControlClimateTemperature extends LitElement {
.disabled=${!active}
>
</ha-control-circular-slider>
<div class="info">
${this._renderLabel()} ${this._renderSecondary()}
</div>
${this._renderInfo()}
</div>
`;
}
@@ -105,11 +105,18 @@ export class HaStateControlHumidifierHumidity extends LitElement {
const action = this.stateObj.attributes.action;
const isHumidityDisplayed =
(this.stateObj.attributes.current_humidity != null &&
this.showCurrentAsPrimary) ||
(this._targetHumidity != null && !this.showCurrentAsPrimary);
return html`
<p class="label">
${action
${action && action !== "off"
? this.hass.formatEntityAttributeValue(this.stateObj, "action")
: this.hass.formatEntityState(this.stateObj)}
: isHumidityDisplayed
? this.hass.formatEntityState(this.stateObj)
: nothing}
</p>
`;
}
@@ -144,6 +151,14 @@ export class HaStateControlHumidifierHumidity extends LitElement {
return this._renderTarget(this._targetHumidity!, "big");
}
if (this.stateObj.state !== UNAVAILABLE) {
return html`
<p class="primary-state">
${this.hass.formatEntityState(this.stateObj)}
</p>
`;
}
return nothing;
}
@@ -225,6 +240,14 @@ export class HaStateControlHumidifierHumidity extends LitElement {
`;
}
private _renderInfo() {
return html`
<div class="info">
${this._renderLabel()}${this._renderPrimary()}${this._renderSecondary()}
</div>
`;
}
protected render() {
const stateColor = stateColorCss(this.stateObj);
const active = stateActive(this.stateObj);
@@ -272,10 +295,7 @@ export class HaStateControlHumidifierHumidity extends LitElement {
@value-changing=${this._valueChanging}
>
</ha-control-circular-slider>
<div class="info">
${this._renderLabel()}${this._renderPrimary()}${this._renderSecondary()}
</div>
${this._renderButtons()}
${this._renderInfo()} ${this._renderButtons()}
</div>
`;
}
@@ -284,6 +304,7 @@ export class HaStateControlHumidifierHumidity extends LitElement {
<div
class="container${containerSizeClass}"
style=${styleMap({
"--state-color": stateColor,
"--action-color": actionColor,
})}
>
@@ -296,9 +317,7 @@ export class HaStateControlHumidifierHumidity extends LitElement {
disabled
>
</ha-control-circular-slider>
<div class="info">
${this._renderLabel()} ${this._renderSecondary()}
</div>
${this._renderInfo()}
</div>
`;
}
@@ -54,7 +54,6 @@ export const stateControlCircularSliderStyle = css`
.label.disabled {
color: var(--secondary-text-color);
}
.buttons {
position: absolute;
bottom: 10px;
@@ -67,6 +66,9 @@ export const stateControlCircularSliderStyle = css`
align-items: center;
justify-content: center;
}
.primary-state {
font-size: 36px;
}
.buttons ha-outlined-icon-button {
--md-outlined-icon-button-container-width: 48px;
@@ -77,6 +79,9 @@ export const stateControlCircularSliderStyle = css`
.container.md ha-big-number {
font-size: 44px;
}
.container.md .state {
font-size: 30px;
}
.container.md .info {
margin-top: 12px;
gap: 6px;
@@ -91,6 +96,9 @@ export const stateControlCircularSliderStyle = css`
.container.sm ha-big-number {
font-size: 32px;
}
.container.sm .state {
font-size: 26px;
}
.container.sm .info {
margin-top: 12px;
font-size: 14px;
@@ -107,6 +115,9 @@ export const stateControlCircularSliderStyle = css`
.container.xs ha-big-number {
font-size: 32px;
}
.container.xs .state {
font-size: 16px;
}
.container.xs .info {
margin-top: 12px;
}
+25 -21
View File
@@ -2492,13 +2492,13 @@
"groups": {
"entity": {
"label": "Entity",
"description": "When something happens to an entity"
"description": "When something happens to an entity."
},
"time_location": {
"label": "Time and location",
"description": "When someone enters or leaves a zone, or at a specific time."
},
"other": { "label": "Other" }
"other": { "label": "Other triggers" }
},
"type": {
"calendar": {
@@ -2534,7 +2534,7 @@
"context_user_picked": "User firing event",
"context_user_pick": "Select user",
"description": {
"picker": "When an event is being received (event is an advanced concept in Home Assistant)",
"picker": "When an event is being received (event is an advanced concept in Home Assistant).",
"full": "When {eventTypes} event is fired"
}
},
@@ -2546,7 +2546,7 @@
"enter": "Enter",
"leave": "Leave",
"description": {
"picker": "When an entity created by a geolocation platform appears in or disappears from a zone",
"picker": "When an entity created by a geolocation platform appears in or disappears from a zone.",
"full": "When {source} {event, select, \n enter {enters}\n leave {leaves} other {} \n} {zone} {numberOfZones, plural,\n one {zone}\n other {zones}\n}"
}
},
@@ -2608,7 +2608,7 @@
"updated": "updated"
},
"description": {
"picker": "When a persistent notification is added or removed",
"picker": "When a persistent notification is added or removed.",
"full": "When a persistent notification is updated"
}
},
@@ -2619,7 +2619,7 @@
"sunset": "Sunset",
"offset": "Offset (optional)",
"description": {
"picker": "When the sun sets or rises",
"picker": "When the sun sets or rises.",
"sets": "When the sun sets{hasDuration, select, \n true { offset by {duration}} \n other {}\n }",
"rises": "When the sun rises{hasDuration, select, \n true { offset by {duration}} \n other {}\n }"
}
@@ -2648,7 +2648,7 @@
"value_template": "Value template",
"for": "For",
"description": {
"picker": "When a template is evaluated to true.",
"picker": "When a template evaluates to true.",
"full": "When a template changes from false to true{hasDuration, select, \n true { for {duration}} \n other {}\n }"
}
},
@@ -2669,7 +2669,7 @@
"minutes": "Minutes",
"seconds": "Seconds",
"description": {
"picker": "Periodically, every defined interval of time."
"picker": "Periodically, at a defined interval."
}
},
"webhook": {
@@ -2692,7 +2692,7 @@
"enter": "Enter",
"leave": "Leave",
"description": {
"picker": "When someone (or something) enters or leaves a zone",
"picker": "When someone (or something) enters or leaves a zone.",
"full": "When {entity} {event, select, \n enter {enters}\n leave {leaves} other {} \n} {zone} {numberOfZones, plural,\n one {zone} \n other {zones}\n}"
}
}
@@ -2734,7 +2734,7 @@
"label": "Time and location",
"description": "If someone is in a zone or if the current time is before or after a specified time."
},
"other": { "label": "Other" },
"other": { "label": "Other conditions" },
"building_blocks": {
"label": "Building blocks",
"description": "Build more complex conditions."
@@ -2760,13 +2760,13 @@
"preset_mode": "Preset mode"
},
"description": {
"picker": "If a device is in a certain state. Great way to start."
"picker": "Set of conditions provided by your device. Great way to start."
}
},
"not": {
"label": "Not",
"description": {
"picker": "Test if a condition is not true",
"picker": "Test if a condition is not true.",
"no_conditions": "Test if no condition matches",
"one_condition": "Test if 1 condition does not match",
"full": "Test if none of {count} conditions match"
@@ -2800,7 +2800,7 @@
"label": "[%key:ui::panel::config::automation::editor::triggers::type::state::label%]",
"state": "[%key:ui::panel::config::automation::editor::triggers::type::state::label%]",
"description": {
"picker": "If an entity (or attribute) is in a specific state",
"picker": "If an entity (or attribute) is in a specific state.",
"no_entity": "Confirm state",
"full": "Confirm{hasAttribute, select, \n true { {attribute} of}\n other {}\n} {numberOfEntities, plural,\n zero {an entity is}\n one {{entities} is}\n other {{entities} are}\n} {numberOfStates, plural,\n zero {a state}\n other {{states}}\n}{hasDuration, select, \n true { for {duration}} \n other {}\n }"
}
@@ -2821,7 +2821,7 @@
"label": "[%key:ui::panel::config::automation::editor::triggers::type::template::label%]",
"value_template": "[%key:ui::panel::config::automation::editor::triggers::type::template::value_template%]",
"description": {
"picker": "If a template is evaluated to true.",
"picker": "If a template evaluates to true.",
"full": "Test if template renders a value equal to true"
}
},
@@ -2844,7 +2844,7 @@
"sun": "Sunday"
},
"description": {
"picker": "If the current time is before or after a specified time",
"picker": "If the current time is before or after a specified time.",
"full": "Confirm the {hasTime, select, \n after {time is after {time_after}}\n before {time is before {time_before}}\n after_before {time is after {time_after} and before {time_before}} \n other {}\n }{hasTimeAndDay, select, \n true { and the }\n other {}\n}{hasDay, select, \n true { day is {day}}\n other {}\n}"
}
},
@@ -2862,7 +2862,7 @@
"entity": "[%key:ui::panel::config::automation::editor::triggers::type::zone::entity%]",
"zone": "[%key:ui::panel::config::automation::editor::triggers::type::zone::label%]",
"description": {
"picker": "If someone (or something) is in a zone",
"picker": "If someone (or something) is in a zone.",
"full": "Confirm {entity} {numberOfEntities, plural,\n one {is}\n other {are}\n} in {zone} {numberOfZones, plural,\n one {zone} \n other {zones}\n} "
}
}
@@ -2899,7 +2899,7 @@
"continue_on_error": "Continue on error",
"groups": {
"helpers": { "label": "Helpers" },
"other": { "label": "Other" },
"other": { "label": "Other actions" },
"building_blocks": {
"label": "Building blocks",
"description": "Build more complex sequences of actions."
@@ -2914,6 +2914,7 @@
"description": {
"service_based_on_template": "Call a service based on a template on {targets}",
"service_based_on_name": "Call a service ''{name}'' on {targets}",
"service_name": "{domain} ''{name}'' on {targets}",
"service": "Call a service",
"target_template": "templated {name}",
"target_unknown_entity": "unknown entity",
@@ -2990,7 +2991,8 @@
"flash": "Flash"
},
"description": {
"picker": "Do something on a device. Great way to start."
"picker": "Do something on a device. Great way to start.",
"no_device": "Device action"
}
},
"activate_scene": {
@@ -3069,14 +3071,14 @@
"response_variable": "The name of the variable to use as response",
"error": "Stop because of an unexpected error",
"description": {
"picker": "Stop the sequence of actions",
"picker": "Stop the sequence of actions.",
"full": "Stop {hasReason, select, \n true { because: {reason}} \n other {}\n }"
}
},
"parallel": {
"label": "Run in parallel",
"description": {
"picker": "perform a sequence of actions in parallel.",
"picker": "Perform a sequence of actions in parallel.",
"full": "Run {number} {number, plural,\n one {action}\n other {actions}\n} in parallel"
}
},
@@ -5018,7 +5020,9 @@
"select_view": {
"header": "Choose a view",
"dashboard_label": "Dashboard",
"views_label": "View"
"views_label": "View",
"no_config": "No config found.",
"no_views": "No views in this dashboard."
},
"suggest_card": {
"header": "We created a suggestion for you",
+70 -73
View File
@@ -62,26 +62,26 @@ __metadata:
languageName: node
linkType: hard
"@babel/core@npm:7.23.6, @babel/core@npm:^7.11.1, @babel/core@npm:^7.12.3":
version: 7.23.6
resolution: "@babel/core@npm:7.23.6"
"@babel/core@npm:7.23.7, @babel/core@npm:^7.11.1, @babel/core@npm:^7.12.3":
version: 7.23.7
resolution: "@babel/core@npm:7.23.7"
dependencies:
"@ampproject/remapping": "npm:^2.2.0"
"@babel/code-frame": "npm:^7.23.5"
"@babel/generator": "npm:^7.23.6"
"@babel/helper-compilation-targets": "npm:^7.23.6"
"@babel/helper-module-transforms": "npm:^7.23.3"
"@babel/helpers": "npm:^7.23.6"
"@babel/helpers": "npm:^7.23.7"
"@babel/parser": "npm:^7.23.6"
"@babel/template": "npm:^7.22.15"
"@babel/traverse": "npm:^7.23.6"
"@babel/traverse": "npm:^7.23.7"
"@babel/types": "npm:^7.23.6"
convert-source-map: "npm:^2.0.0"
debug: "npm:^4.1.0"
gensync: "npm:^1.0.0-beta.2"
json5: "npm:^2.2.3"
semver: "npm:^6.3.1"
checksum: a72ba71d2f557d09ff58a5f0846344b9cea9dfcbd7418729a3a74d5b0f37a5ca024942fef4d19f248de751928a1be3d5cb0488746dd8896009dd55b974bb552e
checksum: 956841695ea801c8b4196d01072e6c1062335960715a6fcfd4009831003b526b00627c78b373ed49b1658c3622c71142f7ff04235fe839cac4a1a25ed51b90aa
languageName: node
linkType: hard
@@ -128,9 +128,9 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-create-class-features-plugin@npm:^7.22.15, @babel/helper-create-class-features-plugin@npm:^7.23.6":
version: 7.23.6
resolution: "@babel/helper-create-class-features-plugin@npm:7.23.6"
"@babel/helper-create-class-features-plugin@npm:^7.22.15, @babel/helper-create-class-features-plugin@npm:^7.23.6, @babel/helper-create-class-features-plugin@npm:^7.23.7":
version: 7.23.7
resolution: "@babel/helper-create-class-features-plugin@npm:7.23.7"
dependencies:
"@babel/helper-annotate-as-pure": "npm:^7.22.5"
"@babel/helper-environment-visitor": "npm:^7.22.20"
@@ -143,7 +143,7 @@ __metadata:
semver: "npm:^6.3.1"
peerDependencies:
"@babel/core": ^7.0.0
checksum: 5e0cff67a6809d2285215057be45de9dd8900b91e3526fad5eac79023c1d6bee32aed1a04fcdf0e4d99ee4bd49ea5459cb98260c13222edf3bb983621bb452f4
checksum: c8b3ef58fca399a25f00d703b0fb2ac1d86642d9e3bd7af04df77857641ed08aaca042ffb271ef93771f9272481fd1cf102a9bddfcee407fb126c927deeef6a7
languageName: node
linkType: hard
@@ -335,14 +335,14 @@ __metadata:
languageName: node
linkType: hard
"@babel/helpers@npm:^7.23.6":
version: 7.23.6
resolution: "@babel/helpers@npm:7.23.6"
"@babel/helpers@npm:^7.23.7":
version: 7.23.7
resolution: "@babel/helpers@npm:7.23.7"
dependencies:
"@babel/template": "npm:^7.22.15"
"@babel/traverse": "npm:^7.23.6"
"@babel/traverse": "npm:^7.23.7"
"@babel/types": "npm:^7.23.6"
checksum: 2a85fd2bcbc15a6c94dbe7b9e94d8920f9de76d164179d6895fee89c4339079d9e3e56f572bf19b5e7d1e6f1997d7fbaeaa686b47d35136852631dfd09e85c2f
checksum: ec07061dc871d406ed82c8757c4d7a510aaf15145799fb0a2c3bd3c72ca101fe82a02dd5f83ca604fbbba5de5408dd731bb1452150562bed4f3b0a2846f81f61
languageName: node
linkType: hard
@@ -390,31 +390,28 @@ __metadata:
languageName: node
linkType: hard
"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.23.3":
version: 7.23.3
resolution: "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:7.23.3"
"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.23.7":
version: 7.23.7
resolution: "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:7.23.7"
dependencies:
"@babel/helper-environment-visitor": "npm:^7.22.20"
"@babel/helper-plugin-utils": "npm:^7.22.5"
peerDependencies:
"@babel/core": ^7.0.0
checksum: 6e13f14949eb943d33cf4d3775a7195fa93c92851dfb648931038e9eb92a9b1709fdaa5a0ff6cf063cfcd68b3e52d280f3ebc0f3085b3e006e64dd6196ecb72a
checksum: 3b0c9554cd0048e6e7341d7b92f29d400dbc6a5a4fc2f86dbed881d32e02ece9b55bc520387bae2eac22a5ab38a0b205c29b52b181294d99b4dd75e27309b548
languageName: node
linkType: hard
"@babel/plugin-proposal-decorators@npm:7.23.6":
version: 7.23.6
resolution: "@babel/plugin-proposal-decorators@npm:7.23.6"
"@babel/plugin-proposal-decorators@npm:7.23.7":
version: 7.23.7
resolution: "@babel/plugin-proposal-decorators@npm:7.23.7"
dependencies:
"@babel/helper-create-class-features-plugin": "npm:^7.23.6"
"@babel/helper-create-class-features-plugin": "npm:^7.23.7"
"@babel/helper-plugin-utils": "npm:^7.22.5"
"@babel/helper-replace-supers": "npm:^7.22.20"
"@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5"
"@babel/helper-split-export-declaration": "npm:^7.22.6"
"@babel/plugin-syntax-decorators": "npm:^7.23.3"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 091796967ad728c7a00ae60c978d20f17cea08fa37bf977568880324b2619f8ce11b4a3797ef1b3137085fcb890639c49462f6be19f7cc3707e17e057df11c75
checksum: 1fc506b113fa204323537b3299686641c29b7626f05aa098c68a818da65ce657d2398c49aea69af91c45bbdfca6086424e28d283729dba401eb93c8a16826c95
languageName: node
linkType: hard
@@ -670,9 +667,9 @@ __metadata:
languageName: node
linkType: hard
"@babel/plugin-transform-async-generator-functions@npm:^7.23.4":
version: 7.23.4
resolution: "@babel/plugin-transform-async-generator-functions@npm:7.23.4"
"@babel/plugin-transform-async-generator-functions@npm:^7.23.7":
version: 7.23.7
resolution: "@babel/plugin-transform-async-generator-functions@npm:7.23.7"
dependencies:
"@babel/helper-environment-visitor": "npm:^7.22.20"
"@babel/helper-plugin-utils": "npm:^7.22.5"
@@ -680,7 +677,7 @@ __metadata:
"@babel/plugin-syntax-async-generators": "npm:^7.8.4"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: e2fc132c9033711d55209f4781e1fc73f0f4da5e0ca80a2da73dec805166b73c92a6e83571a8994cd2c893a28302e24107e90856202b24781bab734f800102bb
checksum: b1f66b23423933c27336b1161ac92efef46683321caea97e2255a666f992979376f47a5559f64188d3831fa66a4b24c2a7a40838cc0e9737e90eebe20e8e6372
languageName: node
linkType: hard
@@ -1137,19 +1134,19 @@ __metadata:
languageName: node
linkType: hard
"@babel/plugin-transform-runtime@npm:7.23.6":
version: 7.23.6
resolution: "@babel/plugin-transform-runtime@npm:7.23.6"
"@babel/plugin-transform-runtime@npm:7.23.7":
version: 7.23.7
resolution: "@babel/plugin-transform-runtime@npm:7.23.7"
dependencies:
"@babel/helper-module-imports": "npm:^7.22.15"
"@babel/helper-plugin-utils": "npm:^7.22.5"
babel-plugin-polyfill-corejs2: "npm:^0.4.6"
babel-plugin-polyfill-corejs3: "npm:^0.8.5"
babel-plugin-polyfill-regenerator: "npm:^0.5.3"
babel-plugin-polyfill-corejs2: "npm:^0.4.7"
babel-plugin-polyfill-corejs3: "npm:^0.8.7"
babel-plugin-polyfill-regenerator: "npm:^0.5.4"
semver: "npm:^6.3.1"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 54e540ec04f05f664c69db36a78b5db56b0d3e25bbe34d7f11cfe21cc5aad5240661f5ada630c7b2abcae99d229851aafbb6b2218cf285771379e0844a3f24a9
checksum: 1e0b21c943e565e6a2a859991059f5b5a8b917689aab9b3beb172babece1843b42f9ae9ff9913f01134fb201fd67ac2831559578949c7287e7c782e6d6740de8
languageName: node
linkType: hard
@@ -1270,9 +1267,9 @@ __metadata:
languageName: node
linkType: hard
"@babel/preset-env@npm:7.23.6, @babel/preset-env@npm:^7.11.0":
version: 7.23.6
resolution: "@babel/preset-env@npm:7.23.6"
"@babel/preset-env@npm:7.23.7, @babel/preset-env@npm:^7.11.0":
version: 7.23.7
resolution: "@babel/preset-env@npm:7.23.7"
dependencies:
"@babel/compat-data": "npm:^7.23.5"
"@babel/helper-compilation-targets": "npm:^7.23.6"
@@ -1280,7 +1277,7 @@ __metadata:
"@babel/helper-validator-option": "npm:^7.23.5"
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.23.3"
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.23.3"
"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "npm:^7.23.3"
"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "npm:^7.23.7"
"@babel/plugin-proposal-private-property-in-object": "npm:7.21.0-placeholder-for-preset-env.2"
"@babel/plugin-syntax-async-generators": "npm:^7.8.4"
"@babel/plugin-syntax-class-properties": "npm:^7.12.13"
@@ -1301,7 +1298,7 @@ __metadata:
"@babel/plugin-syntax-top-level-await": "npm:^7.14.5"
"@babel/plugin-syntax-unicode-sets-regex": "npm:^7.18.6"
"@babel/plugin-transform-arrow-functions": "npm:^7.23.3"
"@babel/plugin-transform-async-generator-functions": "npm:^7.23.4"
"@babel/plugin-transform-async-generator-functions": "npm:^7.23.7"
"@babel/plugin-transform-async-to-generator": "npm:^7.23.3"
"@babel/plugin-transform-block-scoped-functions": "npm:^7.23.3"
"@babel/plugin-transform-block-scoping": "npm:^7.23.4"
@@ -1349,14 +1346,14 @@ __metadata:
"@babel/plugin-transform-unicode-regex": "npm:^7.23.3"
"@babel/plugin-transform-unicode-sets-regex": "npm:^7.23.3"
"@babel/preset-modules": "npm:0.1.6-no-external-plugins"
babel-plugin-polyfill-corejs2: "npm:^0.4.6"
babel-plugin-polyfill-corejs3: "npm:^0.8.5"
babel-plugin-polyfill-regenerator: "npm:^0.5.3"
babel-plugin-polyfill-corejs2: "npm:^0.4.7"
babel-plugin-polyfill-corejs3: "npm:^0.8.7"
babel-plugin-polyfill-regenerator: "npm:^0.5.4"
core-js-compat: "npm:^3.31.0"
semver: "npm:^6.3.1"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: b47e9e7cdb0d31b2a6919ffb1b767f8159a69b000e257c1dad1121dea8c42d7ec12a892a691d1a8e90cde86edd41b017254574ec6b82a984013bb3c9e3df2b36
checksum: 2059dee350c39aba0a1f128d00ccfd7abf97f92a9b661f4db07a96c11d91c928a9f1df39477583f068090627bff571b4415f1a4f94008d29f6ad8b124e69804e
languageName: node
linkType: hard
@@ -1395,12 +1392,12 @@ __metadata:
languageName: node
linkType: hard
"@babel/runtime@npm:7.23.6, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4":
version: 7.23.6
resolution: "@babel/runtime@npm:7.23.6"
"@babel/runtime@npm:7.23.7, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4":
version: 7.23.7
resolution: "@babel/runtime@npm:7.23.7"
dependencies:
regenerator-runtime: "npm:^0.14.0"
checksum: 4c4ab16f0361c59fb23956e4d0a29935f1f8a64aa8dd37876ce38355b6f4d8f0e54237aacb89c73b1532def60539ddde2d651523c8fa887b30b19a8cf0c465b0
checksum: b29cf3ca6277aea8c5c823d9b86e7f7153757f07eaaa81d726f125de00ac0e7451c90845770f919826a94ade8f71a6bda9c0421410dfcc285ee17a40f8f8ca00
languageName: node
linkType: hard
@@ -1415,9 +1412,9 @@ __metadata:
languageName: node
linkType: hard
"@babel/traverse@npm:^7.23.6":
version: 7.23.6
resolution: "@babel/traverse@npm:7.23.6"
"@babel/traverse@npm:^7.23.7":
version: 7.23.7
resolution: "@babel/traverse@npm:7.23.7"
dependencies:
"@babel/code-frame": "npm:^7.23.5"
"@babel/generator": "npm:^7.23.6"
@@ -1429,7 +1426,7 @@ __metadata:
"@babel/types": "npm:^7.23.6"
debug: "npm:^4.3.1"
globals: "npm:^11.1.0"
checksum: ee4434a3ce792ee8956b64d76843caa1dda4779bb621ed9f951dd3551965bf1f292f097011c9730ecbc0b57f02434b1fa5a771610a2ef570726b0df0fc3332d9
checksum: 3215e59429963c8dac85c26933372cdd322952aa9930e4bc5ef2d0e4bd7a1510d1ecf8f8fd860ace5d4d9fe496d23805a1ea019a86410aee4111de5f63ee84f9
languageName: node
linkType: hard
@@ -1451,12 +1448,12 @@ __metadata:
languageName: node
linkType: hard
"@bundle-stats/plugin-webpack-filter@npm:4.8.3":
version: 4.8.3
resolution: "@bundle-stats/plugin-webpack-filter@npm:4.8.3"
"@bundle-stats/plugin-webpack-filter@npm:4.8.4":
version: 4.8.4
resolution: "@bundle-stats/plugin-webpack-filter@npm:4.8.4"
peerDependencies:
core-js: ^3.0.0
checksum: a832be786197b0cd534e4ccb35cd7230f322b2b77697f5f4ac0b098e51f7867bff0364020f6015d31f2963619e1408933454841baac035452a9fec37d921f841
checksum: 7a33a19094fb32579ca215cfee7c536a7e524558e6a17bee0376377691b4957bd1346531dfe3b01e8daf364acef75e1926f3d1aed76d986be79ba374157782fb
languageName: node
linkType: hard
@@ -6003,7 +6000,7 @@ __metadata:
languageName: node
linkType: hard
"babel-plugin-polyfill-corejs2@npm:^0.4.6":
"babel-plugin-polyfill-corejs2@npm:^0.4.7":
version: 0.4.7
resolution: "babel-plugin-polyfill-corejs2@npm:0.4.7"
dependencies:
@@ -6016,7 +6013,7 @@ __metadata:
languageName: node
linkType: hard
"babel-plugin-polyfill-corejs3@npm:^0.8.5":
"babel-plugin-polyfill-corejs3@npm:^0.8.7":
version: 0.8.7
resolution: "babel-plugin-polyfill-corejs3@npm:0.8.7"
dependencies:
@@ -6028,7 +6025,7 @@ __metadata:
languageName: node
linkType: hard
"babel-plugin-polyfill-regenerator@npm:^0.5.3":
"babel-plugin-polyfill-regenerator@npm:^0.5.4":
version: 0.5.4
resolution: "babel-plugin-polyfill-regenerator@npm:0.5.4"
dependencies:
@@ -9514,15 +9511,15 @@ __metadata:
version: 0.0.0-use.local
resolution: "home-assistant-frontend@workspace:."
dependencies:
"@babel/core": "npm:7.23.6"
"@babel/core": "npm:7.23.7"
"@babel/helper-define-polyfill-provider": "npm:0.4.4"
"@babel/plugin-proposal-decorators": "npm:7.23.6"
"@babel/plugin-transform-runtime": "npm:7.23.6"
"@babel/preset-env": "npm:7.23.6"
"@babel/plugin-proposal-decorators": "npm:7.23.7"
"@babel/plugin-transform-runtime": "npm:7.23.7"
"@babel/preset-env": "npm:7.23.7"
"@babel/preset-typescript": "npm:7.23.3"
"@babel/runtime": "npm:7.23.6"
"@babel/runtime": "npm:7.23.7"
"@braintree/sanitize-url": "npm:7.0.0"
"@bundle-stats/plugin-webpack-filter": "npm:4.8.3"
"@bundle-stats/plugin-webpack-filter": "npm:4.8.4"
"@codemirror/autocomplete": "npm:6.11.1"
"@codemirror/commands": "npm:6.3.2"
"@codemirror/language": "npm:6.9.3"
@@ -9679,7 +9676,7 @@ __metadata:
luxon: "npm:3.4.4"
magic-string: "npm:0.30.5"
map-stream: "npm:0.0.7"
marked: "npm:11.1.0"
marked: "npm:11.1.1"
memoize-one: "npm:6.0.0"
mocha: "npm:10.2.0"
node-vibrant: "npm:3.2.1-alpha.1"
@@ -11655,12 +11652,12 @@ __metadata:
languageName: node
linkType: hard
"marked@npm:11.1.0":
version: 11.1.0
resolution: "marked@npm:11.1.0"
"marked@npm:11.1.1":
version: 11.1.1
resolution: "marked@npm:11.1.1"
bin:
marked: bin/marked.js
checksum: 4636b16283c1963a715e97578d9fd91588b11949276e633a4de53dc408bcdab7b846d2b5c2cf3239f6d2dc8affe5294a0895954b5e3d9562d77301d8847a8915
checksum: c2e15a330ac75cca2e12e25aae09985a78ad7e96a84418964dcdd3ee776764a38812dc0e94e9fcbacac43113d1650ca7946f9dc0bab800d72181e56a37e7631e
languageName: node
linkType: hard