From 1aa6bd55777319ae2cc47c833e30c69d464d722a Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 4 Dec 2022 05:38:43 -0800 Subject: [PATCH 01/29] Fix recurrence rule generation for UNTIL rules (#14541) --- src/panels/calendar/dialog-calendar-event-editor.ts | 1 + src/panels/calendar/ha-recurrence-rule-editor.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/panels/calendar/dialog-calendar-event-editor.ts b/src/panels/calendar/dialog-calendar-event-editor.ts index 790312570f..d673577df5 100644 --- a/src/panels/calendar/dialog-calendar-event-editor.ts +++ b/src/panels/calendar/dialog-calendar-event-editor.ts @@ -189,6 +189,7 @@ class DialogCalendarEventEditor extends LitElement { diff --git a/src/panels/calendar/ha-recurrence-rule-editor.ts b/src/panels/calendar/ha-recurrence-rule-editor.ts index b2c97a9aaa..08e239b62a 100644 --- a/src/panels/calendar/ha-recurrence-rule-editor.ts +++ b/src/panels/calendar/ha-recurrence-rule-editor.ts @@ -34,6 +34,8 @@ export class RecurrenceRuleEditor extends LitElement { @property({ attribute: false }) public locale!: HomeAssistant["locale"]; + @property() public timezone?: string; + @state() private _computedRRule = ""; @state() private _freq?: RepeatFrequency = "none"; @@ -292,6 +294,7 @@ export class RecurrenceRuleEditor extends LitElement { byweekday: ruleByWeekDay(this._weekday), count: this._count, until: this._until, + tzid: this.timezone, }; const contentline = RRule.optionsToString(options); return contentline.slice(6); // Strip "RRULE:" prefix From 4c5f4508b252d755919b8431180e24cb2ae36ef1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 4 Dec 2022 17:48:40 -0500 Subject: [PATCH 02/29] Bump decode-uri-component from 0.2.0 to 0.2.2 (#14543) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index daf74efbe6..c65175d338 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7026,9 +7026,9 @@ __metadata: linkType: hard "decode-uri-component@npm:^0.2.0": - version: 0.2.0 - resolution: "decode-uri-component@npm:0.2.0" - checksum: f3749344ab9305ffcfe4bfe300e2dbb61fc6359e2b736812100a3b1b6db0a5668cba31a05e4b45d4d63dbf1a18dfa354cd3ca5bb3ededddabb8cd293f4404f94 + version: 0.2.2 + resolution: "decode-uri-component@npm:0.2.2" + checksum: 95476a7d28f267292ce745eac3524a9079058bbb35767b76e3ee87d42e34cd0275d2eb19d9d08c3e167f97556e8a2872747f5e65cbebcac8b0c98d83e285f139 languageName: node linkType: hard From 65cef9d9966d0cc076fabee40c1d354cc5d1aefe Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 5 Dec 2022 03:11:53 +0100 Subject: [PATCH 03/29] Prevent negative media player progress (#14547) --- src/data/media-player.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/data/media-player.ts b/src/data/media-player.ts index 4470156588..fb45071f6f 100644 --- a/src/data/media-player.ts +++ b/src/data/media-player.ts @@ -210,7 +210,10 @@ export const getCurrentProgress = (stateObj: MediaPlayerEntity): number => { (Date.now() - new Date(stateObj.attributes.media_position_updated_at!).getTime()) / 1000.0; - return progress; + // Prevent negative values, so we do not go back to 59:59 at the start + // for example if there are slight clock sync deltas between backend and frontend and + // therefore media_position_updated_at might be slightly larger than Date.now(). + return progress < 0 ? 0 : progress; }; export const computeMediaDescription = ( From 3eac53e20959a11382692f5ee3b04a2cea7a1c61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 4 Dec 2022 23:54:32 -0500 Subject: [PATCH 04/29] Bump async from 2.6.2 to 2.6.4 (#14548) Bumps [async](https://github.com/caolan/async) from 2.6.2 to 2.6.4. - [Release notes](https://github.com/caolan/async/releases) - [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md) - [Commits](https://github.com/caolan/async/compare/v2.6.2...v2.6.4) --- updated-dependencies: - dependency-name: async dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index c65175d338..c290c86388 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5729,11 +5729,11 @@ __metadata: linkType: hard "async@npm:^2.6.2": - version: 2.6.2 - resolution: "async@npm:2.6.2" + version: 2.6.4 + resolution: "async@npm:2.6.4" dependencies: - lodash: ^4.17.11 - checksum: e5e90a3bcc4d9bf964bfc6b77d63b8f5bee8c14e9a51c3317dbcace44d5b6b1fe01cd4fd347449704a107da7fcd25e1382ee8545957b2702782ae720605cf7a4 + lodash: ^4.17.14 + checksum: a52083fb32e1ebe1d63e5c5624038bb30be68ff07a6c8d7dfe35e47c93fc144bd8652cbec869e0ac07d57dde387aa5f1386be3559cdee799cb1f789678d88e19 languageName: node linkType: hard @@ -11294,7 +11294,7 @@ fsevents@^1.2.7: languageName: node linkType: hard -"lodash@npm:^4.17.11, lodash@npm:^4.17.20, lodash@npm:^4.17.21": +"lodash@npm:^4.17.14, lodash@npm:^4.17.20, lodash@npm:^4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 From 24f2ad8be9a9975525c32d5dba3ba847f7e60a29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Dec 2022 00:22:55 -0500 Subject: [PATCH 05/29] Bump @braintree/sanitize-url from 5.0.2 to 6.0.0 (#14549) Bumps [@braintree/sanitize-url](https://github.com/braintree/sanitize-url) from 5.0.2 to 6.0.0. - [Release notes](https://github.com/braintree/sanitize-url/releases) - [Changelog](https://github.com/braintree/sanitize-url/blob/main/CHANGELOG.md) - [Commits](https://github.com/braintree/sanitize-url/compare/v5.0.2...v6.0.0) --- updated-dependencies: - dependency-name: "@braintree/sanitize-url" dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 5e91415849..37a9ab3c7a 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "author": "Paulus Schoutsen (http://paulusschoutsen.nl)", "license": "Apache-2.0", "dependencies": { - "@braintree/sanitize-url": "^5.0.2", + "@braintree/sanitize-url": "^6.0.0", "@codemirror/autocomplete": "^0.19.12", "@codemirror/commands": "^0.19.8", "@codemirror/gutter": "^0.19.9", diff --git a/yarn.lock b/yarn.lock index c290c86388..02acb127f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1358,10 +1358,10 @@ __metadata: languageName: node linkType: hard -"@braintree/sanitize-url@npm:^5.0.2": - version: 5.0.2 - resolution: "@braintree/sanitize-url@npm:5.0.2" - checksum: c033f9a0e6dd6fbd4022df2d3916a278510f759971b1e8ab278b3ce1123a3816d5fdd9d84c5c9fbcd6c94c05f8421c4c669f110c8db67eaf58f3018825af514e +"@braintree/sanitize-url@npm:^6.0.0": + version: 6.0.2 + resolution: "@braintree/sanitize-url@npm:6.0.2" + checksum: 6a9dfd4081cc96516eeb281d1a83d3b5f1ad3d2837adf968fcc2ba18889ee833554f9c641b4083c36d3360a932e4504ddf25b0b51e9933c3742622df82cf7c9a languageName: node linkType: hard @@ -9282,7 +9282,7 @@ fsevents@^1.2.7: "@babel/plugin-syntax-top-level-await": ^7.14.5 "@babel/preset-env": ^7.20.2 "@babel/preset-typescript": ^7.18.6 - "@braintree/sanitize-url": ^5.0.2 + "@braintree/sanitize-url": ^6.0.0 "@codemirror/autocomplete": ^0.19.12 "@codemirror/commands": ^0.19.8 "@codemirror/gutter": ^0.19.9 From 340449d06485150332da20137c075029070fb5e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Dec 2022 09:25:02 +0100 Subject: [PATCH 06/29] Bump dessant/lock-threads from 3.0.0 to 4.0.0 (#14550) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lock.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index e49906d686..ad51a2699b 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -9,7 +9,7 @@ jobs: lock: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v3.0.0 + - uses: dessant/lock-threads@v4.0.0 with: github-token: ${{ github.token }} issue-lock-inactive-days: "30" From 04ef783f5b2c9c354d53cf1b3a27376f8c0a67cf Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 5 Dec 2022 12:49:21 +0100 Subject: [PATCH 07/29] Fix adjust statistic parameter name (#14552) --- src/data/recorder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/recorder.ts b/src/data/recorder.ts index 510212c1e8..1f12f5e9e2 100644 --- a/src/data/recorder.ts +++ b/src/data/recorder.ts @@ -267,7 +267,7 @@ export const adjustStatisticsSum = ( return hass.callWS({ type: "recorder/adjust_sum_statistics", statistic_id, - start_time_iso, + start_time: start_time_iso, adjustment, adjustment_unit_of_measurement, }); From 7e58bd59c3dd241500c7f8932a3c5aae98155850 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 5 Dec 2022 14:21:13 +0100 Subject: [PATCH 08/29] Fix rounded corners for expansion panel (#14531) fixes undefined --- src/components/ha-expansion-panel.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/ha-expansion-panel.ts b/src/components/ha-expansion-panel.ts index 21b7fb1c1d..797b5bdbd9 100644 --- a/src/components/ha-expansion-panel.ts +++ b/src/components/ha-expansion-panel.ts @@ -31,7 +31,7 @@ export class HaExpansionPanel extends LitElement { protected render(): TemplateResult { return html` -
+
Date: Mon, 5 Dec 2022 08:22:17 -0500 Subject: [PATCH 09/29] Fix localize key errors for helpers (#14496) --- src/common/{ => array}/ensure-array.ts | 0 src/common/array/literal-includes.ts | 5 +++++ src/common/translations/localize.ts | 1 - src/components/entity/ha-statistic-picker.ts | 2 +- src/components/ha-target-picker.ts | 2 +- src/components/trace/hat-script-graph.ts | 2 +- src/data/automation_i18n.ts | 2 +- src/data/script.ts | 7 ++----- src/data/script_i18n.ts | 2 +- .../types/ha-automation-action-choose.ts | 2 +- .../ha-automation-action-wait_for_trigger.ts | 2 +- .../types/ha-automation-condition-trigger.ts | 2 +- .../types/ha-automation-trigger-state.ts | 2 +- src/panels/config/helpers/const.ts | 6 +++++- .../config/helpers/dialog-helper-detail.ts | 19 +++++++++++++++---- .../config/helpers/ha-config-helpers.ts | 8 ++++---- .../helpers/show-dialog-helper-detail.ts | 3 ++- .../integrations/ha-config-integrations.ts | 4 ++-- src/panels/history/ha-panel-history.ts | 2 +- src/panels/logbook/ha-logbook.ts | 2 +- .../hui-statistics-graph-card-editor.ts | 2 +- 21 files changed, 47 insertions(+), 30 deletions(-) rename src/common/{ => array}/ensure-array.ts (100%) create mode 100644 src/common/array/literal-includes.ts diff --git a/src/common/ensure-array.ts b/src/common/array/ensure-array.ts similarity index 100% rename from src/common/ensure-array.ts rename to src/common/array/ensure-array.ts diff --git a/src/common/array/literal-includes.ts b/src/common/array/literal-includes.ts new file mode 100644 index 0000000000..c73648586a --- /dev/null +++ b/src/common/array/literal-includes.ts @@ -0,0 +1,5 @@ +// Creates a type predicate function for determining if an array literal includes a given value +export const arrayLiteralIncludes = + (array: T) => + (searchElement: unknown, fromIndex?: number): searchElement is T[number] => + array.includes(searchElement as T[number], fromIndex); diff --git a/src/common/translations/localize.ts b/src/common/translations/localize.ts index de307f90f7..eec0503178 100644 --- a/src/common/translations/localize.ts +++ b/src/common/translations/localize.ts @@ -30,7 +30,6 @@ export type LocalizeKeys = | `ui.panel.config.dashboard.${string}` | `ui.panel.config.devices.${string}` | `ui.panel.config.energy.${string}` - | `ui.panel.config.helpers.${string}` | `ui.panel.config.info.${string}` | `ui.panel.config.logs.${string}` | `ui.panel.config.lovelace.${string}` diff --git a/src/components/entity/ha-statistic-picker.ts b/src/components/entity/ha-statistic-picker.ts index 533f6c2efc..a19ee9e011 100644 --- a/src/components/entity/ha-statistic-picker.ts +++ b/src/components/entity/ha-statistic-picker.ts @@ -3,7 +3,7 @@ import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit"; import { customElement, property, query, state } from "lit/decorators"; import memoizeOne from "memoize-one"; -import { ensureArray } from "../../common/ensure-array"; +import { ensureArray } from "../../common/array/ensure-array"; import { fireEvent } from "../../common/dom/fire_event"; import { stringCompare } from "../../common/string/compare"; import { diff --git a/src/components/ha-target-picker.ts b/src/components/ha-target-picker.ts index 3ad3baf159..d5dd2c8630 100644 --- a/src/components/ha-target-picker.ts +++ b/src/components/ha-target-picker.ts @@ -18,7 +18,7 @@ import { css, CSSResultGroup, html, LitElement, unsafeCSS } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { fireEvent } from "../common/dom/fire_event"; -import { ensureArray } from "../common/ensure-array"; +import { ensureArray } from "../common/array/ensure-array"; import { computeDomain } from "../common/entity/compute_domain"; import { computeStateName } from "../common/entity/compute_state_name"; import { diff --git a/src/components/trace/hat-script-graph.ts b/src/components/trace/hat-script-graph.ts index 5473d4cbb5..4698396819 100644 --- a/src/components/trace/hat-script-graph.ts +++ b/src/components/trace/hat-script-graph.ts @@ -25,7 +25,7 @@ import { import { css, html, LitElement, PropertyValues } from "lit"; import { customElement, property } from "lit/decorators"; import { fireEvent } from "../../common/dom/fire_event"; -import { ensureArray } from "../../common/ensure-array"; +import { ensureArray } from "../../common/array/ensure-array"; import { Condition, Trigger } from "../../data/automation"; import { Action, diff --git a/src/data/automation_i18n.ts b/src/data/automation_i18n.ts index 78cd7ea8e8..564d5bf95d 100644 --- a/src/data/automation_i18n.ts +++ b/src/data/automation_i18n.ts @@ -1,6 +1,6 @@ import { formatDuration } from "../common/datetime/format_duration"; import secondsToDuration from "../common/datetime/seconds_to_duration"; -import { ensureArray } from "../common/ensure-array"; +import { ensureArray } from "../common/array/ensure-array"; import { computeStateName } from "../common/entity/compute_state_name"; import type { HomeAssistant } from "../types"; import { Condition, Trigger } from "./automation"; diff --git a/src/data/script.ts b/src/data/script.ts index 52a3f10f29..ed0a2c8e6a 100644 --- a/src/data/script.ts +++ b/src/data/script.ts @@ -15,6 +15,7 @@ import { Describe, boolean, } from "superstruct"; +import { arrayLiteralIncludes } from "../common/array/literal-includes"; import { navigate } from "../common/navigate"; import { HomeAssistant } from "../types"; import { @@ -28,11 +29,7 @@ import { BlueprintInput } from "./blueprint"; export const MODES = ["single", "restart", "queued", "parallel"] as const; export const MODES_MAX = ["queued", "parallel"] as const; - -export const isMaxMode = ( - mode: typeof MODES[number] -): mode is typeof MODES_MAX[number] => - MODES_MAX.includes(mode as typeof MODES_MAX[number]); +export const isMaxMode = arrayLiteralIncludes(MODES_MAX); export const baseActionStruct = object({ alias: optional(string()), diff --git a/src/data/script_i18n.ts b/src/data/script_i18n.ts index 163f13e2e0..1d99a9c2d5 100644 --- a/src/data/script_i18n.ts +++ b/src/data/script_i18n.ts @@ -1,6 +1,6 @@ import { formatDuration } from "../common/datetime/format_duration"; import secondsToDuration from "../common/datetime/seconds_to_duration"; -import { ensureArray } from "../common/ensure-array"; +import { ensureArray } from "../common/array/ensure-array"; import { computeStateName } from "../common/entity/compute_state_name"; import { isTemplate } from "../common/string/has-template"; import { HomeAssistant } from "../types"; diff --git a/src/panels/config/automation/action/types/ha-automation-action-choose.ts b/src/panels/config/automation/action/types/ha-automation-action-choose.ts index b9b15a384d..bbc9b5a039 100644 --- a/src/panels/config/automation/action/types/ha-automation-action-choose.ts +++ b/src/panels/config/automation/action/types/ha-automation-action-choose.ts @@ -2,7 +2,7 @@ import { mdiDelete, mdiPlus } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../../../common/dom/fire_event"; -import { ensureArray } from "../../../../../common/ensure-array"; +import { ensureArray } from "../../../../../common/array/ensure-array"; import "../../../../../components/ha-icon-button"; import { Condition } from "../../../../../data/automation"; import { Action, ChooseAction } from "../../../../../data/script"; diff --git a/src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger.ts b/src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger.ts index ceabac3ce7..89fdb53585 100644 --- a/src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger.ts +++ b/src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger.ts @@ -10,7 +10,7 @@ import { ActionElement, handleChangeEvent } from "../ha-automation-action-row"; import "../../../../../components/ha-duration-input"; import { createDurationData } from "../../../../../common/datetime/create_duration_data"; import { TimeChangedEvent } from "../../../../../components/ha-base-time-input"; -import { ensureArray } from "../../../../../common/ensure-array"; +import { ensureArray } from "../../../../../common/array/ensure-array"; @customElement("ha-automation-action-wait_for_trigger") export class HaWaitForTriggerAction diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-trigger.ts b/src/panels/config/automation/condition/types/ha-automation-condition-trigger.ts index 8c5e54f645..3eb72d48d2 100644 --- a/src/panels/config/automation/condition/types/ha-automation-condition-trigger.ts +++ b/src/panels/config/automation/condition/types/ha-automation-condition-trigger.ts @@ -3,7 +3,7 @@ import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../../../common/dom/fire_event"; -import { ensureArray } from "../../../../../common/ensure-array"; +import { ensureArray } from "../../../../../common/array/ensure-array"; import "../../../../../components/ha-select"; import type { AutomationConfig, diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts index abce03227e..b17d3bfd71 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts @@ -11,7 +11,7 @@ import { union, } from "superstruct"; import memoizeOne from "memoize-one"; -import { ensureArray } from "../../../../../common/ensure-array"; +import { ensureArray } from "../../../../../common/array/ensure-array"; import { fireEvent } from "../../../../../common/dom/fire_event"; import { hasTemplate } from "../../../../../common/string/has-template"; import { StateTrigger } from "../../../../../data/automation"; diff --git a/src/panels/config/helpers/const.ts b/src/panels/config/helpers/const.ts index 87b03eff10..82992ca6b4 100644 --- a/src/panels/config/helpers/const.ts +++ b/src/panels/config/helpers/const.ts @@ -1,3 +1,4 @@ +import { arrayLiteralIncludes } from "../../../common/array/literal-includes"; import type { Counter } from "../../../data/counter"; import type { InputBoolean } from "../../../data/input_boolean"; import type { InputButton } from "../../../data/input_button"; @@ -18,7 +19,10 @@ export const HELPER_DOMAINS = [ "counter", "timer", "schedule", -]; +] as const; + +export type HelperDomain = typeof HELPER_DOMAINS[number]; +export const isHelperDomain = arrayLiteralIncludes(HELPER_DOMAINS); export type Helper = | InputBoolean diff --git a/src/panels/config/helpers/dialog-helper-detail.ts b/src/panels/config/helpers/dialog-helper-detail.ts index 672f40a26e..ab0e7c5d64 100644 --- a/src/panels/config/helpers/dialog-helper-detail.ts +++ b/src/panels/config/helpers/dialog-helper-detail.ts @@ -25,7 +25,7 @@ import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-c import { haStyleDialog } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; import { brandsUrl } from "../../../util/brands-url"; -import { Helper } from "./const"; +import { Helper, HelperDomain } from "./const"; import "./forms/ha-counter-form"; import "./forms/ha-input_boolean-form"; import "./forms/ha-input_button-form"; @@ -37,7 +37,18 @@ import "./forms/ha-schedule-form"; import "./forms/ha-timer-form"; import type { ShowDialogHelperDetailParams } from "./show-dialog-helper-detail"; -const HELPERS = { +type HelperCreators = { + [domain in HelperDomain]: ( + hass: HomeAssistant, + // Not properly typed because there is currently a mismatch for this._item between: + // 1. Type passed to form should be Helper + // 2. Type received by creator should be MutableParams version + // The two are not compatible. + params: any + ) => Promise; +}; + +const HELPERS: HelperCreators = { input_boolean: createInputBoolean, input_button: createInputButton, input_text: createInputText, @@ -57,7 +68,7 @@ export class DialogHelperDetail extends LitElement { @state() private _opened = false; - @state() private _domain?: string; + @state() private _domain?: HelperDomain; @state() private _error?: string; @@ -127,7 +138,7 @@ export class DialogHelperDetail extends LitElement { } else { const items: [string, string][] = []; - for (const helper of Object.keys(HELPERS)) { + for (const helper of Object.keys(HELPERS) as (keyof typeof HELPERS)[]) { items.push([ helper, this.hass.localize(`ui.panel.config.helpers.types.${helper}`) || diff --git a/src/panels/config/helpers/ha-config-helpers.ts b/src/panels/config/helpers/ha-config-helpers.ts index 4861cffa72..8e8c8268fe 100644 --- a/src/panels/config/helpers/ha-config-helpers.ts +++ b/src/panels/config/helpers/ha-config-helpers.ts @@ -35,7 +35,7 @@ import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import { HomeAssistant, Route } from "../../../types"; import { configSections } from "../ha-panel-config"; import "../integrations/ha-integration-overflow-menu"; -import { HELPER_DOMAINS } from "./const"; +import { HelperDomain, isHelperDomain } from "./const"; import { showHelperDetailDialog } from "./show-dialog-helper-detail"; // This groups items by a key but only returns last entry per key. @@ -118,7 +118,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { sortable: true, width: "25%", filterable: true, - template: (type, row) => + template: (type: HelperDomain, row) => row.configEntry ? domainToName(localize, type) : html` @@ -243,7 +243,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { if (!domain) { return; } - if (HELPER_DOMAINS.includes(domain)) { + if (isHelperDomain(domain)) { showHelperDetailDialog(this, { domain, }); @@ -330,7 +330,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { const newStates = Object.values(this.hass!.states).filter( (entity) => extraEntities.has(entity.entity_id) || - HELPER_DOMAINS.includes(computeStateDomain(entity)) + isHelperDomain(computeStateDomain(entity)) ); if ( diff --git a/src/panels/config/helpers/show-dialog-helper-detail.ts b/src/panels/config/helpers/show-dialog-helper-detail.ts index bbee0bc619..da1942c108 100644 --- a/src/panels/config/helpers/show-dialog-helper-detail.ts +++ b/src/panels/config/helpers/show-dialog-helper-detail.ts @@ -1,10 +1,11 @@ import { fireEvent } from "../../../common/dom/fire_event"; import { DataEntryFlowDialogParams } from "../../../dialogs/config-flow/show-dialog-data-entry-flow"; +import { HelperDomain } from "./const"; export const loadHelperDetailDialog = () => import("./dialog-helper-detail"); export interface ShowDialogHelperDetailParams { - domain?: string; + domain?: HelperDomain; // Only used for config entries dialogClosedCallback?: DataEntryFlowDialogParams["dialogClosedCallback"]; } diff --git a/src/panels/config/integrations/ha-config-integrations.ts b/src/panels/config/integrations/ha-config-integrations.ts index 0ea91c848b..3ba289ae99 100644 --- a/src/panels/config/integrations/ha-config-integrations.ts +++ b/src/panels/config/integrations/ha-config-integrations.ts @@ -70,7 +70,7 @@ import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import { haStyle } from "../../../resources/styles"; import type { HomeAssistant, Route } from "../../../types"; import { configSections } from "../ha-panel-config"; -import { HELPER_DOMAINS } from "../helpers/const"; +import { isHelperDomain } from "../helpers/const"; import "./ha-config-flow-card"; import "./ha-ignored-config-entry-card"; import "./ha-integration-card"; @@ -785,7 +785,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) { } // If not an integration or supported brand, try helper else show alert - if (HELPER_DOMAINS.includes(domain)) { + if (isHelperDomain(domain)) { navigate(`/config/helpers/add?domain=${domain}`, { replace: true, }); diff --git a/src/panels/history/ha-panel-history.ts b/src/panels/history/ha-panel-history.ts index 95573534bd..8f07895d24 100644 --- a/src/panels/history/ha-panel-history.ts +++ b/src/panels/history/ha-panel-history.ts @@ -18,7 +18,7 @@ import { css, html, LitElement, PropertyValues } from "lit"; import { property, state } from "lit/decorators"; import { firstWeekdayIndex } from "../../common/datetime/first_weekday"; import { LocalStorage } from "../../common/decorators/local-storage"; -import { ensureArray } from "../../common/ensure-array"; +import { ensureArray } from "../../common/array/ensure-array"; import { navigate } from "../../common/navigate"; import { createSearchParam, diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts index 025d8369ce..bfeaf28c67 100644 --- a/src/panels/logbook/ha-logbook.ts +++ b/src/panels/logbook/ha-logbook.ts @@ -1,7 +1,7 @@ import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; -import { ensureArray } from "../../common/ensure-array"; +import { ensureArray } from "../../common/array/ensure-array"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { throttle } from "../../common/util/throttle"; import "../../components/ha-circular-progress"; diff --git a/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts index 0f911d8826..a491318179 100644 --- a/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts @@ -20,7 +20,7 @@ import { union, } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; -import { ensureArray } from "../../../../common/ensure-array"; +import { ensureArray } from "../../../../common/array/ensure-array"; import type { LocalizeFunc } from "../../../../common/translations/localize"; import { deepEqual } from "../../../../common/util/deep-equal"; import { From 96080f3c78877c04afa4c9e84c03b0e67cdc97e6 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 5 Dec 2022 14:24:31 +0100 Subject: [PATCH 10/29] Set initial calendar event date based on active calendar view (#14516) --- src/panels/calendar/dialog-calendar-event-editor.ts | 6 +++++- src/panels/calendar/ha-full-calendar.ts | 11 +++++++++++ .../calendar/show-dialog-calendar-event-detail.ts | 2 +- .../calendar/show-dialog-calendar-event-editor.ts | 3 ++- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/panels/calendar/dialog-calendar-event-editor.ts b/src/panels/calendar/dialog-calendar-event-editor.ts index d673577df5..0997260929 100644 --- a/src/panels/calendar/dialog-calendar-event-editor.ts +++ b/src/panels/calendar/dialog-calendar-event-editor.ts @@ -73,7 +73,11 @@ class DialogCalendarEventEditor extends LitElement { } } else { this._allDay = false; - this._dtstart = startOfHour(new Date()); + // If we have been provided a selected date (e.g. based on the currently displayed + // day in a calendar view), use that as the starting value. + this._dtstart = startOfHour( + params.selectedDate ? params.selectedDate : new Date() + ); this._dtend = addHours(this._dtstart, 1); } } diff --git a/src/panels/calendar/ha-full-calendar.ts b/src/panels/calendar/ha-full-calendar.ts index 5fcde29675..7089b4f97c 100644 --- a/src/panels/calendar/ha-full-calendar.ts +++ b/src/panels/calendar/ha-full-calendar.ts @@ -276,8 +276,19 @@ export class HAFullCalendar extends LitElement { } private _createEvent(_info) { + // Logic for selectedDate: In week and day view, use the start of the week or the selected day. + // If we are in month view, we only use the start of the month, if we are not showing the + // current actual month, as for that one the current day is automatically highlighted and + // defaulting to a different day in the event creation dialog would be weird. showCalendarEventEditDialog(this, { calendars: this._mutableCalendars, + selectedDate: + this._activeView === "dayGridWeek" || + this._activeView === "dayGridDay" || + (this._activeView === "dayGridMonth" && + this.calendar!.view.currentStart.getMonth() !== new Date().getMonth()) + ? this.calendar!.view.currentStart + : undefined, updated: () => { this._fireViewChanged(); }, diff --git a/src/panels/calendar/show-dialog-calendar-event-detail.ts b/src/panels/calendar/show-dialog-calendar-event-detail.ts index 902e8746b6..9946ed9bdb 100644 --- a/src/panels/calendar/show-dialog-calendar-event-detail.ts +++ b/src/panels/calendar/show-dialog-calendar-event-detail.ts @@ -2,7 +2,7 @@ import { fireEvent } from "../../common/dom/fire_event"; import { Calendar, CalendarEventData } from "../../data/calendar"; export interface CalendarEventDetailDialogParams { - calendars: Calendar[]; // When creating new events, is the list of events that support creation + calendars: Calendar[]; // When creating new events, is the list of calendar entities that support creation calendarId?: string; entry?: CalendarEventData; canDelete?: boolean; diff --git a/src/panels/calendar/show-dialog-calendar-event-editor.ts b/src/panels/calendar/show-dialog-calendar-event-editor.ts index 695832d164..f2ab58a4f6 100644 --- a/src/panels/calendar/show-dialog-calendar-event-editor.ts +++ b/src/panels/calendar/show-dialog-calendar-event-editor.ts @@ -2,8 +2,9 @@ import { fireEvent } from "../../common/dom/fire_event"; import { Calendar, CalendarEventData } from "../../data/calendar"; export interface CalendarEventEditDialogParams { - calendars: Calendar[]; // When creating new events, is the list of events that support creation + calendars: Calendar[]; // When creating new events, is the list of calendar entities that support creation calendarId?: string; + selectedDate?: Date; // When provided is used as the pre-filled date for the event creation dialog entry?: CalendarEventData; canDelete?: boolean; updated: () => void; From d059b97a2f57d4e6171914cd82d8912aa6018454 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 5 Dec 2022 14:25:30 +0100 Subject: [PATCH 11/29] Prevent issues in "ha-form" with undefined "disabled" state (#14518) fixes undefined --- src/components/ha-form/ha-form.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ha-form/ha-form.ts b/src/components/ha-form/ha-form.ts index 348f49494f..ce5208ec67 100644 --- a/src/components/ha-form/ha-form.ts +++ b/src/components/ha-form/ha-form.ts @@ -85,7 +85,7 @@ export class HaForm extends LitElement implements HaFormElement { .selector=${item.selector} .value=${getValue(this.data, item)} .label=${this._computeLabel(item, this.data)} - .disabled=${this.disabled || item.disabled} + .disabled=${item.disabled || this.disabled} .helper=${this._computeHelper(item)} .required=${item.required || false} .context=${this._generateContext(item)} From 76a682fa28353f19d4e5e9205a0d3869f0178d54 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 5 Dec 2022 14:26:11 +0100 Subject: [PATCH 12/29] Ensure calendar event texts are aligned (#14521) --- src/panels/calendar/dialog-calendar-event-detail.ts | 10 +++++++--- src/panels/calendar/dialog-calendar-event-editor.ts | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/panels/calendar/dialog-calendar-event-detail.ts b/src/panels/calendar/dialog-calendar-event-detail.ts index cf3db9adb5..e5a6aaca00 100644 --- a/src/panels/calendar/dialog-calendar-event-detail.ts +++ b/src/panels/calendar/dialog-calendar-event-detail.ts @@ -8,6 +8,7 @@ import { formatDate } from "../../common/datetime/format_date"; import { formatDateTime } from "../../common/datetime/format_date_time"; import { formatTime } from "../../common/datetime/format_time"; import { fireEvent } from "../../common/dom/fire_event"; +import { capitalizeFirstLetter } from "../../common/string/capitalize-first-letter"; import { isDate } from "../../common/string/is_date"; import "../../components/entity/state-info"; import "../../components/ha-date-input"; @@ -108,7 +109,8 @@ class DialogCalendarEventDetail extends LitElement { ${this.hass.localize("ui.components.calendar.event.delete")} ` - : ""}${this._params.canEdit + : ""} + ${this._params.canEdit ? html`${readableText}
`; } catch (e) { return ""; @@ -227,7 +231,7 @@ class DialogCalendarEventDetail extends LitElement { ha-svg-icon { width: 40px; margin-right: 8px; - margin-inline-end: 8px; + margin-inline-end: 16px; margin-inline-start: initial; direction: var(--direction); vertical-align: top; diff --git a/src/panels/calendar/dialog-calendar-event-editor.ts b/src/panels/calendar/dialog-calendar-event-editor.ts index 0997260929..6c29ab0d80 100644 --- a/src/panels/calendar/dialog-calendar-event-editor.ts +++ b/src/panels/calendar/dialog-calendar-event-editor.ts @@ -440,7 +440,7 @@ class DialogCalendarEventEditor extends LitElement { ha-svg-icon { width: 40px; margin-right: 8px; - margin-inline-end: 8px; + margin-inline-end: 16px; margin-inline-start: initial; direction: var(--direction); vertical-align: top; From 1105c925692145f3fcb90e8cf50ed1089178dc72 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 5 Dec 2022 14:28:43 +0100 Subject: [PATCH 13/29] Add support for calendar event description (#14522) --- src/data/calendar.ts | 3 +++ .../calendar/dialog-calendar-event-detail.ts | 10 +++++++ .../calendar/dialog-calendar-event-editor.ts | 26 ++++++++++++++++--- src/translations/en.json | 3 ++- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/data/calendar.ts b/src/data/calendar.ts index 807fb5e3c7..819bbee25d 100644 --- a/src/data/calendar.ts +++ b/src/data/calendar.ts @@ -29,6 +29,7 @@ export interface CalendarEventData { dtstart: string; dtend: string; rrule?: string; + description?: string; } export interface CalendarEventMutableParams { @@ -36,6 +37,7 @@ export interface CalendarEventMutableParams { dtstart: string; dtend: string; rrule?: string; + description?: string; } // The scope of a delete/update for a recurring event @@ -84,6 +86,7 @@ export const fetchCalendarEvents = async ( const eventData: CalendarEventData = { uid: ev.uid, summary: ev.summary, + description: ev.description, dtstart: eventStart, dtend: eventEnd, recurrence_id: ev.recurrence_id, diff --git a/src/panels/calendar/dialog-calendar-event-detail.ts b/src/panels/calendar/dialog-calendar-event-detail.ts index e5a6aaca00..9776a9e784 100644 --- a/src/panels/calendar/dialog-calendar-event-detail.ts +++ b/src/panels/calendar/dialog-calendar-event-detail.ts @@ -87,6 +87,11 @@ class DialogCalendarEventDetail extends LitElement { ${this._data!.rrule ? this._renderRruleAsText(this._data.rrule) : ""} + ${this._data.description + ? html`
+
${this._data.description}
+
` + : html``}
@@ -239,6 +244,11 @@ class DialogCalendarEventDetail extends LitElement { .field { display: flex; } + .description { + color: var(--secondary-text-color); + max-width: 300px; + overflow-wrap: break-word; + } `, ]; } diff --git a/src/panels/calendar/dialog-calendar-event-editor.ts b/src/panels/calendar/dialog-calendar-event-editor.ts index 6c29ab0d80..7316f27dc3 100644 --- a/src/panels/calendar/dialog-calendar-event-editor.ts +++ b/src/panels/calendar/dialog-calendar-event-editor.ts @@ -7,6 +7,7 @@ import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { isDate } from "../../common/string/is_date"; import "../../components/ha-date-input"; +import "../../components/ha-textarea"; import "../../components/ha-time-input"; import { Calendar, @@ -42,6 +43,8 @@ class DialogCalendarEventEditor extends LitElement { @state() private _summary = ""; + @state() private _description = ""; + @state() private _rrule?: string; @state() private _allDay = false; @@ -127,6 +130,15 @@ class DialogCalendarEventEditor extends LitElement { error-message=${this.hass.localize("ui.common.error_required")} dialogInitialFocus > + Date: Mon, 5 Dec 2022 14:30:28 +0100 Subject: [PATCH 14/29] set backpath for supervisor addons panel (#14555) --- hassio/src/dashboard/hassio-dashboard.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/hassio/src/dashboard/hassio-dashboard.ts b/hassio/src/dashboard/hassio-dashboard.ts index 89cb7c500d..b0316ed0ee 100644 --- a/hassio/src/dashboard/hassio-dashboard.ts +++ b/hassio/src/dashboard/hassio-dashboard.ts @@ -28,6 +28,7 @@ class HassioDashboard extends LitElement { .hass=${this.hass} .narrow=${this.narrow} .route=${this.route} + back-path="/config" .header=${this.supervisor.localize("panel.addons")} > Date: Mon, 5 Dec 2022 14:31:19 +0100 Subject: [PATCH 15/29] Prevent identical double debug log downloads for integration (#14537) fixes undefined --- src/panels/config/integrations/ha-integration-card.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/panels/config/integrations/ha-integration-card.ts b/src/panels/config/integrations/ha-integration-card.ts index 9a145617ba..5d3b7096c7 100644 --- a/src/panels/config/integrations/ha-integration-card.ts +++ b/src/panels/config/integrations/ha-integration-card.ts @@ -548,6 +548,9 @@ export class HaIntegrationCard extends LitElement { } private async _handleDisableDebugLogging(ev: MouseEvent) { + // Stop propagation since otherwise we end up here twice while we await the log level change + // and trigger two identical debug log downloads. + ev.stopPropagation(); const configEntry = ((ev.target as HTMLElement).closest("ha-card") as any) .configEntry; const integration = configEntry.domain; From 4fcfcbeefb28bc9d173b4562946804c5adbcc203 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 5 Dec 2022 14:31:44 +0100 Subject: [PATCH 16/29] Simplify --- src/panels/calendar/dialog-calendar-event-detail.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/panels/calendar/dialog-calendar-event-detail.ts b/src/panels/calendar/dialog-calendar-event-detail.ts index 9776a9e784..33609da394 100644 --- a/src/panels/calendar/dialog-calendar-event-detail.ts +++ b/src/panels/calendar/dialog-calendar-event-detail.ts @@ -130,12 +130,13 @@ class DialogCalendarEventDetail extends LitElement { private _renderRruleAsText(value: string) { // TODO: Make sure this handles translations + if (!value) { + return ""; + } try { - const readableText = - value === "" - ? "" - : capitalizeFirstLetter(RRule.fromString(`RRULE:${value}`).toText()); - return html`
${readableText}
`; + return html`
+ ${capitalizeFirstLetter(RRule.fromString(`RRULE:${value}`).toText())} +
`; } catch (e) { return ""; } From ef3caf91f1f38c567dba7c399eda5afd1441342b Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Mon, 5 Dec 2022 14:33:50 +0100 Subject: [PATCH 17/29] Fix retreiving topic value when using MQTT publish (#14554) fixes undefined --- .../integrations/integration-panels/mqtt/mqtt-config-panel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts b/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts index 1d81f69fe4..f658f0b6fb 100644 --- a/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts +++ b/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts @@ -82,7 +82,7 @@ class HaPanelDevMqtt extends LitElement { } private _handleTopic(ev: CustomEvent) { - this.topic = ev.detail.value; + this.topic = (ev.target! as any).value; if (localStorage && this.inited) { localStorage["panel-dev-mqtt-topic"] = this.topic; } From 07ad429f8cc1dfa64fc4310504cf588db6b52007 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 5 Dec 2022 14:54:38 +0100 Subject: [PATCH 18/29] Fix first day of the week locale issue with scheduler (#14529) fixes undefined --- src/panels/config/helpers/forms/ha-schedule-form.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/panels/config/helpers/forms/ha-schedule-form.ts b/src/panels/config/helpers/forms/ha-schedule-form.ts index 2c238c1ef4..3eb7f700ae 100644 --- a/src/panels/config/helpers/forms/ha-schedule-form.ts +++ b/src/panels/config/helpers/forms/ha-schedule-form.ts @@ -208,7 +208,10 @@ class HaScheduleForm extends LitElement { private get _events() { const events: any[] = []; const currentDay = new Date().getDay(); - const baseDay = currentDay === 0 ? 7 : currentDay; + const baseDay = + currentDay === 0 && firstWeekdayIndex(this.hass.locale) === 1 + ? 7 + : currentDay; for (const [i, day] of weekdays.entries()) { if (!this[`_${day}`].length) { @@ -216,8 +219,11 @@ class HaScheduleForm extends LitElement { } this[`_${day}`].forEach((item: ScheduleDay, index: number) => { - // Add 7 to 0 because we start the calendar on Monday - const distance = i - baseDay + (i === 0 ? 7 : 0); + // Add 7 to 0 because we start the calendar on Monday, except when the locale says otherwise (firstWeekdayIndex() != 1) + const distance = + i - + baseDay + + (i === 0 && firstWeekdayIndex(this.hass.locale) === 1 ? 7 : 0); const start = new Date(); start.setDate(start.getDate() + distance); From f0127511b0ad77bb32fdc6b39c0ffe5fd6efc0fd Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 5 Dec 2022 15:01:18 +0100 Subject: [PATCH 19/29] Handle optional state enum from sensor entities (#14428) --- src/common/entity/get_states.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/common/entity/get_states.ts b/src/common/entity/get_states.ts index 7c59eb2faa..92c1365b14 100644 --- a/src/common/entity/get_states.ts +++ b/src/common/entity/get_states.ts @@ -261,6 +261,11 @@ export const getStates = ( result.push(...state.attributes.activity_list); } break; + case "sensor": + if (!attribute && state.attributes.device_class === "enum") { + result.push(...state.attributes.options); + } + break; case "vacuum": if (attribute === "fan_speed") { result.push(...state.attributes.fan_speed_list); From 076ddb71b6dc0ebc57259bfb531707e82f3cf562 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 5 Dec 2022 15:32:24 +0100 Subject: [PATCH 20/29] Show proper media title in media bar (split URI) (#14464) --- src/data/media-player.ts | 8 +++++++- src/panels/media-browser/ha-bar-media-player.ts | 3 +-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/data/media-player.ts b/src/data/media-player.ts index fb45071f6f..1bf35025fc 100644 --- a/src/data/media-player.ts +++ b/src/data/media-player.ts @@ -405,7 +405,13 @@ export const cleanupMediaTitle = (title?: string): string | undefined => { } const index = title.indexOf("?authSig="); - return index > 0 ? title.slice(0, index) : title; + let cleanTitle = index > 0 ? title.slice(0, index) : title; + + if (cleanTitle.startsWith("http")) { + cleanTitle = decodeURIComponent(cleanTitle.split("/").pop()!); + } + + return cleanTitle; }; /** diff --git a/src/panels/media-browser/ha-bar-media-player.ts b/src/panels/media-browser/ha-bar-media-player.ts index 1953f1b6de..b46902e0b2 100644 --- a/src/panels/media-browser/ha-bar-media-player.ts +++ b/src/panels/media-browser/ha-bar-media-player.ts @@ -212,7 +212,7 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) { const mediaDescription = computeMediaDescription(stateObj); const mediaDuration = formatMediaTime(stateObj.attributes.media_duration); const mediaTitleClean = cleanupMediaTitle( - stateObj.attributes.media_title || "" + stateObj.attributes.media_title || stateObj.attributes.media_content_id ); const mediaArt = stateObj.attributes.entity_picture_local || @@ -232,7 +232,6 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) { Date: Mon, 5 Dec 2022 15:33:41 +0100 Subject: [PATCH 21/29] Fix YAML validation for automation action service call enabling/disabling (#14536) fixes undefined --- .../action/types/ha-automation-action-service.ts | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/panels/config/automation/action/types/ha-automation-action-service.ts b/src/panels/config/automation/action/types/ha-automation-action-service.ts index 83102c1d82..e59c1800dd 100644 --- a/src/panels/config/automation/action/types/ha-automation-action-service.ts +++ b/src/panels/config/automation/action/types/ha-automation-action-service.ts @@ -1,22 +1,13 @@ import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { any, assert, object, optional, string } from "superstruct"; +import { assert } from "superstruct"; import { fireEvent } from "../../../../../common/dom/fire_event"; import { hasTemplate } from "../../../../../common/string/has-template"; -import { entityIdOrAll } from "../../../../../common/structs/is-entity-id"; import "../../../../../components/ha-service-control"; -import { ServiceAction } from "../../../../../data/script"; +import { ServiceAction, serviceActionStruct } from "../../../../../data/script"; import type { HomeAssistant } from "../../../../../types"; import { ActionElement } from "../ha-automation-action-row"; -const actionStruct = object({ - alias: optional(string()), - service: optional(string()), - entity_id: optional(entityIdOrAll()), - target: optional(any()), - data: optional(any()), -}); - @customElement("ha-automation-action-service") export class HaServiceAction extends LitElement implements ActionElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -38,7 +29,7 @@ export class HaServiceAction extends LitElement implements ActionElement { return; } try { - assert(this.action, actionStruct); + assert(this.action, serviceActionStruct); } catch (err: any) { fireEvent(this, "ui-mode-not-available", err); return; From b389127f78f3a4b3b4492d3ac8cb2510a71667b9 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 5 Dec 2022 15:55:23 +0100 Subject: [PATCH 22/29] Prevent "ha-chip" focus outline frame (#14535) --- src/components/ha-chip.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/ha-chip.ts b/src/components/ha-chip.ts index 88bfc8be3c..d9fa23971f 100644 --- a/src/components/ha-chip.ts +++ b/src/components/ha-chip.ts @@ -77,6 +77,10 @@ export class HaChip extends LitElement { span[role="gridcell"] { line-height: 14px; } + + :host { + outline: none; + } `; } } From b6444072609b2892089823d1a1e2da18e64dd22b Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 5 Dec 2022 16:08:00 +0100 Subject: [PATCH 23/29] Consistent table style for calendar and scheduler (#14528) --- src/panels/calendar/ha-full-calendar.ts | 7 ++++--- .../config/helpers/forms/ha-schedule-form.ts | 14 ++++++++++++++ src/resources/styles.ts | 1 + 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/panels/calendar/ha-full-calendar.ts b/src/panels/calendar/ha-full-calendar.ts index 7089b4f97c..d55c344a43 100644 --- a/src/panels/calendar/ha-full-calendar.ts +++ b/src/panels/calendar/ha-full-calendar.ts @@ -391,7 +391,7 @@ export class HAFullCalendar extends LitElement { } a { - color: var(--primary-text-color); + color: var(--primary-color); } .controls { @@ -460,9 +460,10 @@ export class HAFullCalendar extends LitElement { } th.fc-col-header-cell.fc-day { - color: var(--secondary-text-color); + background-color: var(--table-header-background-color); + color: var(--primary-text-color); font-size: 11px; - font-weight: 400; + font-weight: bold; text-transform: uppercase; } diff --git a/src/panels/config/helpers/forms/ha-schedule-form.ts b/src/panels/config/helpers/forms/ha-schedule-form.ts index 3eb7f700ae..ba7376f492 100644 --- a/src/panels/config/helpers/forms/ha-schedule-form.ts +++ b/src/panels/config/helpers/forms/ha-schedule-form.ts @@ -394,6 +394,8 @@ class HaScheduleForm extends LitElement { -webkit-user-select: none; -ms-user-select: none; user-select: none; + --fc-border-color: var(--divider-color); + --fc-event-border-color: var(--divider-color); } .fc-scroller { overflow-x: visible !important; @@ -401,6 +403,18 @@ class HaScheduleForm extends LitElement { .fc-v-event .fc-event-time { white-space: inherit; } + + a { + color: inherit !important; + } + + th.fc-col-header-cell.fc-day { + background-color: var(--table-header-background-color); + color: var(--primary-text-color); + font-size: 11px; + font-weight: bold; + text-transform: uppercase; + } `, ]; } diff --git a/src/resources/styles.ts b/src/resources/styles.ts index a943e154b2..83ddd066dc 100644 --- a/src/resources/styles.ts +++ b/src/resources/styles.ts @@ -75,6 +75,7 @@ export const derivedStyles = { "paper-listbox-background-color": "var(--card-background-color)", "paper-item-icon-color": "var(--state-icon-color)", "paper-item-icon-active-color": "var(--state-icon-active-color)", + "table-header-background-color": "var(--input-fill-color)", "table-row-background-color": "var(--primary-background-color)", "table-row-alternative-background-color": "var(--secondary-background-color)", "paper-slider-knob-color": "var(--slider-color)", From 0ae8246d8a88851cca8f1f70224ee02d24bdd37f Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 5 Dec 2022 16:11:12 +0100 Subject: [PATCH 24/29] Simplify calendar "listWeek" and enable for "initial_view" (#9993) fixes undefined --- src/panels/calendar/ha-full-calendar.ts | 89 ++++++++++++------- .../lovelace/cards/hui-calendar-card.ts | 4 +- .../hui-calendar-card-editor.ts | 2 +- src/translations/en.json | 3 +- src/types.ts | 2 +- 5 files changed, 63 insertions(+), 37 deletions(-) diff --git a/src/panels/calendar/ha-full-calendar.ts b/src/panels/calendar/ha-full-calendar.ts index d55c344a43..569e3d6492 100644 --- a/src/panels/calendar/ha-full-calendar.ts +++ b/src/panels/calendar/ha-full-calendar.ts @@ -29,28 +29,29 @@ import { } from "lit"; import { property, state } from "lit/decorators"; import memoize from "memoize-one"; +import { firstWeekdayIndex } from "../../common/datetime/first_weekday"; import { useAmPm } from "../../common/datetime/use_am_pm"; import { fireEvent } from "../../common/dom/fire_event"; +import { supportsFeature } from "../../common/entity/supports-feature"; +import { LocalizeFunc } from "../../common/translations/localize"; +import { computeRTLDirection } from "../../common/util/compute_rtl"; import "../../components/ha-button-toggle-group"; import "../../components/ha-fab"; -import "../../components/ha-icon-button-prev"; import "../../components/ha-icon-button-next"; +import "../../components/ha-icon-button-prev"; +import type { + Calendar as CalendarData, + CalendarEvent, +} from "../../data/calendar"; +import { CalendarEntityFeature } from "../../data/calendar"; import { haStyle } from "../../resources/styles"; -import { computeRTLDirection } from "../../common/util/compute_rtl"; import type { CalendarViewChanged, FullCalendarView, HomeAssistant, ToggleButton, } from "../../types"; -import { firstWeekdayIndex } from "../../common/datetime/first_weekday"; -import { supportsFeature } from "../../common/entity/supports-feature"; import { showCalendarEventDetailDialog } from "./show-dialog-calendar-event-detail"; -import type { - Calendar as CalendarData, - CalendarEvent, -} from "../../data/calendar"; -import { CalendarEntityFeature } from "../../data/calendar"; import { showCalendarEventEditDialog } from "./show-dialog-calendar-event-editor"; declare global { @@ -62,15 +63,6 @@ declare global { } } -const getListWeekRange = (currentDate: Date): { start: Date; end: Date } => { - const startDate = new Date(currentDate.valueOf()); - const endDate = new Date(currentDate.valueOf()); - - endDate.setDate(endDate.getDate() + 7); - - return { start: startDate, end: endDate }; -}; - const defaultFullCalendarConfig: CalendarOptions = { headerToolbar: false, plugins: [dayGridPlugin, listPlugin, interactionPlugin], @@ -80,19 +72,13 @@ const defaultFullCalendarConfig: CalendarOptions = { eventDisplay: "list-item", locales: allLocales, views: { - list: { - visibleRange: getListWeekRange, + listWeek: { + type: "list", + duration: { days: 7 }, }, }, }; -const viewButtons: ToggleButton[] = [ - { label: "Month View", value: "dayGridMonth", iconPath: mdiViewModule }, - { label: "Week View", value: "dayGridWeek", iconPath: mdiViewWeek }, - { label: "Day View", value: "dayGridDay", iconPath: mdiViewDay }, - { label: "List View", value: "list", iconPath: mdiViewAgenda }, -]; - export class HAFullCalendar extends LitElement { public hass!: HomeAssistant; @@ -106,12 +92,15 @@ export class HAFullCalendar extends LitElement { "dayGridMonth", "dayGridWeek", "dayGridDay", + "listWeek", ]; @property() public initialView: FullCalendarView = "dayGridMonth"; private calendar?: Calendar; + private _viewButtons?: ToggleButton[]; + @state() private _activeView = this.initialView; public updateSize(): void { @@ -119,7 +108,10 @@ export class HAFullCalendar extends LitElement { } protected render(): TemplateResult { - const viewToggleButtons = this._viewToggleButtons(this.views); + const viewToggleButtons = this._viewToggleButtons( + this.views, + this.hass.localize + ); return html` ${this.calendar @@ -349,11 +341,44 @@ export class HAFullCalendar extends LitElement { }); } - private _viewToggleButtons = memoize((views) => - viewButtons.filter((button) => + private _viewToggleButtons = memoize((views, localize: LocalizeFunc) => { + if (!this._viewButtons) { + this._viewButtons = [ + { + label: localize( + "ui.panel.lovelace.editor.card.calendar.views.dayGridMonth" + ), + value: "dayGridMonth", + iconPath: mdiViewModule, + }, + { + label: localize( + "ui.panel.lovelace.editor.card.calendar.views.dayGridWeek" + ), + value: "dayGridWeek", + iconPath: mdiViewWeek, + }, + { + label: localize( + "ui.panel.lovelace.editor.card.calendar.views.dayGridDay" + ), + value: "dayGridDay", + iconPath: mdiViewDay, + }, + { + label: localize( + "ui.panel.lovelace.editor.card.calendar.views.listWeek" + ), + value: "listWeek", + iconPath: mdiViewAgenda, + }, + ]; + } + + return this._viewButtons.filter((button) => views.includes(button.value as FullCalendarView) - ) - ); + ); + }); static get styles(): CSSResultGroup { return [ diff --git a/src/panels/lovelace/cards/hui-calendar-card.ts b/src/panels/lovelace/cards/hui-calendar-card.ts index 313762e918..6b551746c3 100644 --- a/src/panels/lovelace/cards/hui-calendar-card.ts +++ b/src/panels/lovelace/cards/hui-calendar-card.ts @@ -119,8 +119,8 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard { } const views: FullCalendarView[] = this._veryNarrow - ? ["list"] - : ["list", "dayGridMonth", "dayGridDay"]; + ? ["listWeek"] + : ["dayGridMonth", "dayGridDay", "listWeek"]; return html` diff --git a/src/panels/lovelace/editor/config-elements/hui-calendar-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-calendar-card-editor.ts index 7f0f6ff8a0..ed25fd5201 100644 --- a/src/panels/lovelace/editor/config-elements/hui-calendar-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-calendar-card-editor.ts @@ -31,7 +31,7 @@ const cardConfigStruct = assign( }) ); -const views = ["dayGridMonth", "dayGridDay", "list"] as const; +const views = ["dayGridMonth", "dayGridDay", "listWeek"] as const; @customElement("hui-calendar-card-editor") export class HuiCalendarCardEditor diff --git a/src/translations/en.json b/src/translations/en.json index ea1db48ce7..c67b281e9c 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -4010,8 +4010,9 @@ "calendar_entities": "Calendar Entities", "views": { "dayGridMonth": "Month", + "dayGridWeek": "Week", "dayGridDay": "Day", - "list": "List" + "listWeek": "List (7 days)" } }, "conditional": { diff --git a/src/types.ts b/src/types.ts index e5939b7a4f..30f9139310 100644 --- a/src/types.ts +++ b/src/types.ts @@ -122,7 +122,7 @@ export type FullCalendarView = | "dayGridMonth" | "dayGridWeek" | "dayGridDay" - | "list"; + | "listWeek"; export interface ToggleButton { label: string; From 75b1b1c9a0a5ad39aa3736db1f8f932792e8244c Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 5 Dec 2022 16:13:22 +0100 Subject: [PATCH 25/29] Fix media browser gender translations (#14551) --- src/data/cloud/tts.ts | 2 +- src/translations/en.json | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/data/cloud/tts.ts b/src/data/cloud/tts.ts index ec563a6087..e5fc6e1804 100644 --- a/src/data/cloud/tts.ts +++ b/src/data/cloud/tts.ts @@ -60,7 +60,7 @@ export const getCloudTtsSupportedGenders = ( genders.push([ gender, gender === "male" || gender === "female" - ? localize(`ui.panel.config.cloud.account.tts.${gender}`) + ? localize(`ui.components.media-browser.tts.gender_${gender}`) : gender, ]); } diff --git a/src/translations/en.json b/src/translations/en.json index c67b281e9c..2ba4a278af 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2580,8 +2580,6 @@ "info": "Bring personality to your home by having it speak to you by using our Text-to-Speech services. You can use this in automations and scripts by using the {service} service.", "default_language": "Default language to use", "default_gender": "Default gender to use", - "male": "Male", - "female": "Female", "try": "Try", "dialog": { "header": "Try Text to Speech", From f0f699a37ec8627d5832b836320ca46fe1592255 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 5 Dec 2022 16:33:47 +0100 Subject: [PATCH 26/29] Auto remove empty items from shopping list (#14487) --- src/data/shopping-list.ts | 9 +++++++ .../lovelace/cards/hui-shopping-list-card.ts | 24 ++++++++++++------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/data/shopping-list.ts b/src/data/shopping-list.ts index 1d1d16b26e..7a1d60a1ca 100644 --- a/src/data/shopping-list.ts +++ b/src/data/shopping-list.ts @@ -39,6 +39,15 @@ export const addItem = ( name, }); +export const removeItem = ( + hass: HomeAssistant, + item_id: string +): Promise => + hass.callWS({ + type: "shopping_list/items/remove", + item_id, + }); + export const reorderItems = ( hass: HomeAssistant, itemIds: string[] diff --git a/src/panels/lovelace/cards/hui-shopping-list-card.ts b/src/panels/lovelace/cards/hui-shopping-list-card.ts index 8886fdddbd..f1d82f45e3 100644 --- a/src/panels/lovelace/cards/hui-shopping-list-card.ts +++ b/src/panels/lovelace/cards/hui-shopping-list-card.ts @@ -8,32 +8,33 @@ import { PropertyValues, TemplateResult, } from "lit"; -import { customElement, property, state, query } from "lit/decorators"; +import { customElement, property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { guard } from "lit/directives/guard"; import { repeat } from "lit/directives/repeat"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import "../../../components/ha-card"; -import "../../../components/ha-svg-icon"; import "../../../components/ha-checkbox"; +import "../../../components/ha-svg-icon"; import "../../../components/ha-textfield"; +import type { HaTextField } from "../../../components/ha-textfield"; import { addItem, clearItems, fetchItems, + removeItem, reorderItems, ShoppingListItem, updateItem, } from "../../../data/shopping-list"; import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; -import { HomeAssistant } from "../../../types"; -import { LovelaceCard, LovelaceCardEditor } from "../types"; -import { SensorCardConfig, ShoppingListCardConfig } from "./types"; -import type { HaTextField } from "../../../components/ha-textfield"; import { loadSortable, SortableInstance, } from "../../../resources/sortable.ondemand"; +import { HomeAssistant } from "../../../types"; +import { LovelaceCard, LovelaceCardEditor } from "../types"; +import { SensorCardConfig, ShoppingListCardConfig } from "./types"; @customElement("hui-shopping-list-card") class HuiShoppingListCard @@ -264,9 +265,14 @@ class HuiShoppingListCard } private _saveEdit(ev): void { - updateItem(this.hass!, ev.target.itemId, { - name: ev.target.value, - }).catch(() => this._fetchData()); + // If name is not empty, update the item otherwise remove it + if (ev.target.value) { + updateItem(this.hass!, ev.target.itemId, { + name: ev.target.value, + }).catch(() => this._fetchData()); + } else { + removeItem(this.hass!, ev.target.itemId).catch(() => this._fetchData()); + } ev.target.blur(); } From ecdd07ff4d0724715b71b48a34fc785ea09bf177 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 5 Dec 2022 18:28:48 +0100 Subject: [PATCH 27/29] Add calendar event recurrence rule translations (#14544) Co-authored-by: Bram Kragten --- src/common/datetime/format_date.ts | 19 ++++++- src/common/translations/day_names.ts | 10 ++++ src/common/translations/localize.ts | 1 + src/common/translations/month_names.ts | 10 ++++ src/components/chart/chart-date-adapter.ts | 4 +- .../more-info/controls/more-info-weather.ts | 4 +- .../calendar/dialog-calendar-event-detail.ts | 57 ++++++++++++++++--- .../calendar/dialog-calendar-event-editor.ts | 11 ++++ src/translations/en.json | 25 ++++++++ 9 files changed, 126 insertions(+), 15 deletions(-) create mode 100644 src/common/translations/day_names.ts create mode 100644 src/common/translations/month_names.ts diff --git a/src/common/datetime/format_date.ts b/src/common/datetime/format_date.ts index fb6e75feb0..35bc5eaa7a 100644 --- a/src/common/datetime/format_date.ts +++ b/src/common/datetime/format_date.ts @@ -7,10 +7,12 @@ if (__BUILD__ === "latest" && polyfillsLoaded) { } // Tuesday, August 10 -export const formatDateWeekday = (dateObj: Date, locale: FrontendLocaleData) => - formatDateWeekdayMem(locale).format(dateObj); +export const formatDateWeekdayDay = ( + dateObj: Date, + locale: FrontendLocaleData +) => formatDateWeekdayDayMem(locale).format(dateObj); -const formatDateWeekdayMem = memoizeOne( +const formatDateWeekdayDayMem = memoizeOne( (locale: FrontendLocaleData) => new Intl.DateTimeFormat(locale.language, { weekday: "long", @@ -92,3 +94,14 @@ const formatDateYearMem = memoizeOne( year: "numeric", }) ); + +// Monday +export const formatDateWeekday = (dateObj: Date, locale: FrontendLocaleData) => + formatDateWeekdayMem(locale).format(dateObj); + +const formatDateWeekdayMem = memoizeOne( + (locale: FrontendLocaleData) => + new Intl.DateTimeFormat(locale.language, { + weekday: "long", + }) +); diff --git a/src/common/translations/day_names.ts b/src/common/translations/day_names.ts new file mode 100644 index 0000000000..34b18fdb4a --- /dev/null +++ b/src/common/translations/day_names.ts @@ -0,0 +1,10 @@ +import { addDays, startOfWeek } from "date-fns"; +import memoizeOne from "memoize-one"; +import { FrontendLocaleData } from "../../data/translation"; +import { formatDateWeekday } from "../datetime/format_date"; + +export const dayNames = memoizeOne((locale: FrontendLocaleData): string[] => + Array.from({ length: 7 }, (_, d) => + formatDateWeekday(addDays(startOfWeek(new Date()), d), locale) + ) +); diff --git a/src/common/translations/localize.ts b/src/common/translations/localize.ts index eec0503178..0b5fc77030 100644 --- a/src/common/translations/localize.ts +++ b/src/common/translations/localize.ts @@ -18,6 +18,7 @@ export type LocalizeKeys = | `ui.card.alarm_control_panel.${string}` | `ui.card.weather.attributes.${string}` | `ui.card.weather.cardinal_direction.${string}` + | `ui.components.calendar.event.rrule.${string}` | `ui.components.logbook.${string}` | `ui.components.selectors.file.${string}` | `ui.dialogs.entity_registry.editor.${string}` diff --git a/src/common/translations/month_names.ts b/src/common/translations/month_names.ts new file mode 100644 index 0000000000..2e456b1855 --- /dev/null +++ b/src/common/translations/month_names.ts @@ -0,0 +1,10 @@ +import { addMonths, startOfYear } from "date-fns"; +import memoizeOne from "memoize-one"; +import { FrontendLocaleData } from "../../data/translation"; +import { formatDateMonth } from "../datetime/format_date"; + +export const monthNames = memoizeOne((locale: FrontendLocaleData): string[] => + Array.from({ length: 12 }, (_, m) => + formatDateMonth(addMonths(startOfYear(new Date()), m), locale) + ) +); diff --git a/src/components/chart/chart-date-adapter.ts b/src/components/chart/chart-date-adapter.ts index 3819a22e88..462ee52dc1 100644 --- a/src/components/chart/chart-date-adapter.ts +++ b/src/components/chart/chart-date-adapter.ts @@ -40,7 +40,7 @@ import { formatDateMonth, formatDateMonthYear, formatDateShort, - formatDateWeekday, + formatDateWeekdayDay, formatDateYear, } from "../../common/datetime/format_date"; import { @@ -92,7 +92,7 @@ _adapters._date.override({ case "hour": return formatTime(new Date(time), this.options.locale); case "weekday": - return formatDateWeekday(new Date(time), this.options.locale); + return formatDateWeekdayDay(new Date(time), this.options.locale); case "date": return formatDate(new Date(time), this.options.locale); case "day": diff --git a/src/dialogs/more-info/controls/more-info-weather.ts b/src/dialogs/more-info/controls/more-info-weather.ts index a680dbd754..01a091b146 100644 --- a/src/dialogs/more-info/controls/more-info-weather.ts +++ b/src/dialogs/more-info/controls/more-info-weather.ts @@ -14,7 +14,7 @@ import { TemplateResult, } from "lit"; import { customElement, property } from "lit/decorators"; -import { formatDateWeekday } from "../../../common/datetime/format_date"; +import { formatDateWeekdayDay } from "../../../common/datetime/format_date"; import { formatTimeWeekday } from "../../../common/datetime/format_time"; import { formatNumber } from "../../../common/number/format_number"; import "../../../components/ha-svg-icon"; @@ -170,7 +170,7 @@ class MoreInfoWeather extends LitElement { ` : html`
- ${formatDateWeekday( + ${formatDateWeekdayDay( new Date(item.datetime), this.hass.locale )} diff --git a/src/panels/calendar/dialog-calendar-event-detail.ts b/src/panels/calendar/dialog-calendar-event-detail.ts index 33609da394..ebe36d920c 100644 --- a/src/panels/calendar/dialog-calendar-event-detail.ts +++ b/src/panels/calendar/dialog-calendar-event-detail.ts @@ -3,13 +3,15 @@ import { mdiCalendarClock, mdiClose } from "@mdi/js"; import { addDays, isSameDay } from "date-fns/esm"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { property, state } from "lit/decorators"; -import { RRule } from "rrule"; +import { RRule, Weekday } from "rrule"; import { formatDate } from "../../common/datetime/format_date"; import { formatDateTime } from "../../common/datetime/format_date_time"; import { formatTime } from "../../common/datetime/format_time"; import { fireEvent } from "../../common/dom/fire_event"; import { capitalizeFirstLetter } from "../../common/string/capitalize-first-letter"; import { isDate } from "../../common/string/is_date"; +import { dayNames } from "../../common/translations/day_names"; +import { monthNames } from "../../common/translations/month_names"; import "../../components/entity/state-info"; import "../../components/ha-date-input"; import "../../components/ha-time-input"; @@ -85,7 +87,7 @@ class DialogCalendarEventDetail extends LitElement {
${this._formatDateRange()}
${this._data!.rrule - ? this._renderRruleAsText(this._data.rrule) + ? this._renderRRuleAsText(this._data.rrule) : ""} ${this._data.description ? html`
@@ -128,20 +130,59 @@ class DialogCalendarEventDetail extends LitElement { `; } - private _renderRruleAsText(value: string) { - // TODO: Make sure this handles translations + private _renderRRuleAsText(value: string) { if (!value) { return ""; } try { - return html`
- ${capitalizeFirstLetter(RRule.fromString(`RRULE:${value}`).toText())} -
`; + const rule = RRule.fromString(`RRULE:${value}`); + if (rule.isFullyConvertibleToText()) { + return html`
+ ${capitalizeFirstLetter( + rule.toText( + this._translateRRuleElement, + { + dayNames: dayNames(this.hass.locale), + monthNames: monthNames(this.hass.locale), + tokens: {}, + }, + this._formatDate + ) + )} +
`; + } + + return html`
Cannot convert recurrence rule
`; } catch (e) { - return ""; + return "Error while processing the rule"; } } + private _translateRRuleElement = (id: string | number | Weekday): string => { + if (typeof id === "string") { + return this.hass.localize(`ui.components.calendar.event.rrule.${id}`); + } + + return ""; + }; + + private _formatDate = (year: number, month: string, day: number): string => { + if (!year || !month || !day) { + return ""; + } + + // Build date so we can then format it + const date = new Date(); + date.setFullYear(year); + // As input we already get the localized month name, so we now unfortunately + // need to convert it back to something Date can work with. The already localized + // months names are a must in the RRule.Language structure (an empty string[] would + // mean we get undefined months input in this method here). + date.setMonth(monthNames(this.hass.locale).indexOf(month)); + date.setDate(day); + return formatDate(date, this.hass.locale); + }; + private _formatDateRange() { const start = new Date(this._data!.dtstart); // All day events should be displayed as a day earlier diff --git a/src/panels/calendar/dialog-calendar-event-editor.ts b/src/panels/calendar/dialog-calendar-event-editor.ts index 7316f27dc3..06dc41c1fe 100644 --- a/src/panels/calendar/dialog-calendar-event-editor.ts +++ b/src/panels/calendar/dialog-calendar-event-editor.ts @@ -330,6 +330,13 @@ class DialogCalendarEventEditor extends LitElement { } private async _createEvent() { + if (!this._summary || !this._calendarId) { + this._error = this.hass.localize( + "ui.components.calendar.event.not_all_required_fields" + ); + return; + } + this._submitting = true; try { await createCalendarEvent( @@ -418,6 +425,10 @@ class DialogCalendarEventEditor extends LitElement { state-info { line-height: 40px; } + ha-alert { + display: block; + margin-bottom: 16px; + } ha-textfield, ha-textarea { display: block; diff --git a/src/translations/en.json b/src/translations/en.json index 2ba4a278af..c7e2d39589 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -642,6 +642,7 @@ "all_day": "All Day", "start": "Start", "end": "End", + "not_all_required_fields": "Not all required fields are filled in.", "confirm_delete": { "delete": "Delete Event", "delete_this": "Delete Only This Event", @@ -666,6 +667,30 @@ "daily": "days" } }, + "rrule": { + "every": "every", + "years": "years", + "year": "year", + "months": "months", + "month": "month", + "weeks": "weeks", + "week": "week", + "weekdays": "weekdays", + "weekday": "weekday", + "days": "days", + "day": "day", + "until": "until", + "for": "for", + "in": "in", + "on": "on", + "on the": "on the", + "and": "and", + "or": "or", + "at": "at", + "last": "last", + "time": "time", + "times": "times" + }, "summary": "Summary", "description": "Description" } From 8699f3e3a82c8dc3e7b38127a703d269621b1be4 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 5 Dec 2022 20:59:09 +0100 Subject: [PATCH 28/29] Use rounded corners for calendar (#14525) Co-authored-by: Bram Kragten --- src/panels/calendar/ha-full-calendar.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/panels/calendar/ha-full-calendar.ts b/src/panels/calendar/ha-full-calendar.ts index 569e3d6492..3cd139d303 100644 --- a/src/panels/calendar/ha-full-calendar.ts +++ b/src/panels/calendar/ha-full-calendar.ts @@ -478,6 +478,12 @@ export class HAFullCalendar extends LitElement { .fc-theme-standard .fc-scrollgrid { border: 1px solid var(--divider-color); + border-radius: var(--mdc-shape-small, 4px); + } + + .fc-theme-standard td { + border-bottom-left-radius: var(--mdc-shape-small, 4px); + border-bottom-right-radius: var(--mdc-shape-small, 4px); } .fc-scrollgrid-section-header td { From 08ffe375b9caa507baba09b623dc0c50a9492953 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 5 Dec 2022 22:48:02 +0100 Subject: [PATCH 29/29] Bumped version to 20221205.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c6e0d8889d..4a4e82f951 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20221201.1" +version = "20221205.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md"