diff --git a/pyproject.toml b/pyproject.toml
index 3c695ec8291b..55d7a058ef82 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
-version = "20231026.0"
+version = "20231027.0"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"
diff --git a/src/common/string/slugify.ts b/src/common/string/slugify.ts
index ddf378059cbf..b7ddfed77c90 100644
--- a/src/common/string/slugify.ts
+++ b/src/common/string/slugify.ts
@@ -14,7 +14,7 @@ export const slugify = (value: string, delimiter = "_") => {
.toString()
.toLowerCase()
.replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters
- .replace(/(?<=\d),(?=\d)/g, "") // Remove Commas between numbers
+ .replace(/(\d),(?=\d)/g, "$1") // Remove Commas between numbers
.replace(/[^a-z0-9]+/g, delimiter) // Replace all non-word characters
.replace(new RegExp(`(${delimiter})\\1+`, "g"), "$1") // Replace multiple delimiters with single delimiter
.replace(new RegExp(`^${delimiter}+`), "") // Trim delimiter from start of text
diff --git a/src/components/ha-button.ts b/src/components/ha-button.ts
index 473d08dd1589..b58f32c8b5c3 100644
--- a/src/components/ha-button.ts
+++ b/src/components/ha-button.ts
@@ -20,6 +20,10 @@ export class HaButton extends Button {
.trailing-icon {
display: flex;
}
+ .slot-container {
+ width: 100%;
+ overflow: hidden;
+ }
`,
];
}
diff --git a/src/components/ha-date-range-picker.ts b/src/components/ha-date-range-picker.ts
index 966d429d408c..ed5403872f07 100644
--- a/src/components/ha-date-range-picker.ts
+++ b/src/components/ha-date-range-picker.ts
@@ -46,6 +46,8 @@ export class HaDateRangePicker extends LitElement {
@property() public ranges?: DateRangePickerRanges | false;
+ @state() private _ranges?: DateRangePickerRanges;
+
@property() public autoApply = false;
@property() public timePicker = true;
@@ -93,7 +95,7 @@ export class HaDateRangePicker extends LitElement {
}
);
- this.ranges = {
+ this._ranges = {
[this.hass.localize("ui.components.date-range-picker.ranges.today")]: [
calcDate(today, startOfDay, this.hass.locale, this.hass.config, {
weekStartsOn,
@@ -206,15 +208,15 @@ export class HaDateRangePicker extends LitElement {
.path=${mdiCalendar}
>`}
- ${this.ranges
+ ${this.ranges !== false && (this.ranges || this._ranges)
? html`
- ${Object.keys(this.ranges).map(
- (name) => html` ${name} `
+ ${Object.keys(this.ranges || this._ranges!).map(
+ (name) => html`${name}`
)}
`
@@ -234,7 +236,9 @@ export class HaDateRangePicker extends LitElement {
}
private _setDateRange(ev: CustomEvent) {
- const dateRange = Object.values(this.ranges!)[ev.detail.index];
+ const dateRange = Object.values(this.ranges || this._ranges!)[
+ ev.detail.index
+ ];
const dateRangePicker = this._dateRangePicker;
dateRangePicker.clickRange(dateRange);
dateRangePicker.clickedApply();
diff --git a/src/components/ha-dialog.ts b/src/components/ha-dialog.ts
index f061931322a4..feaceec93daf 100644
--- a/src/components/ha-dialog.ts
+++ b/src/components/ha-dialog.ts
@@ -94,6 +94,8 @@ export class HaDialog extends DialogBase {
}
.mdc-dialog__title {
padding: 24px 24px 0 24px;
+ text-overflow: ellipsis;
+ overflow: hidden;
}
.mdc-dialog__actions {
padding: 12px 24px 12px 24px;
diff --git a/src/panels/config/entities/entity-registry-settings-editor.ts b/src/panels/config/entities/entity-registry-settings-editor.ts
index 42c64edd64df..0241542addea 100644
--- a/src/panels/config/entities/entity-registry-settings-editor.ts
+++ b/src/panels/config/entities/entity-registry-settings-editor.ts
@@ -200,6 +200,8 @@ export class EntityRegistrySettingsEditor extends LitElement {
this._name = this.entry.name || "";
this._icon = this.entry.icon || "";
+ this._deviceClass =
+ this.entry.device_class || this.entry.original_device_class;
this._origEntityId = this.entry.entity_id;
this._areaId = this.entry.area_id;
this._entityId = this.entry.entity_id;
diff --git a/src/panels/config/lovelace/resources/ha-config-lovelace-resources.ts b/src/panels/config/lovelace/resources/ha-config-lovelace-resources.ts
index a1733e094040..5dc3abbcfe75 100644
--- a/src/panels/config/lovelace/resources/ha-config-lovelace-resources.ts
+++ b/src/panels/config/lovelace/resources/ha-config-lovelace-resources.ts
@@ -1,5 +1,12 @@
import { mdiPlus } from "@mdi/js";
-import { html, LitElement, PropertyValues, TemplateResult } from "lit";
+import {
+ css,
+ CSSResultGroup,
+ html,
+ LitElement,
+ PropertyValues,
+ TemplateResult,
+} from "lit";
import { customElement, property, state } from "lit/decorators";
import memoize from "memoize-one";
import { stringCompare } from "../../../../common/string/compare";
@@ -7,6 +14,7 @@ import {
DataTableColumnContainer,
RowClickedEvent,
} from "../../../../components/data-table/ha-data-table";
+import "../../../../components/ha-card";
import "../../../../components/ha-fab";
import "../../../../components/ha-svg-icon";
import {
@@ -21,7 +29,9 @@ import {
showConfirmationDialog,
} from "../../../../dialogs/generic/show-dialog-box";
import "../../../../layouts/hass-loading-screen";
+import "../../../../layouts/hass-subpage";
import "../../../../layouts/hass-tabs-subpage-data-table";
+import { haStyle } from "../../../../resources/styles";
import { HomeAssistant, Route } from "../../../../types";
import { loadLovelaceResources } from "../../../lovelace/common/load-resources";
import { lovelaceTabs } from "../ha-config-lovelace";
@@ -72,6 +82,36 @@ export class HaConfigLovelaceRescources extends LitElement {
return html` `;
}
+ if (this.hass.config.safe_mode) {
+ return html`
+
+
+
+
+
+ ${this.hass.localize(
+ "ui.panel.config.lovelace.resources.unavailable"
+ )}
+
+
+ ${this.hass.localize(
+ "ui.panel.config.lovelace.resources.unavailable_safe_mode"
+ )}
+
+
+
+
+
+ `;
+ }
+
return html`
@@ -347,7 +347,8 @@ class HaPanelDevState extends LitElement {
ev.preventDefault();
}
- private _entityIdChanged() {
+ private _entityIdChanged(ev: CustomEvent) {
+ this._entityId = ev.detail.value;
if (!this._entityId) {
this._entity = undefined;
this._state = "";
diff --git a/src/panels/energy/ha-panel-energy.ts b/src/panels/energy/ha-panel-energy.ts
index 74ae7dfd7b6c..c9cf85f45bce 100644
--- a/src/panels/energy/ha-panel-energy.ts
+++ b/src/panels/energy/ha-panel-energy.ts
@@ -48,6 +48,9 @@ class PanelEnergy extends LitElement {
if (oldHass?.locale !== this.hass.locale) {
this._setLovelace();
}
+ if (oldHass && oldHass.localize !== this.hass.localize) {
+ this._reloadView();
+ }
}
protected render(): TemplateResult {
diff --git a/src/panels/lovelace/cards/hui-todo-list-card.ts b/src/panels/lovelace/cards/hui-todo-list-card.ts
index f2d38bc64f83..68af5f2b5914 100644
--- a/src/panels/lovelace/cards/hui-todo-list-card.ts
+++ b/src/panels/lovelace/cards/hui-todo-list-card.ts
@@ -135,10 +135,14 @@ export class HuiTodoListCard
public hassSubscribe(): Promise[] {
return [
- this.hass!.connection.subscribeEvents(
- () => this._fetchData(),
- "shopping_list_updated"
- ),
+ this.hass!.connection.subscribeEvents(() => {
+ if (
+ this._entityId &&
+ this.hass!.entities[this._entityId]?.platform === "shopping_list"
+ ) {
+ this._fetchData();
+ }
+ }, "shopping_list_updated"),
];
}
@@ -159,6 +163,15 @@ export class HuiTodoListCard
) {
applyThemesOnElement(this, this.hass.themes, this._config.theme);
}
+
+ if (
+ this._entityId &&
+ oldHass &&
+ oldHass.states[this._entityId] !== this.hass.states[this._entityId] &&
+ this.hass.entities[this._entityId]?.platform !== "shopping_list"
+ ) {
+ this._fetchData();
+ }
}
protected render() {
diff --git a/src/panels/lovelace/common/load-resources.ts b/src/panels/lovelace/common/load-resources.ts
index d8d0f5a1b3a9..3285d35d5272 100644
--- a/src/panels/lovelace/common/load-resources.ts
+++ b/src/panels/lovelace/common/load-resources.ts
@@ -10,11 +10,6 @@ export const loadLovelaceResources = (
resources: NonNullable,
hass: HomeAssistant
) => {
- // Don't load ressources on safe mode
- // Sometimes, hass.config is null but it should not.
- if (hass.config?.safe_mode) {
- return;
- }
resources.forEach((resource) => {
const normalizedUrl = new URL(
resource.url,
diff --git a/src/panels/lovelace/components/hui-energy-period-selector.ts b/src/panels/lovelace/components/hui-energy-period-selector.ts
index aa78828abfe1..c06d3ab21063 100644
--- a/src/panels/lovelace/components/hui-energy-period-selector.ts
+++ b/src/panels/lovelace/components/hui-energy-period-selector.ts
@@ -65,7 +65,7 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
@state() _endDate?: Date;
- @state() private _ranges?: DateRangePickerRanges;
+ @state() private _ranges: DateRangePickerRanges = {};
@state() private _compare = false;
diff --git a/src/panels/todo/ha-panel-todo.ts b/src/panels/todo/ha-panel-todo.ts
index 8dbdd77e8c7c..abafcbf6035c 100644
--- a/src/panels/todo/ha-panel-todo.ts
+++ b/src/panels/todo/ha-panel-todo.ts
@@ -2,10 +2,10 @@ import { ResizeController } from "@lit-labs/observers/resize-controller";
import "@material/mwc-list";
import {
mdiChevronDown,
+ mdiCommentProcessingOutline,
mdiDelete,
mdiDotsVertical,
mdiInformationOutline,
- mdiMicrophone,
mdiPlus,
} from "@mdi/js";
import {
@@ -173,11 +173,13 @@ class PanelTodo extends LitElement {
.x=${this.mobile ? 0 : undefined}
>
- ${this._entityId
- ? this._entityId in this.hass.states
- ? computeStateName(this.hass.states[this._entityId])
- : this._entityId
- : ""}
+
+ ${this._entityId
+ ? this._entityId in this.hass.states
+ ? computeStateName(this.hass.states[this._entityId])
+ : this._entityId
+ : ""}
+
`
- : "Lists"}
+ : this.hass.localize("panel.todo")}
${listItems}
@@ -216,8 +218,9 @@ class PanelTodo extends LitElement {
: nothing}
-
- ${this.hass.localize("ui.panel.todo.start_conversation")}
+
+
+ ${this.hass.localize("ui.panel.todo.assist")}
${entityRegistryEntry?.platform === "local_todo"
? html`
@@ -335,11 +338,18 @@ class PanelTodo extends LitElement {
:host([mobile]) .lists {
--mdc-menu-min-width: 100vw;
}
+ :host(:not([mobile])) .lists ha-list-item {
+ max-width: calc(100vw - 120px);
+ }
:host([mobile]) ha-button-menu {
--mdc-shape-medium: 0 0 var(--mdc-shape-medium)
var(--mdc-shape-medium);
}
+ ha-button-menu {
+ max-width: 100%;
+ }
ha-button-menu ha-button {
+ max-width: 100%;
--mdc-theme-primary: currentColor;
--mdc-typography-button-text-transform: none;
--mdc-typography-button-font-size: var(
@@ -360,6 +370,13 @@ class PanelTodo extends LitElement {
);
--button-height: 40px;
}
+ ha-button-menu ha-button div {
+ text-overflow: ellipsis;
+ width: 100%;
+ overflow: hidden;
+ white-space: nowrap;
+ display: block;
+ }
`,
];
}
diff --git a/src/state/connection-mixin.ts b/src/state/connection-mixin.ts
index 08113c821a2c..c3cced5705c8 100644
--- a/src/state/connection-mixin.ts
+++ b/src/state/connection-mixin.ts
@@ -274,6 +274,10 @@ export const connectionMixin = >(
// on reconnect always fetch config as we might miss an update while we were disconnected
// @ts-ignore
this.hass!.callWS({ type: "get_config" }).then((config: HassConfig) => {
+ if (config.safe_mode) {
+ // @ts-ignore Firefox supports forceGet
+ location.reload(true);
+ }
this._updateHass({ config });
this.checkDataBaseMigration();
});
diff --git a/src/translations/en.json b/src/translations/en.json
index c99285a8c0b9..e3047241c3d5 100644
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -2140,6 +2140,8 @@
"js": "JavaScript file (deprecated)",
"module": "JavaScript module"
},
+ "unavailable": "Resources unavailable",
+ "unavailable_safe_mode": "Resources are not available in safe mode",
"picker": {
"headers": {
"url": "URL",
@@ -4520,7 +4522,6 @@
"never_triggered": "Never triggered"
},
"todo-list": {
- "lists": "To-do Lists",
"checked_items": "Checked items",
"clear_items": "Clear checked items",
"add_item": "Add item",
@@ -5094,7 +5095,7 @@
"description": "The Sensor card gives you a quick overview of your sensors state with an optional graph to visualize change over time."
},
"todo-list": {
- "name": "Todo list",
+ "name": "To-do list",
"description": "The to-do list card allows you to add, edit, check-off, and clear items from your to-do list.",
"integration_not_loaded": "This card requires the `todo` integration to be set up."
},
@@ -5515,7 +5516,7 @@
}
},
"todo": {
- "start_conversation": "Start conversation",
+ "assist": "[%key:ui::panel::lovelace::menu::assist%]",
"create_list": "Create list",
"delete_list": "Delete list",
"information": "Information",