diff --git a/src/data/counter.ts b/src/data/counter.ts new file mode 100644 index 0000000000..b01bf65869 --- /dev/null +++ b/src/data/counter.ts @@ -0,0 +1,51 @@ +import { HomeAssistant } from "../types"; + +export interface Counter { + id: string; + name: string; + icon?: string; + initial?: number; + restore?: boolean; + minimum?: number; + maximum?: number; + step?: number; +} + +export interface CounterMutableParams { + name: string; + icon: string; + initial: number; + restore: boolean; + minimum: number; + maximum: number; + step: number; +} + +export const fetchCounter = (hass: HomeAssistant) => + hass.callWS({ type: "counter/list" }); + +export const createCounter = ( + hass: HomeAssistant, + values: CounterMutableParams +) => + hass.callWS({ + type: "counter/create", + ...values, + }); + +export const updateCounter = ( + hass: HomeAssistant, + id: string, + updates: Partial +) => + hass.callWS({ + type: "counter/update", + counter_id: id, + ...updates, + }); + +export const deleteCounter = (hass: HomeAssistant, id: string) => + hass.callWS({ + type: "counter/delete", + counter_id: id, + }); diff --git a/src/panels/config/entities/const.ts b/src/panels/config/entities/const.ts index bf9b0c5ad6..ca76edf359 100644 --- a/src/panels/config/entities/const.ts +++ b/src/panels/config/entities/const.ts @@ -5,4 +5,5 @@ export const PLATFORMS_WITH_SETTINGS_TAB = { input_text: "entity-settings-helper-tab", input_boolean: "entity-settings-helper-tab", input_datetime: "entity-settings-helper-tab", + counter: "entity-settings-helper-tab", }; diff --git a/src/panels/config/entities/editor-tabs/settings/entity-settings-helper-tab.ts b/src/panels/config/entities/editor-tabs/settings/entity-settings-helper-tab.ts index fbaa723ec8..09c7e268e8 100644 --- a/src/panels/config/entities/editor-tabs/settings/entity-settings-helper-tab.ts +++ b/src/panels/config/entities/editor-tabs/settings/entity-settings-helper-tab.ts @@ -42,6 +42,11 @@ import { fetchInputText, updateInputText, } from "../../../../../data/input_text"; +import { + deleteCounter, + fetchCounter, + updateCounter, +} from "../../../../../data/counter"; import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dialog-box"; import type { HomeAssistant } from "../../../../../types"; import type { Helper } from "../../../helpers/const"; @@ -50,6 +55,7 @@ import "../../../helpers/forms/ha-input_datetime-form"; import "../../../helpers/forms/ha-input_number-form"; import "../../../helpers/forms/ha-input_select-form"; import "../../../helpers/forms/ha-input_text-form"; +import "../../../helpers/forms/ha-counter-form"; import "../../entity-registry-basic-editor"; import type { HaEntityRegistryBasicEditor } from "../../entity-registry-basic-editor"; import { haStyle } from "../../../../../resources/styles"; @@ -80,6 +86,11 @@ const HELPERS = { update: updateInputSelect, delete: deleteInputSelect, }, + counter: { + fetch: fetchCounter, + update: updateCounter, + delete: deleteCounter, + }, }; @customElement("entity-settings-helper-tab") diff --git a/src/panels/config/helpers/const.ts b/src/panels/config/helpers/const.ts index d273290613..22ebcab1a6 100644 --- a/src/panels/config/helpers/const.ts +++ b/src/panels/config/helpers/const.ts @@ -3,6 +3,7 @@ import { InputDateTime } from "../../../data/input_datetime"; import { InputNumber } from "../../../data/input_number"; import { InputSelect } from "../../../data/input_select"; import { InputText } from "../../../data/input_text"; +import { Counter } from "../../../data/counter"; export const HELPER_DOMAINS = [ "input_boolean", @@ -10,6 +11,7 @@ export const HELPER_DOMAINS = [ "input_number", "input_datetime", "input_select", + "counter", ]; export type Helper = @@ -17,4 +19,5 @@ export type Helper = | InputText | InputNumber | InputSelect - | InputDateTime; + | InputDateTime + | Counter; diff --git a/src/panels/config/helpers/dialog-helper-detail.ts b/src/panels/config/helpers/dialog-helper-detail.ts index a7320a5e0e..38fc564544 100644 --- a/src/panels/config/helpers/dialog-helper-detail.ts +++ b/src/panels/config/helpers/dialog-helper-detail.ts @@ -22,6 +22,7 @@ import { createInputDateTime } from "../../../data/input_datetime"; import { createInputNumber } from "../../../data/input_number"; import { createInputSelect } from "../../../data/input_select"; import { createInputText } from "../../../data/input_text"; +import { createCounter } from "../../../data/counter"; import { haStyleDialog } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; import { Helper } from "./const"; @@ -30,6 +31,7 @@ import "./forms/ha-input_datetime-form"; import "./forms/ha-input_number-form"; import "./forms/ha-input_select-form"; import "./forms/ha-input_text-form"; +import "./forms/ha-counter-form"; const HELPERS = { input_boolean: createInputBoolean, @@ -37,6 +39,7 @@ const HELPERS = { input_number: createInputNumber, input_datetime: createInputDateTime, input_select: createInputSelect, + counter: createCounter, }; @customElement("dialog-helper-detail") diff --git a/src/panels/config/helpers/forms/ha-counter-form.ts b/src/panels/config/helpers/forms/ha-counter-form.ts new file mode 100644 index 0000000000..723d9f134e --- /dev/null +++ b/src/panels/config/helpers/forms/ha-counter-form.ts @@ -0,0 +1,210 @@ +import "@polymer/paper-input/paper-input"; +import { + css, + CSSResult, + customElement, + html, + internalProperty, + LitElement, + property, + TemplateResult, +} from "lit-element"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import "../../../../components/ha-icon-input"; +import "../../../../components/ha-switch"; +import type { HaSwitch } from "../../../../components/ha-switch"; +import { Counter } from "../../../../data/counter"; +import { haStyle } from "../../../../resources/styles"; +import { HomeAssistant } from "../../../../types"; + +@customElement("ha-counter-form") +class HaCounterForm extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public new?: boolean; + + private _item?: Partial; + + @internalProperty() private _name!: string; + + @internalProperty() private _icon!: string; + + @internalProperty() private _maximum?: number; + + @internalProperty() private _minimum?: number; + + @internalProperty() private _restore?: boolean; + + @internalProperty() private _initial?: number; + + @internalProperty() private _step?: number; + + set item(item: Counter) { + this._item = item; + if (item) { + this._name = item.name || ""; + this._icon = item.icon || ""; + this._maximum = item.maximum; + this._minimum = item.minimum; + this._restore = item.restore ?? true; + this._step = item.step ?? 1; + this._initial = item.initial ?? 0; + } else { + this._name = ""; + this._icon = ""; + this._maximum = undefined; + this._minimum = undefined; + this._restore = true; + this._step = 1; + this._initial = 0; + } + } + + public focus() { + this.updateComplete.then(() => + (this.shadowRoot?.querySelector( + "[dialogInitialFocus]" + ) as HTMLElement)?.focus() + ); + } + + protected render(): TemplateResult { + if (!this.hass) { + return html``; + } + const nameInvalid = !this._name || this._name.trim() === ""; + + return html` +
+ + + + + + ${this.hass.userData?.showAdvanced + ? html` + +
+ + +
+ ${this.hass.localize( + "ui.dialogs.helper_settings.counter.restore" + )} +
+
+ ` + : ""} +
+ `; + } + + private _valueChanged(ev: CustomEvent) { + if (!this.new && !this._item) { + return; + } + ev.stopPropagation(); + const target = ev.target as any; + const configValue = target.configValue; + const value = + target.type === "number" + ? Number(ev.detail.value) + : target.localName === "ha-switch" + ? (ev.target as HaSwitch).checked + : ev.detail.value; + if (this[`_${configValue}`] === value) { + return; + } + const newValue = { ...this._item }; + if (value === undefined || value === "") { + delete newValue[configValue]; + } else { + newValue[configValue] = value; + } + fireEvent(this, "value-changed", { + value: newValue, + }); + } + + static get styles(): CSSResult[] { + return [ + haStyle, + css` + .form { + color: var(--primary-text-color); + } + .row { + margin-top: 12px; + margin-bottom: 12px; + color: var(--primary-text-color); + display: flex; + align-items: center; + } + .row div { + margin-left: 16px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-counter-form": HaCounterForm; + } +} diff --git a/src/translations/en.json b/src/translations/en.json index 419d8f1ae0..1491539715 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -601,6 +601,13 @@ "add_option": "Add option", "no_options": "There are no options yet.", "add": "Add" + }, + "counter": { + "minimum": "Minimum value", + "maximum": "Maximum value", + "initial": "Initial value", + "restore": "Restore the last known value when Home Assistant starts", + "step": "Step size" } }, "options_flow": { @@ -784,7 +791,8 @@ "input_number": "Number", "input_select": "Dropdown", "input_boolean": "Toggle", - "input_datetime": "Date and/or time" + "input_datetime": "Date and/or time", + "counter": "Counter" }, "picker": { "headers": {