mirror of
https://github.com/home-assistant/frontend.git
synced 2026-07-02 13:11:53 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 674755e430 | |||
| f84664909f | |||
| 4fd631f229 | |||
| 18cf41b793 | |||
| e28788cb95 | |||
| f81b43491d | |||
| 23335fffdb | |||
| 0a93a681e3 | |||
| 7bc2cad83e | |||
| 39ee60a8ef |
+4
-4
@@ -102,7 +102,7 @@
|
||||
"gulp-zopfli-green": "7.0.0",
|
||||
"hls.js": "1.6.16",
|
||||
"home-assistant-js-websocket": "9.6.0",
|
||||
"idb-keyval": "6.2.5",
|
||||
"idb-keyval": "6.2.6",
|
||||
"intl-messageformat": "11.2.9",
|
||||
"js-yaml": "5.2.0",
|
||||
"leaflet": "1.9.4",
|
||||
@@ -147,7 +147,7 @@
|
||||
"@octokit/plugin-retry": "8.1.0",
|
||||
"@octokit/rest": "22.0.1",
|
||||
"@playwright/test": "1.61.1",
|
||||
"@rsdoctor/rspack-plugin": "1.5.16",
|
||||
"@rsdoctor/rspack-plugin": "1.5.17",
|
||||
"@rspack/core": "2.1.1",
|
||||
"@rspack/dev-server": "2.1.0",
|
||||
"@types/babel__plugin-transform-runtime": "7.9.5",
|
||||
@@ -172,7 +172,7 @@
|
||||
"eslint": "10.6.0",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"eslint-import-resolver-webpack": "0.13.11",
|
||||
"eslint-plugin-import-x": "4.17.0",
|
||||
"eslint-plugin-import-x": "4.17.1",
|
||||
"eslint-plugin-lit": "2.3.1",
|
||||
"eslint-plugin-lit-a11y": "5.1.1",
|
||||
"eslint-plugin-unused-imports": "4.4.1",
|
||||
@@ -198,7 +198,7 @@
|
||||
"map-stream": "0.0.7",
|
||||
"minify-literals": "2.0.2",
|
||||
"pinst": "3.0.0",
|
||||
"prettier": "3.9.1",
|
||||
"prettier": "3.9.3",
|
||||
"rspack-manifest-plugin": "5.2.2",
|
||||
"serve": "14.2.6",
|
||||
"sinon": "22.0.0",
|
||||
|
||||
@@ -525,10 +525,10 @@ export class HaServiceControl extends LitElement {
|
||||
this._manifest
|
||||
? html` <a
|
||||
href=${
|
||||
this._manifest.is_built_in
|
||||
this._manifest.is_built_in && this._value?.action
|
||||
? documentationUrl(
|
||||
this.hass,
|
||||
`/integrations/${this._manifest.domain}`
|
||||
`/actions/${this._value.action}`
|
||||
)
|
||||
: this._manifest.documentation
|
||||
}
|
||||
|
||||
@@ -127,8 +127,6 @@ export class HaInput extends WaInputMixin(LitElement) {
|
||||
@query("wa-input")
|
||||
private _input?: WaInput;
|
||||
|
||||
private _startSlotResizeObserver?: ResizeObserver;
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
protected i18n?: ContextType<typeof internationalizationContext>;
|
||||
@@ -169,15 +167,9 @@ export class HaInput extends WaInputMixin(LitElement) {
|
||||
// Wait for wa-input to finish its first render
|
||||
await this._input?.updateComplete;
|
||||
this._syncStartSlotWidth();
|
||||
this._observeStartSlot();
|
||||
}
|
||||
}
|
||||
|
||||
public override disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this._startSlotResizeObserver?.disconnect();
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const hasLabelSlot = this.label
|
||||
? false
|
||||
@@ -299,25 +291,6 @@ export class HaInput extends WaInputMixin(LitElement) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
// Safari can report the start-slot width as 0 during the first render, which
|
||||
// leaves the floating label overlapping the start icon (e.g. the magnify icon
|
||||
// in ha-input-search). Re-sync whenever the wrapper's size changes
|
||||
// (0 -> icon width, or hidden -> shown) so the label padding stays correct.
|
||||
private _observeStartSlot() {
|
||||
if (typeof ResizeObserver === "undefined") {
|
||||
return;
|
||||
}
|
||||
const startEl = this._input?.shadowRoot?.querySelector('[part~="start"]');
|
||||
if (!startEl) {
|
||||
return;
|
||||
}
|
||||
this._startSlotResizeObserver?.disconnect();
|
||||
this._startSlotResizeObserver = new ResizeObserver(() =>
|
||||
this._syncStartSlotWidth()
|
||||
);
|
||||
this._startSlotResizeObserver.observe(startEl);
|
||||
}
|
||||
|
||||
private _syncStartSlotWidth = () => {
|
||||
const startEl = this._input?.shadowRoot?.querySelector(
|
||||
'[part~="start"]'
|
||||
|
||||
+16
-6
@@ -27,6 +27,7 @@ export interface LogbookEntry {
|
||||
source?: string; // The trigger source (English phrase, parsed for the cause)
|
||||
domain?: string;
|
||||
state?: string; // The state of the entity
|
||||
attributes?: { event_type?: string }; // Selected attributes the backend surfaces
|
||||
// Context data
|
||||
context_id?: string;
|
||||
context_user_id?: string;
|
||||
@@ -244,13 +245,13 @@ export const parseTriggerSource = (source: string): ParsedTriggerSource => {
|
||||
};
|
||||
|
||||
// Short label shown instead of the bare timestamp for each timestamp-state
|
||||
// domain. Typed to TIMESTAMP_STATE_DOMAINS minus datetime (a real value), so a
|
||||
// new timestamp domain won't compile until it gets a label here.
|
||||
// domain. Typed to TIMESTAMP_STATE_DOMAINS minus datetime (a real value) and
|
||||
// event (handled separately via its event type), so a new timestamp domain
|
||||
// won't compile until it gets a label here.
|
||||
type LogbookActionMessage =
|
||||
| "pressed"
|
||||
| "activated"
|
||||
| "scanned"
|
||||
| "detected_event_no_type"
|
||||
| "updated"
|
||||
| "sent"
|
||||
| "detected"
|
||||
@@ -261,14 +262,13 @@ type LogbookActionMessage =
|
||||
| "command_sent";
|
||||
|
||||
const STATE_ACTION_MESSAGES: Record<
|
||||
Exclude<TimestampStateDomain, "datetime">,
|
||||
Exclude<TimestampStateDomain, "datetime" | "event">,
|
||||
LogbookActionMessage
|
||||
> = {
|
||||
button: "pressed",
|
||||
input_button: "pressed",
|
||||
scene: "activated",
|
||||
tag: "scanned",
|
||||
event: "detected_event_no_type",
|
||||
image: "updated",
|
||||
notify: "sent",
|
||||
wake_word: "detected",
|
||||
@@ -284,8 +284,18 @@ export const localizeStateMessage = (
|
||||
hass: HomeAssistant,
|
||||
state: string,
|
||||
stateObj: HassEntity,
|
||||
domain: string
|
||||
domain: string,
|
||||
attributes?: LogbookEntry["attributes"]
|
||||
): string => {
|
||||
// Events show the triggered event type, falling back to a generic label when
|
||||
// the type is unknown (the timestamp state is meaningless on its own).
|
||||
if (domain === "event") {
|
||||
const eventType = attributes?.event_type;
|
||||
if (eventType != null) {
|
||||
return hass.formatEntityAttributeValue(stateObj, "event_type", eventType);
|
||||
}
|
||||
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.detected_event_no_type`);
|
||||
}
|
||||
const actionKey: LogbookActionMessage | undefined =
|
||||
STATE_ACTION_MESSAGES[domain as keyof typeof STATE_ACTION_MESSAGES];
|
||||
if (actionKey) {
|
||||
|
||||
@@ -195,7 +195,7 @@ export class HaPlatformCondition extends LitElement {
|
||||
this._manifest.is_built_in
|
||||
? documentationUrl(
|
||||
this.hass,
|
||||
`/integrations/${this._manifest.domain}`
|
||||
`/conditions/${this.condition.condition}`
|
||||
)
|
||||
: this._manifest.documentation
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ export class HaPlatformTrigger extends LitElement {
|
||||
this._manifest.is_built_in
|
||||
? documentationUrl(
|
||||
this.hass,
|
||||
`/integrations/${this._manifest.domain}`
|
||||
`/triggers/${this.trigger.trigger}`
|
||||
)
|
||||
: this._manifest.documentation
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||
import type { LocalizeKeys } from "../../../../common/translations/localize";
|
||||
import { debounce } from "../../../../common/util/debounce";
|
||||
import "../../../../components/ha-alert";
|
||||
import "../../../../components/ha-button";
|
||||
@@ -40,6 +41,15 @@ For loop example getting entity values in the weather domain:
|
||||
{{ state.name | lower }} is {{state.state_with_unit}}
|
||||
{%- endfor %}.`;
|
||||
|
||||
// key resolves the label/description translation keys; path is passed through
|
||||
// documentationUrl().
|
||||
const TEMPLATE_DOCS_LINKS: { key: string; path: string }[] = [
|
||||
{ key: "docs_introduction", path: "/docs/templating/introduction/" },
|
||||
{ key: "docs_states", path: "/docs/templating/states/" },
|
||||
{ key: "docs_debugging", path: "/docs/templating/debugging/" },
|
||||
{ key: "docs_functions", path: "/template-functions/" },
|
||||
];
|
||||
|
||||
@customElement("developer-tools-template")
|
||||
class HaPanelDevTemplate extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@@ -120,31 +130,36 @@ class HaPanelDevTemplate extends LitElement {
|
||||
"ui.panel.config.developer-tools.tabs.templates.description"
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.developer-tools.tabs.templates.engine_info"
|
||||
)}
|
||||
</p>
|
||||
<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.developer-tools.tabs.templates.learn_more"
|
||||
)}
|
||||
</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://jinja.palletsprojects.com/en/latest/templates/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.developer-tools.tabs.templates.jinja_documentation"
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href=${documentationUrl(
|
||||
this.hass,
|
||||
"/docs/configuration/templating/"
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.developer-tools.tabs.templates.template_extensions"
|
||||
)}</a
|
||||
>
|
||||
</li>
|
||||
${TEMPLATE_DOCS_LINKS.map(
|
||||
(link) => html`
|
||||
<li>
|
||||
<a
|
||||
href=${documentationUrl(this.hass, link.path)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>${this.hass.localize(
|
||||
`ui.panel.config.developer-tools.tabs.templates.${link.key}` as LocalizeKeys
|
||||
)}</a
|
||||
>
|
||||
<span class="link-description"
|
||||
>${this.hass.localize(
|
||||
`ui.panel.config.developer-tools.tabs.templates.${link.key}_description` as LocalizeKeys
|
||||
)}</span
|
||||
>
|
||||
</li>
|
||||
`
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</ha-expansion-panel>
|
||||
@@ -441,6 +456,17 @@ ${
|
||||
margin-block-start: var(--ha-space-1);
|
||||
margin-block-end: var(--ha-space-1);
|
||||
}
|
||||
.description > h3 {
|
||||
font-size: var(--ha-font-size-m);
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
margin-block-end: var(--ha-space-1);
|
||||
}
|
||||
.description li {
|
||||
margin-block-end: var(--ha-space-1);
|
||||
}
|
||||
.description .link-description {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.render-pane .card-content {
|
||||
user-select: text;
|
||||
|
||||
@@ -273,7 +273,13 @@ const computeLogbookValue = (
|
||||
if (item.entity_id && item.state) {
|
||||
return {
|
||||
text: stateObj
|
||||
? localizeStateMessage(hass, item.state, stateObj, domain!)
|
||||
? localizeStateMessage(
|
||||
hass,
|
||||
item.state,
|
||||
stateObj,
|
||||
domain!,
|
||||
item.attributes
|
||||
)
|
||||
: item.state,
|
||||
type: "state",
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ import { mdiVolumeHigh, mdiVolumeOff } from "@mdi/js";
|
||||
import { html, nothing } from "lit";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { supportsFeature } from "../../../../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
import "../../../../components/ha-control-button";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import { forwardHaptic } from "../../../../data/haptics";
|
||||
@@ -12,7 +13,7 @@ import {
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
|
||||
export const renderMuteButton = (
|
||||
hass: HomeAssistant,
|
||||
localize: LocalizeFunc,
|
||||
stateObj: MediaPlayerEntity,
|
||||
showMuteButton: boolean | undefined,
|
||||
disabled: boolean,
|
||||
@@ -28,7 +29,7 @@ export const renderMuteButton = (
|
||||
return html`
|
||||
<ha-control-button
|
||||
class="mute"
|
||||
.label=${hass.localize(
|
||||
.label=${localize(
|
||||
`ui.card.media_player.${isMuted ? "media_volume_unmute" : "media_volume_mute"}`
|
||||
)}
|
||||
.disabled=${disabled}
|
||||
@@ -43,13 +44,13 @@ export const renderMuteButton = (
|
||||
|
||||
export const toggleMediaPlayerMute = (
|
||||
ev: Event,
|
||||
hass: HomeAssistant,
|
||||
callService: HomeAssistant["callService"],
|
||||
stateObj: MediaPlayerEntity,
|
||||
el: HTMLElement
|
||||
): void => {
|
||||
ev.stopPropagation();
|
||||
forwardHaptic(el, "light");
|
||||
hass.callService("media_player", "volume_mute", {
|
||||
callService("media_player", "volume_mute", {
|
||||
entity_id: stateObj.entity_id,
|
||||
is_volume_muted: !stateObj.attributes.is_volume_muted,
|
||||
});
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { mdiShieldOff } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-control-select";
|
||||
@@ -21,8 +28,9 @@ import {
|
||||
setProtectedAlarmControlPanelMode,
|
||||
supportedAlarmModes,
|
||||
} from "../../../data/alarm_control_panel";
|
||||
import { apiContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HomeAssistant, HomeAssistantApi } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
@@ -31,6 +39,11 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsAlarmModesCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "alarm_control_panel";
|
||||
};
|
||||
|
||||
export const supportsAlarmModesCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -39,8 +52,7 @@ export const supportsAlarmModesCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "alarm_control_panel";
|
||||
return supportsAlarmModesCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-alarm-modes-card-feature")
|
||||
@@ -48,10 +60,20 @@ class HuiAlarmModeCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: AlarmControlPanelEntity;
|
||||
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state() private _config?: AlarmModesCardFeatureConfig;
|
||||
|
||||
@state() _currentMode?: AlarmMode;
|
||||
@@ -74,25 +96,10 @@ class HuiAlarmModeCardFeature
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id] as
|
||||
AlarmControlPanelEntity | undefined;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues<this>): void {
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (
|
||||
(changedProp.has("hass") || changedProp.has("context")) &&
|
||||
this._stateObj
|
||||
) {
|
||||
const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
|
||||
const oldStateObj = oldHass?.states[this.context!.entity_id!];
|
||||
if (oldStateObj !== this._stateObj) {
|
||||
this._currentMode = this._getCurrentMode(this._stateObj);
|
||||
}
|
||||
if (changedProp.has("_stateObj") && this._stateObj) {
|
||||
this._currentMode = this._getCurrentMode(this._stateObj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +133,11 @@ class HuiAlarmModeCardFeature
|
||||
private async _setMode(mode: AlarmMode) {
|
||||
await setProtectedAlarmControlPanelMode(
|
||||
this,
|
||||
this.hass!,
|
||||
{
|
||||
callService: this._api.callService,
|
||||
callWS: this._api.callWS,
|
||||
localize: this._localize,
|
||||
},
|
||||
this._stateObj!,
|
||||
mode
|
||||
);
|
||||
@@ -135,10 +146,9 @@ class HuiAlarmModeCardFeature
|
||||
protected render(): TemplateResult | typeof nothing {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsAlarmModesCardFeature(this.hass, this.context)
|
||||
!supportsAlarmModesCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -152,7 +162,7 @@ class HuiAlarmModeCardFeature
|
||||
this._config.modes
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.localize(`ui.card.alarm_control_panel.modes.${mode}`),
|
||||
label: this._localize(`ui.card.alarm_control_panel.modes.${mode}`),
|
||||
path: ALARM_MODES[mode].path,
|
||||
}));
|
||||
|
||||
@@ -160,7 +170,7 @@ class HuiAlarmModeCardFeature
|
||||
return html`
|
||||
<ha-control-button-group>
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.alarm_control_panel.disarm")}
|
||||
.label=${this._localize("ui.card.alarm_control_panel.disarm")}
|
||||
@click=${this._disarm}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiShieldOff}></ha-svg-icon>
|
||||
@@ -175,7 +185,7 @@ class HuiAlarmModeCardFeature
|
||||
.value=${this._currentMode}
|
||||
@value-changed=${this._valueChanged}
|
||||
hide-option-label
|
||||
.label=${this.hass.localize("ui.card.alarm_control_panel.modes_label")}
|
||||
.label=${this._localize("ui.card.alarm_control_panel.modes_label")}
|
||||
style=${styleMap({
|
||||
"--control-select-color": color,
|
||||
"--modes-count": options.length.toString(),
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, LitElement, nothing, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { consumeEntityState } from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { isNumericFromAttributes } from "../../../common/number/format_number";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
@@ -9,6 +11,11 @@ import type {
|
||||
BarGaugeCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
const supportsBarGaugeCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "sensor" && isNumericFromAttributes(stateObj.attributes);
|
||||
};
|
||||
|
||||
export const supportsBarGaugeCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -17,16 +24,17 @@ export const supportsBarGaugeCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "sensor" && isNumericFromAttributes(stateObj.attributes);
|
||||
return supportsBarGaugeCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-bar-gauge-card-feature")
|
||||
class HuiBarGaugeCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context!: LovelaceCardFeatureContext;
|
||||
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: HassEntity;
|
||||
|
||||
@state() private _config?: BarGaugeCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): BarGaugeCardFeatureConfig {
|
||||
@@ -50,15 +58,13 @@ class HuiBarGaugeCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this.context.entity_id ||
|
||||
!this.hass.states[this.context.entity_id] ||
|
||||
!supportsBarGaugeCardFeature(this.hass, this.context)
|
||||
!this._stateObj ||
|
||||
!supportsBarGaugeCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
const stateObj = this.hass.states[this.context.entity_id];
|
||||
const stateObj = this._stateObj;
|
||||
const min = this._config.min ?? 0;
|
||||
const max = this._config.max ?? 100;
|
||||
const value = parseFloat(stateObj.state);
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import { apiContext, servicesContext } from "../../../data/context";
|
||||
import {
|
||||
hasRequiredScriptFields,
|
||||
requiredScriptFieldsFilled,
|
||||
hasRequiredScriptFieldsForServices,
|
||||
requiredScriptFieldsFilledForServices,
|
||||
} from "../../../data/script";
|
||||
import { showMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info-dialog";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HomeAssistant, HomeAssistantApi } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -17,6 +24,11 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsButtonCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return ["button", "input_button", "scene", "script"].includes(domain);
|
||||
};
|
||||
|
||||
export const supportsButtonCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -25,27 +37,33 @@ export const supportsButtonCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return ["button", "input_button", "scene", "script"].includes(domain);
|
||||
return supportsButtonCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-button-card-feature")
|
||||
class HuiButtonCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: HassEntity;
|
||||
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: servicesContext, subscribe: true })
|
||||
private _services!: HomeAssistant["services"];
|
||||
|
||||
@state() private _config?: ButtonCardFeatureConfig;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as HassEntity | undefined;
|
||||
}
|
||||
|
||||
private _pressButton() {
|
||||
if (!this.hass || !this._stateObj) return;
|
||||
if (!this._stateObj) return;
|
||||
|
||||
const domain = computeDomain(this._stateObj.entity_id);
|
||||
const service =
|
||||
@@ -54,8 +72,12 @@ class HuiButtonCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
if (domain === "script") {
|
||||
const entityId = this._stateObj.entity_id;
|
||||
if (
|
||||
hasRequiredScriptFields(this.hass!, entityId) &&
|
||||
!requiredScriptFieldsFilled(this.hass!, entityId, this._config?.data)
|
||||
hasRequiredScriptFieldsForServices(this._services, entityId) &&
|
||||
!requiredScriptFieldsFilledForServices(
|
||||
this._services,
|
||||
entityId,
|
||||
this._config?.data
|
||||
)
|
||||
) {
|
||||
showMoreInfoDialog(this, {
|
||||
entityId: entityId,
|
||||
@@ -74,7 +96,7 @@ class HuiButtonCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
: {}),
|
||||
};
|
||||
|
||||
this.hass.callService(domain, service, serviceData);
|
||||
this._api.callService(domain, service, serviceData);
|
||||
}
|
||||
|
||||
static getStubConfig(): ButtonCardFeatureConfig {
|
||||
@@ -93,10 +115,9 @@ class HuiButtonCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsButtonCardFeature(this.hass, this.context)
|
||||
!supportsButtonCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -108,10 +129,7 @@ class HuiButtonCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
class="press-button"
|
||||
@click=${this._pressButton}
|
||||
>
|
||||
${
|
||||
this._config.action_name ??
|
||||
this.hass.localize("ui.card.button.press")
|
||||
}
|
||||
${this._config.action_name ?? this._localize("ui.card.button.press")}
|
||||
</ha-control-button>
|
||||
</ha-control-button-group>
|
||||
`;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { mdiFan } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
@@ -12,6 +13,14 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsClimateFanModesCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "climate" &&
|
||||
supportsFeature(stateObj, ClimateEntityFeature.FAN_MODE)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsClimateFanModesCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -20,11 +29,7 @@ export const supportsClimateFanModesCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "climate" &&
|
||||
supportsFeature(stateObj, ClimateEntityFeature.FAN_MODE)
|
||||
);
|
||||
return supportsClimateFanModesCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-climate-fan-modes-card-feature")
|
||||
@@ -63,9 +68,8 @@ class HuiClimateFanModesCardFeature
|
||||
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsClimateFanModesCardFeature(this.hass, this.context)
|
||||
this._stateObj &&
|
||||
supportsClimateFanModesCardFeatureFromState(this._stateObj)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { mdiThermostat } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { html } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
@@ -25,6 +26,11 @@ interface HvacModeOption extends HuiModeSelectOption {
|
||||
iconPath: string;
|
||||
}
|
||||
|
||||
const supportsClimateHvacModesCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "climate";
|
||||
};
|
||||
|
||||
export const supportsClimateHvacModesCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -33,8 +39,7 @@ export const supportsClimateHvacModesCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "climate";
|
||||
return supportsClimateHvacModesCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-climate-hvac-modes-card-feature")
|
||||
@@ -60,7 +65,7 @@ class HuiClimateHvacModesCardFeature
|
||||
protected readonly _serviceAction = "set_hvac_mode";
|
||||
|
||||
protected get _label(): string {
|
||||
return this.hass!.localize("ui.card.climate.mode");
|
||||
return this._localize("ui.card.climate.mode");
|
||||
}
|
||||
|
||||
protected readonly _showDropdownOptionIcons = false;
|
||||
@@ -94,7 +99,7 @@ class HuiClimateHvacModesCardFeature
|
||||
}
|
||||
|
||||
protected _getOptions(): HvacModeOption[] {
|
||||
if (!this._stateObj || !this.hass) {
|
||||
if (!this._stateObj) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -106,7 +111,7 @@ class HuiClimateHvacModesCardFeature
|
||||
return filterModes(orderedHvacModes, this._config?.hvac_modes).map(
|
||||
(mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityState(this._stateObj!, mode),
|
||||
label: this._formatters.formatEntityState(this._stateObj!, mode),
|
||||
iconPath: climateHvacModeIcon(mode),
|
||||
})
|
||||
);
|
||||
@@ -121,9 +126,8 @@ class HuiClimateHvacModesCardFeature
|
||||
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsClimateHvacModesCardFeature(this.hass, this.context)
|
||||
this._stateObj &&
|
||||
supportsClimateHvacModesCardFeatureFromState(this._stateObj)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { mdiTuneVariant } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
@@ -12,6 +13,16 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsClimatePresetModesCardFeatureFromState = (
|
||||
stateObj: HassEntity
|
||||
) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "climate" &&
|
||||
supportsFeature(stateObj, ClimateEntityFeature.PRESET_MODE)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsClimatePresetModesCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -20,11 +31,7 @@ export const supportsClimatePresetModesCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "climate" &&
|
||||
supportsFeature(stateObj, ClimateEntityFeature.PRESET_MODE)
|
||||
);
|
||||
return supportsClimatePresetModesCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-climate-preset-modes-card-feature")
|
||||
@@ -65,9 +72,8 @@ class HuiClimatePresetModesCardFeature
|
||||
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsClimatePresetModesCardFeature(this.hass, this.context)
|
||||
this._stateObj &&
|
||||
supportsClimatePresetModesCardFeatureFromState(this._stateObj)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+14
-8
@@ -1,4 +1,5 @@
|
||||
import { mdiArrowOscillating } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
@@ -12,6 +13,16 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsClimateSwingHorizontalModesCardFeatureFromState = (
|
||||
stateObj: HassEntity
|
||||
) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "climate" &&
|
||||
supportsFeature(stateObj, ClimateEntityFeature.SWING_HORIZONTAL_MODE)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsClimateSwingHorizontalModesCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -20,11 +31,7 @@ export const supportsClimateSwingHorizontalModesCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "climate" &&
|
||||
supportsFeature(stateObj, ClimateEntityFeature.SWING_HORIZONTAL_MODE)
|
||||
);
|
||||
return supportsClimateSwingHorizontalModesCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-climate-swing-horizontal-modes-card-feature")
|
||||
@@ -65,9 +72,8 @@ class HuiClimateSwingHorizontalModesCardFeature
|
||||
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsClimateSwingHorizontalModesCardFeature(this.hass, this.context)
|
||||
this._stateObj &&
|
||||
supportsClimateSwingHorizontalModesCardFeatureFromState(this._stateObj)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { mdiArrowOscillating } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
@@ -12,6 +13,16 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsClimateSwingModesCardFeatureFromState = (
|
||||
stateObj: HassEntity
|
||||
) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "climate" &&
|
||||
supportsFeature(stateObj, ClimateEntityFeature.SWING_MODE)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsClimateSwingModesCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -20,11 +31,7 @@ export const supportsClimateSwingModesCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "climate" &&
|
||||
supportsFeature(stateObj, ClimateEntityFeature.SWING_MODE)
|
||||
);
|
||||
return supportsClimateSwingModesCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-climate-swing-modes-card-feature")
|
||||
@@ -65,9 +72,8 @@ class HuiClimateSwingModesCardFeature
|
||||
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsClimateSwingModesCardFeature(this.hass, this.context)
|
||||
this._stateObj &&
|
||||
supportsClimateSwingModesCardFeatureFromState(this._stateObj)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { mdiMinus, mdiPlus, mdiRestore } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-control-select";
|
||||
import { apiContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HomeAssistant, HomeAssistantApi } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import {
|
||||
@@ -17,6 +24,11 @@ import {
|
||||
type LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsCounterActionsCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "counter";
|
||||
};
|
||||
|
||||
export const supportsCounterActionsCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -25,8 +37,7 @@ export const supportsCounterActionsCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "counter";
|
||||
return supportsCounterActionsCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
interface CounterButton {
|
||||
@@ -65,18 +76,21 @@ class HuiCounterActionsCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: CounterActionsCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: HassEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as HassEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state() private _config?: CounterActionsCardFeatureConfig;
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import("../editor/config-elements/hui-counter-actions-card-feature-editor");
|
||||
@@ -99,10 +113,9 @@ class HuiCounterActionsCardFeature
|
||||
protected render(): TemplateResult | null {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsCounterActionsCardFeature(this.hass, this.context)
|
||||
!supportsCounterActionsCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
@@ -118,7 +131,7 @@ class HuiCounterActionsCardFeature
|
||||
return html`
|
||||
<ha-control-button
|
||||
.entry=${button}
|
||||
.label=${this.hass!.localize(
|
||||
.label=${this._localize(
|
||||
// @ts-ignore
|
||||
`ui.card.counter.actions.${button.translationKey}`
|
||||
)}
|
||||
@@ -138,7 +151,7 @@ class HuiCounterActionsCardFeature
|
||||
private _onActionTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
const entry = (ev.target! as any).entry as CounterButton;
|
||||
this.hass!.callService("counter", entry.serviceName, {
|
||||
this._api.callService("counter", entry.serviceName, {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { mdiStop } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import {
|
||||
computeCloseIcon,
|
||||
computeOpenIcon,
|
||||
} from "../../../common/entity/cover_icon";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { apiContext } from "../../../data/context";
|
||||
import {
|
||||
canClose,
|
||||
canOpen,
|
||||
@@ -17,7 +25,7 @@ import {
|
||||
CoverEntityFeature,
|
||||
type CoverEntity,
|
||||
} from "../../../data/cover";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HomeAssistant, HomeAssistantApi } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -25,6 +33,15 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsCoverOpenCloseCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "cover" &&
|
||||
(supportsFeature(stateObj, CoverEntityFeature.OPEN) ||
|
||||
supportsFeature(stateObj, CoverEntityFeature.CLOSE))
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsCoverOpenCloseCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -33,12 +50,7 @@ export const supportsCoverOpenCloseCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "cover" &&
|
||||
(supportsFeature(stateObj, CoverEntityFeature.OPEN) ||
|
||||
supportsFeature(stateObj, CoverEntityFeature.CLOSE))
|
||||
);
|
||||
return supportsCoverOpenCloseCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-cover-open-close-card-feature")
|
||||
@@ -46,18 +58,21 @@ class HuiCoverOpenCloseCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: CoverOpenCloseCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: CoverEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as CoverEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state() private _config?: CoverOpenCloseCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): CoverOpenCloseCardFeatureConfig {
|
||||
return {
|
||||
@@ -74,21 +89,21 @@ class HuiCoverOpenCloseCardFeature
|
||||
|
||||
private _onOpenTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this.hass!.callService("cover", "open_cover", {
|
||||
this._api.callService("cover", "open_cover", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _onCloseTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this.hass!.callService("cover", "close_cover", {
|
||||
this._api.callService("cover", "close_cover", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _onStopTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this.hass!.callService("cover", "stop_cover", {
|
||||
this._api.callService("cover", "stop_cover", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
@@ -96,10 +111,9 @@ class HuiCoverOpenCloseCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsCoverOpenCloseCardFeature(this.hass, this.context)
|
||||
!supportsCoverOpenCloseCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -110,7 +124,7 @@ class HuiCoverOpenCloseCardFeature
|
||||
supportsFeature(this._stateObj, CoverEntityFeature.OPEN)
|
||||
? html`
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.cover.open_cover")}
|
||||
.label=${this._localize("ui.card.cover.open_cover")}
|
||||
@click=${this._onOpenTap}
|
||||
.disabled=${!canOpen(this._stateObj)}
|
||||
>
|
||||
@@ -125,7 +139,7 @@ class HuiCoverOpenCloseCardFeature
|
||||
supportsFeature(this._stateObj, CoverEntityFeature.STOP)
|
||||
? html`
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.cover.stop_cover")}
|
||||
.label=${this._localize("ui.card.cover.stop_cover")}
|
||||
@click=${this._onStopTap}
|
||||
.disabled=${!canStop(this._stateObj)}
|
||||
>
|
||||
@@ -138,7 +152,7 @@ class HuiCoverOpenCloseCardFeature
|
||||
supportsFeature(this._stateObj, CoverEntityFeature.CLOSE)
|
||||
? html`
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.cover.close_cover")}
|
||||
.label=${this._localize("ui.card.cover.close_cover")}
|
||||
@click=${this._onCloseTap}
|
||||
.disabled=${!canClose(this._stateObj)}
|
||||
>
|
||||
|
||||
@@ -1,17 +1,35 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { computeAttributeNameDisplay } from "../../../common/entity/compute_attribute_display";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateActive } from "../../../common/entity/state_active";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-slider";
|
||||
import { coverSupportsPosition, type CoverEntity } from "../../../data/cover";
|
||||
import {
|
||||
apiContext,
|
||||
entitiesContext,
|
||||
internationalizationContext,
|
||||
} from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../data/entity/entity_attributes";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { FrontendLocaleData } from "../../../data/translation";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantInternationalization,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -19,6 +37,11 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsCoverPositionCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "cover" && coverSupportsPosition(stateObj);
|
||||
};
|
||||
|
||||
export const supportsCoverPositionCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -27,8 +50,7 @@ export const supportsCoverPositionCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "cover" && coverSupportsPosition(stateObj);
|
||||
return supportsCoverPositionCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-cover-position-card-feature")
|
||||
@@ -36,20 +58,34 @@ class HuiCoverPositionCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@property({ attribute: false }) public color?: string;
|
||||
|
||||
@state() private _config?: CoverPositionCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: CoverEntity;
|
||||
|
||||
private get _stateObj(): CoverEntity | undefined {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!];
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: entitiesContext, subscribe: true })
|
||||
private _entities!: HomeAssistant["entities"];
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
@transform<HomeAssistantInternationalization, FrontendLocaleData>({
|
||||
transformer: ({ locale }) => locale,
|
||||
})
|
||||
private _locale?: FrontendLocaleData;
|
||||
|
||||
@state() private _config?: CoverPositionCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): CoverPositionCardFeatureConfig {
|
||||
return {
|
||||
@@ -67,10 +103,9 @@ class HuiCoverPositionCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsCoverPositionCardFeature(this.hass, this.context)
|
||||
!supportsCoverPositionCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -104,14 +139,14 @@ class HuiCoverPositionCardFeature
|
||||
show-handle
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${computeAttributeNameDisplay(
|
||||
this.hass.localize,
|
||||
this._localize,
|
||||
this._stateObj,
|
||||
this.hass.entities,
|
||||
this._entities,
|
||||
"current_position"
|
||||
)}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
.unit=${DOMAIN_ATTRIBUTES_UNITS.cover.current_position}
|
||||
.locale=${this.hass.locale}
|
||||
.locale=${this._locale}
|
||||
></ha-control-slider>
|
||||
`;
|
||||
}
|
||||
@@ -120,7 +155,7 @@ class HuiCoverPositionCardFeature
|
||||
const { value } = ev.detail;
|
||||
if (typeof value !== "number" || isNaN(value)) return;
|
||||
|
||||
this.hass!.callService("cover", "set_cover_position", {
|
||||
this._api.callService("cover", "set_cover_position", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
position: value,
|
||||
});
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { mdiArrowBottomLeft, mdiArrowTopRight, mdiStop } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { apiContext } from "../../../data/context";
|
||||
import {
|
||||
CoverEntityFeature,
|
||||
canCloseTilt,
|
||||
@@ -13,7 +21,7 @@ import {
|
||||
canStopTilt,
|
||||
type CoverEntity,
|
||||
} from "../../../data/cover";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HomeAssistant, HomeAssistantApi } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -21,6 +29,15 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsCoverTiltCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "cover" &&
|
||||
(supportsFeature(stateObj, CoverEntityFeature.OPEN_TILT) ||
|
||||
supportsFeature(stateObj, CoverEntityFeature.CLOSE_TILT))
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsCoverTiltCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -29,12 +46,7 @@ export const supportsCoverTiltCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "cover" &&
|
||||
(supportsFeature(stateObj, CoverEntityFeature.OPEN_TILT) ||
|
||||
supportsFeature(stateObj, CoverEntityFeature.CLOSE_TILT))
|
||||
);
|
||||
return supportsCoverTiltCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-cover-tilt-card-feature")
|
||||
@@ -42,18 +54,21 @@ class HuiCoverTiltCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: CoverTiltCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: CoverEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as CoverEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state() private _config?: CoverTiltCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): CoverTiltCardFeatureConfig {
|
||||
return {
|
||||
@@ -70,21 +85,21 @@ class HuiCoverTiltCardFeature
|
||||
|
||||
private _onOpenTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this.hass!.callService("cover", "open_cover_tilt", {
|
||||
this._api.callService("cover", "open_cover_tilt", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _onCloseTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this.hass!.callService("cover", "close_cover_tilt", {
|
||||
this._api.callService("cover", "close_cover_tilt", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _onStopTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this.hass!.callService("cover", "stop_cover_tilt", {
|
||||
this._api.callService("cover", "stop_cover_tilt", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
@@ -92,10 +107,9 @@ class HuiCoverTiltCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsCoverTiltCardFeature(this.hass, this.context)
|
||||
!supportsCoverTiltCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -106,7 +120,7 @@ class HuiCoverTiltCardFeature
|
||||
supportsFeature(this._stateObj, CoverEntityFeature.OPEN_TILT)
|
||||
? html`
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.cover.open_tilt_cover")}
|
||||
.label=${this._localize("ui.card.cover.open_tilt_cover")}
|
||||
@click=${this._onOpenTap}
|
||||
.disabled=${!canOpenTilt(this._stateObj)}
|
||||
>
|
||||
@@ -119,7 +133,7 @@ class HuiCoverTiltCardFeature
|
||||
supportsFeature(this._stateObj, CoverEntityFeature.STOP_TILT)
|
||||
? html`
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.cover.stop_cover")}
|
||||
.label=${this._localize("ui.card.cover.stop_cover")}
|
||||
@click=${this._onStopTap}
|
||||
.disabled=${!canStopTilt(this._stateObj)}
|
||||
>
|
||||
@@ -132,7 +146,7 @@ class HuiCoverTiltCardFeature
|
||||
supportsFeature(this._stateObj, CoverEntityFeature.CLOSE_TILT)
|
||||
? html`
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.cover.close_tilt_cover")}
|
||||
.label=${this._localize("ui.card.cover.close_tilt_cover")}
|
||||
@click=${this._onCloseTap}
|
||||
.disabled=${!canCloseTilt(this._stateObj)}
|
||||
>
|
||||
|
||||
@@ -1,17 +1,35 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { computeAttributeNameDisplay } from "../../../common/entity/compute_attribute_display";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import type { CoverEntity } from "../../../data/cover";
|
||||
import { coverSupportsTiltPosition } from "../../../data/cover";
|
||||
import {
|
||||
apiContext,
|
||||
entitiesContext,
|
||||
internationalizationContext,
|
||||
} from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../data/entity/entity_attributes";
|
||||
import type { FrontendLocaleData } from "../../../data/translation";
|
||||
import { generateTiltSliderTrackBackgroundGradient } from "../../../state-control/cover/ha-state-control-cover-tilt-position";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantInternationalization,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -21,6 +39,15 @@ import type {
|
||||
|
||||
const GRADIENT = generateTiltSliderTrackBackgroundGradient();
|
||||
|
||||
const supportsCoverTiltPositionCardFeatureFromState = (
|
||||
stateObj: HassEntity
|
||||
) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "cover" && coverSupportsTiltPosition(stateObj as CoverEntity)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsCoverTiltPositionCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -29,10 +56,7 @@ export const supportsCoverTiltPositionCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "cover" && coverSupportsTiltPosition(stateObj as CoverEntity)
|
||||
);
|
||||
return supportsCoverTiltPositionCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-cover-tilt-position-card-feature")
|
||||
@@ -40,20 +64,34 @@ class HuiCoverTiltPositionCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@property({ attribute: false }) public color?: string;
|
||||
|
||||
@state() private _config?: CoverTiltPositionCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: CoverEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as CoverEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: entitiesContext, subscribe: true })
|
||||
private _entities!: HomeAssistant["entities"];
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
@transform<HomeAssistantInternationalization, FrontendLocaleData>({
|
||||
transformer: ({ locale }) => locale,
|
||||
})
|
||||
private _locale?: FrontendLocaleData;
|
||||
|
||||
@state() private _config?: CoverTiltPositionCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): CoverTiltPositionCardFeatureConfig {
|
||||
return {
|
||||
@@ -71,10 +109,9 @@ class HuiCoverTiltPositionCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsCoverTiltPositionCardFeature(this.hass, this.context)
|
||||
!supportsCoverTiltPositionCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -105,14 +142,14 @@ class HuiCoverTiltPositionCardFeature
|
||||
inverted
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${computeAttributeNameDisplay(
|
||||
this.hass.localize,
|
||||
this._localize,
|
||||
this._stateObj,
|
||||
this.hass.entities,
|
||||
this._entities,
|
||||
"current_tilt_position"
|
||||
)}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
.unit=${DOMAIN_ATTRIBUTES_UNITS.cover.current_tilt_position}
|
||||
.locale=${this.hass.locale}
|
||||
.locale=${this._locale}
|
||||
>
|
||||
<div slot="background" class="gradient"></div
|
||||
></ha-control-slider>
|
||||
@@ -123,7 +160,7 @@ class HuiCoverTiltPositionCardFeature
|
||||
const { value } = ev.detail;
|
||||
if (typeof value !== "number" || isNaN(value)) return;
|
||||
|
||||
this.hass!.callService("cover", "set_cover_tilt_position", {
|
||||
this._api.callService("cover", "set_cover_tilt_position", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
tilt_position: value,
|
||||
});
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { firstWeekdayIndex } from "../../../common/datetime/first_weekday";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import {
|
||||
fireEvent,
|
||||
type HASSDomCurrentTargetEvent,
|
||||
} from "../../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-control-slider";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { apiContext, internationalizationContext } from "../../../data/context";
|
||||
import type { FrontendLocaleData } from "../../../data/translation";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantInternationalization,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -20,6 +34,14 @@ import type {
|
||||
const loadDatePickerDialog = () =>
|
||||
import("../../../components/date-picker/ha-dialog-date-picker");
|
||||
|
||||
const supportsDateSetCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
(domain === "input_datetime" && stateObj.attributes.has_date) ||
|
||||
["datetime", "date"].includes(domain)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsDateSetCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -28,32 +50,38 @@ export const supportsDateSetCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
(domain === "input_datetime" && stateObj.attributes.has_date) ||
|
||||
["datetime", "date"].includes(domain)
|
||||
);
|
||||
return supportsDateSetCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-date-set-card-feature")
|
||||
class HuiDateSetCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@property({ attribute: false }) public color?: string;
|
||||
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: HassEntity;
|
||||
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
@transform<HomeAssistantInternationalization, FrontendLocaleData>({
|
||||
transformer: ({ locale }) => locale,
|
||||
})
|
||||
private _locale?: FrontendLocaleData;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state() private _config?: DateSetCardFeatureConfig;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] ?? undefined;
|
||||
}
|
||||
|
||||
private _pressButton(ev: HASSDomCurrentTargetEvent<HTMLElement>) {
|
||||
if (!this.hass || !this._stateObj) return;
|
||||
if (!this._stateObj || !this._locale) return;
|
||||
|
||||
fireEvent(this, "show-dialog", {
|
||||
dialogTag: "ha-dialog-date-picker",
|
||||
@@ -63,14 +91,14 @@ class HuiDateSetCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
min: "1970-01-01",
|
||||
value: this._stateObj.state,
|
||||
onChange: (value) => this._dateChanged(value),
|
||||
locale: this.hass.locale.language,
|
||||
firstWeekday: firstWeekdayIndex(this.hass.locale),
|
||||
locale: this._locale.language,
|
||||
firstWeekday: firstWeekdayIndex(this._locale),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _dateChanged(value: string | undefined) {
|
||||
if (!this.hass || !this._stateObj || !value) return;
|
||||
if (!this._stateObj || !value) return;
|
||||
|
||||
const domain = computeDomain(this._stateObj.entity_id);
|
||||
const service = domain === "input_datetime" ? "set_datetime" : "set_value";
|
||||
@@ -85,12 +113,12 @@ class HuiDateSetCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
selectedDate.getDate()
|
||||
);
|
||||
|
||||
this.hass.callService(domain, service, {
|
||||
this._api.callService(domain, service, {
|
||||
entity_id: this._stateObj.entity_id,
|
||||
datetime: dateObj.toISOString(),
|
||||
});
|
||||
} else {
|
||||
this.hass.callService(domain, service, {
|
||||
this._api.callService(domain, service, {
|
||||
entity_id: this._stateObj.entity_id,
|
||||
date: value,
|
||||
});
|
||||
@@ -113,10 +141,10 @@ class HuiDateSetCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsDateSetCardFeature(this.hass, this.context)
|
||||
!this._locale ||
|
||||
!supportsDateSetCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -128,7 +156,7 @@ class HuiDateSetCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
class="press-button"
|
||||
@click=${this._pressButton}
|
||||
>
|
||||
${this.hass.localize("ui.card.date.set_date")}
|
||||
${this._localize("ui.card.date.set_date")}
|
||||
</ha-control-button>
|
||||
</ha-control-button-group>
|
||||
`;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
@@ -16,6 +17,13 @@ import type {
|
||||
|
||||
const FAN_DIRECTIONS: FanDirection[] = ["forward", "reverse"];
|
||||
|
||||
const supportsFanDirectionCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "fan" && supportsFeature(stateObj, FanEntityFeature.DIRECTION)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsFanDirectionCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -24,10 +32,7 @@ export const supportsFanDirectionCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "fan" && supportsFeature(stateObj, FanEntityFeature.DIRECTION)
|
||||
);
|
||||
return supportsFanDirectionCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-fan-direction-card-feature")
|
||||
@@ -52,21 +57,19 @@ class HuiFanDirectionCardFeature
|
||||
}
|
||||
|
||||
protected _getOptions(): HuiModeSelectOption[] {
|
||||
if (!this.hass) {
|
||||
if (!this._stateObj) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return FAN_DIRECTIONS.map((direction) => ({
|
||||
value: direction,
|
||||
label: this.hass!.localize(`ui.card.fan.${direction}`),
|
||||
label: this._localize(`ui.card.fan.${direction}`),
|
||||
}));
|
||||
}
|
||||
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsFanDirectionCardFeature(this.hass, this.context)
|
||||
this._stateObj && supportsFanDirectionCardFeatureFromState(this._stateObj)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { mdiArrowOscillating, mdiArrowOscillatingOff } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-select";
|
||||
import type { ControlSelectOption } from "../../../components/ha-control-select";
|
||||
import { apiContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import type { FanEntity } from "../../../data/fan";
|
||||
import { FanEntityFeature } from "../../../data/fan";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HomeAssistant, HomeAssistantApi } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -20,6 +28,13 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsFanOscillateCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "fan" && supportsFeature(stateObj, FanEntityFeature.OSCILLATE)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsFanOscilatteCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -28,10 +43,7 @@ export const supportsFanOscilatteCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "fan" && supportsFeature(stateObj, FanEntityFeature.OSCILLATE)
|
||||
);
|
||||
return supportsFanOscillateCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-fan-oscillate-card-feature")
|
||||
@@ -39,21 +51,24 @@ class HuiFanOscillateCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: FanEntity;
|
||||
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state() private _config?: FanOscillateCardFeatureConfig;
|
||||
|
||||
@state() _oscillate?: boolean;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as FanEntity | undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): FanOscillateCardFeatureConfig {
|
||||
return {
|
||||
type: "fan-oscillate",
|
||||
@@ -67,16 +82,9 @@ class HuiFanOscillateCardFeature
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues<this>): void {
|
||||
if (
|
||||
(changedProp.has("hass") || changedProp.has("context")) &&
|
||||
this._stateObj
|
||||
) {
|
||||
const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
|
||||
const oldStateObj = oldHass?.states[this.context!.entity_id!];
|
||||
if (oldStateObj !== this._stateObj) {
|
||||
this._oscillate = this._stateObj.attributes.oscillating;
|
||||
}
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
if (changedProp.has("_stateObj") && this._stateObj) {
|
||||
this._oscillate = this._stateObj.attributes.oscillating;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +106,7 @@ class HuiFanOscillateCardFeature
|
||||
}
|
||||
|
||||
private async _updateOscillate(oscillate: boolean) {
|
||||
await this.hass!.callService("fan", "oscillate", {
|
||||
await this._api.callService("fan", "oscillate", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
oscillating: oscillate,
|
||||
});
|
||||
@@ -107,10 +115,9 @@ class HuiFanOscillateCardFeature
|
||||
protected render(): TemplateResult | null {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsFanOscilatteCardFeature(this.hass, this.context)
|
||||
!supportsFanOscillateCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
@@ -120,7 +127,7 @@ class HuiFanOscillateCardFeature
|
||||
const yesNo = ["no", "yes"] as const;
|
||||
const options = yesNo.map<ControlSelectOption>((oscillating) => ({
|
||||
value: oscillating,
|
||||
label: this.hass!.localize(`ui.common.${oscillating}`),
|
||||
label: this._localize(`ui.common.${oscillating}`),
|
||||
path:
|
||||
oscillating === "yes" ? mdiArrowOscillating : mdiArrowOscillatingOff,
|
||||
}));
|
||||
@@ -131,7 +138,7 @@ class HuiFanOscillateCardFeature
|
||||
.value=${this._oscillate ? "yes" : "no"}
|
||||
@value-changed=${this._valueChanged}
|
||||
hide-option-label
|
||||
.label=${this.hass.localize("ui.card.fan.oscillate")}
|
||||
.label=${this._localize("ui.card.fan.oscillate")}
|
||||
style=${styleMap({
|
||||
"--control-select-color": color,
|
||||
})}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { mdiTuneVariant } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
@@ -12,6 +13,13 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsFanPresetModesCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "fan" && supportsFeature(stateObj, FanEntityFeature.PRESET_MODE)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsFanPresetModesCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -20,10 +28,7 @@ export const supportsFanPresetModesCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "fan" && supportsFeature(stateObj, FanEntityFeature.PRESET_MODE)
|
||||
);
|
||||
return supportsFanPresetModesCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-fan-preset-modes-card-feature")
|
||||
@@ -62,9 +67,8 @@ class HuiFanPresetModesCardFeature
|
||||
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsFanPresetModesCardFeature(this.hass, this.context)
|
||||
this._stateObj &&
|
||||
supportsFanPresetModesCardFeatureFromState(this._stateObj)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,27 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { computeAttributeNameDisplay } from "../../../common/entity/compute_attribute_display";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateActive } from "../../../common/entity/state_active";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-select";
|
||||
import type { ControlSelectOption } from "../../../components/ha-control-select";
|
||||
import "../../../components/ha-control-slider";
|
||||
import {
|
||||
apiContext,
|
||||
entitiesContext,
|
||||
formattersContext,
|
||||
internationalizationContext,
|
||||
} from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../data/entity/entity_attributes";
|
||||
import type { FanEntity, FanSpeed } from "../../../data/fan";
|
||||
@@ -20,7 +34,13 @@ import {
|
||||
fanPercentageToSpeed,
|
||||
fanSpeedToPercentage,
|
||||
} from "../../../data/fan";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { FrontendLocaleData } from "../../../data/translation";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantFormatters,
|
||||
HomeAssistantInternationalization,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -28,6 +48,13 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsFanSpeedCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "fan" && supportsFeature(stateObj, FanEntityFeature.SET_SPEED)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsFanSpeedCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -36,26 +63,41 @@ export const supportsFanSpeedCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "fan" && supportsFeature(stateObj, FanEntityFeature.SET_SPEED)
|
||||
);
|
||||
return supportsFanSpeedCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-fan-speed-card-feature")
|
||||
class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: FanSpeedCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: FanEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as FanEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: formattersContext, subscribe: true })
|
||||
private _formatters!: HomeAssistantFormatters;
|
||||
|
||||
@state()
|
||||
@consume({ context: entitiesContext, subscribe: true })
|
||||
private _entities!: HomeAssistant["entities"];
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
@transform<HomeAssistantInternationalization, FrontendLocaleData>({
|
||||
transformer: ({ locale }) => locale,
|
||||
})
|
||||
private _locale?: FrontendLocaleData;
|
||||
|
||||
@state() private _config?: FanSpeedCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): FanSpeedCardFeatureConfig {
|
||||
return {
|
||||
@@ -72,18 +114,17 @@ class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
|
||||
private _localizeSpeed(speed: FanSpeed) {
|
||||
if (speed === "on" || speed === "off") {
|
||||
return this.hass!.formatEntityState(this._stateObj!, speed);
|
||||
return this._formatters.formatEntityState(this._stateObj!, speed);
|
||||
}
|
||||
return this.hass!.localize(`ui.card.fan.speed.${speed}`) || speed;
|
||||
return this._localize(`ui.card.fan.speed.${speed}`) || speed;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsFanSpeedCardFeature(this.hass, this.context)
|
||||
!supportsFanSpeedCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -112,9 +153,9 @@ class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
@value-changed=${this._speedValueChanged}
|
||||
hide-option-label
|
||||
.label=${computeAttributeNameDisplay(
|
||||
this.hass.localize,
|
||||
this._localize,
|
||||
this._stateObj,
|
||||
this.hass.entities,
|
||||
this._entities,
|
||||
"percentage"
|
||||
)}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
@@ -133,14 +174,14 @@ class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
.step=${this._stateObj.attributes.percentage_step ?? 1}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${computeAttributeNameDisplay(
|
||||
this.hass.localize,
|
||||
this._localize,
|
||||
this._stateObj,
|
||||
this.hass.entities,
|
||||
this._entities,
|
||||
"percentage"
|
||||
)}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
.unit=${DOMAIN_ATTRIBUTES_UNITS.fan.percentage}
|
||||
.locale=${this.hass.locale}
|
||||
.locale=${this._locale}
|
||||
></ha-control-slider>
|
||||
`;
|
||||
}
|
||||
@@ -150,7 +191,7 @@ class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
|
||||
const percentage = fanSpeedToPercentage(this._stateObj!, speed);
|
||||
|
||||
this.hass!.callService("fan", "set_percentage", {
|
||||
this._api.callService("fan", "set_percentage", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
percentage: percentage,
|
||||
});
|
||||
@@ -160,7 +201,7 @@ class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
const { value } = ev.detail;
|
||||
if (typeof value !== "number" || isNaN(value)) return;
|
||||
|
||||
this.hass!.callService("fan", "set_percentage", {
|
||||
this._api.callService("fan", "set_percentage", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
percentage: value,
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { mdiTuneVariant } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
@@ -12,6 +13,14 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsHumidifierModesCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "humidifier" &&
|
||||
supportsFeature(stateObj, HumidifierEntityFeature.MODES)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsHumidifierModesCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -20,11 +29,7 @@ export const supportsHumidifierModesCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "humidifier" &&
|
||||
supportsFeature(stateObj, HumidifierEntityFeature.MODES)
|
||||
);
|
||||
return supportsHumidifierModesCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-humidifier-modes-card-feature")
|
||||
@@ -63,9 +68,8 @@ class HuiHumidifierModesCardFeature
|
||||
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsHumidifierModesCardFeature(this.hass, this.context)
|
||||
this._stateObj &&
|
||||
supportsHumidifierModesCardFeatureFromState(this._stateObj)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,31 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { mdiPower, mdiWaterPercent } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-select";
|
||||
import type { ControlSelectOption } from "../../../components/ha-control-select";
|
||||
import { apiContext, formattersContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import type {
|
||||
HumidifierEntity,
|
||||
HumidifierState,
|
||||
} from "../../../data/humidifier";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantFormatters,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -21,6 +33,11 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsHumidifierToggleCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "humidifier";
|
||||
};
|
||||
|
||||
export const supportsHumidifierToggleCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -29,8 +46,7 @@ export const supportsHumidifierToggleCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "humidifier";
|
||||
return supportsHumidifierToggleCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-humidifier-toggle-card-feature")
|
||||
@@ -38,22 +54,28 @@ class HuiHumidifierToggleCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: HumidifierEntity;
|
||||
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: formattersContext, subscribe: true })
|
||||
private _formatters!: HomeAssistantFormatters;
|
||||
|
||||
@state() private _config?: HumidifierToggleCardFeatureConfig;
|
||||
|
||||
@state() _currentState?: HumidifierState;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
HumidifierEntity | undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): HumidifierToggleCardFeatureConfig {
|
||||
return {
|
||||
type: "humidifier-toggle",
|
||||
@@ -67,17 +89,10 @@ class HuiHumidifierToggleCardFeature
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues<this>): void {
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (
|
||||
(changedProp.has("hass") || changedProp.has("context")) &&
|
||||
this._stateObj
|
||||
) {
|
||||
const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
|
||||
const oldStateObj = oldHass?.states[this.context!.entity_id!];
|
||||
if (oldStateObj !== this._stateObj) {
|
||||
this._currentState = this._stateObj.state as HumidifierState;
|
||||
}
|
||||
if (changedProp.has("_stateObj") && this._stateObj) {
|
||||
this._currentState = this._stateObj.state as HumidifierState;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +114,7 @@ class HuiHumidifierToggleCardFeature
|
||||
}
|
||||
|
||||
private async _setState(newState: HumidifierState) {
|
||||
await this.hass!.callService(
|
||||
await this._api.callService(
|
||||
"humidifier",
|
||||
newState === "on" ? "turn_on" : "turn_off",
|
||||
{
|
||||
@@ -111,10 +126,9 @@ class HuiHumidifierToggleCardFeature
|
||||
protected render(): TemplateResult | null {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsHumidifierToggleCardFeature(this.hass, this.context)
|
||||
!supportsHumidifierToggleCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
@@ -123,7 +137,7 @@ class HuiHumidifierToggleCardFeature
|
||||
|
||||
const options = ["off", "on"].map<ControlSelectOption>((entityState) => ({
|
||||
value: entityState,
|
||||
label: this.hass!.formatEntityState(this._stateObj!, entityState),
|
||||
label: this._formatters.formatEntityState(this._stateObj!, entityState),
|
||||
path: entityState === "on" ? mdiWaterPercent : mdiPower,
|
||||
}));
|
||||
|
||||
@@ -133,7 +147,7 @@ class HuiHumidifierToggleCardFeature
|
||||
.value=${this._currentState}
|
||||
@value-changed=${this._valueChanged}
|
||||
hide-option-label
|
||||
.label=${this.hass.localize("ui.card.humidifier.state")}
|
||||
.label=${this._localize("ui.card.humidifier.state")}
|
||||
style=${styleMap({
|
||||
"--control-select-color": color,
|
||||
})}
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { mdiHomeImportOutline, mdiPause, mdiPlay } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { apiContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import type { LawnMowerEntity } from "../../../data/lawn_mower";
|
||||
import { LawnMowerEntityFeature, canDock } from "../../../data/lawn_mower";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HomeAssistant, HomeAssistantApi } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -75,6 +82,14 @@ export const LAWN_MOWER_COMMANDS_BUTTONS: Record<
|
||||
}),
|
||||
};
|
||||
|
||||
const supportsLawnMowerCommandCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "lawn_mower" &&
|
||||
LAWN_MOWER_COMMANDS.some((c) => supportsLawnMowerCommand(stateObj, c))
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsLawnMowerCommandCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -83,11 +98,7 @@ export const supportsLawnMowerCommandCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "lawn_mower" &&
|
||||
LAWN_MOWER_COMMANDS.some((c) => supportsLawnMowerCommand(stateObj, c))
|
||||
);
|
||||
return supportsLawnMowerCommandCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-lawn-mower-commands-card-feature")
|
||||
@@ -95,19 +106,21 @@ class HuiLawnMowerCommandCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: LawnMowerCommandsCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: LawnMowerEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
LawnMowerEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state() private _config?: LawnMowerCommandsCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): LawnMowerCommandsCardFeatureConfig {
|
||||
return {
|
||||
@@ -132,7 +145,7 @@ class HuiLawnMowerCommandCardFeature
|
||||
private _onCommandTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
const entry = (ev.target! as any).entry as LawnMowerButton;
|
||||
this.hass!.callService("lawn_mower", entry.serviceName, {
|
||||
this._api.callService("lawn_mower", entry.serviceName, {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
@@ -140,10 +153,9 @@ class HuiLawnMowerCommandCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsLawnMowerCommandCardFeature(this.hass, this.context)
|
||||
!supportsLawnMowerCommandCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -161,7 +173,7 @@ class HuiLawnMowerCommandCardFeature
|
||||
return html`
|
||||
<ha-control-button
|
||||
.entry=${button}
|
||||
.label=${this.hass!.localize(
|
||||
.label=${this._localize(
|
||||
// @ts-ignore
|
||||
`ui.dialogs.more_info_control.lawn_mower.${button.translationKey}`
|
||||
)}
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateActive } from "../../../common/entity/state_active";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-slider";
|
||||
import { apiContext, internationalizationContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import { lightSupportsBrightness, type LightEntity } from "../../../data/light";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { FrontendLocaleData } from "../../../data/translation";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantInternationalization,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -13,6 +27,11 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsLightBrightnessCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "light" && lightSupportsBrightness(stateObj);
|
||||
};
|
||||
|
||||
export const supportsLightBrightnessCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -21,8 +40,7 @@ export const supportsLightBrightnessCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "light" && lightSupportsBrightness(stateObj);
|
||||
return supportsLightBrightnessCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-light-brightness-card-feature")
|
||||
@@ -30,18 +48,28 @@ class HuiLightBrightnessCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: LightBrightnessCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: LightEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id] as LightEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
@transform<HomeAssistantInternationalization, FrontendLocaleData>({
|
||||
transformer: ({ locale }) => locale,
|
||||
})
|
||||
private _locale?: FrontendLocaleData;
|
||||
|
||||
@state() private _config?: LightBrightnessCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): LightBrightnessCardFeatureConfig {
|
||||
return {
|
||||
@@ -59,10 +87,9 @@ class HuiLightBrightnessCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsLightBrightnessCardFeature(this.hass, this.context)
|
||||
!supportsLightBrightnessCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -83,9 +110,9 @@ class HuiLightBrightnessCardFeature
|
||||
.showHandle=${stateActive(this._stateObj)}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.localize("ui.card.light.brightness")}
|
||||
.label=${this._localize("ui.card.light.brightness")}
|
||||
unit="%"
|
||||
.locale=${this.hass.locale}
|
||||
.locale=${this._locale}
|
||||
></ha-control-slider>
|
||||
`;
|
||||
}
|
||||
@@ -94,7 +121,7 @@ class HuiLightBrightnessCardFeature
|
||||
ev.stopPropagation();
|
||||
const value = ev.detail.value;
|
||||
|
||||
this.hass!.callService("light", "turn_on", {
|
||||
this._api.callService("light", "turn_on", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
brightness_pct: value,
|
||||
});
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { ResizeController } from "@lit-labs/observers/resize-controller";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing, unsafeCSS } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type {
|
||||
Connection,
|
||||
UnsubscribeFunc,
|
||||
HassEntity,
|
||||
} from "home-assistant-js-websocket";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import { apiContext, connectionContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import {
|
||||
computeDefaultFavoriteColors,
|
||||
@@ -11,7 +23,11 @@ import {
|
||||
type LightColor,
|
||||
lightSupportsFavoriteColors,
|
||||
} from "../../../data/light";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantConnection,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -29,6 +45,13 @@ import { getMoreInfoHintCardFeatureEditor } from "./get-more-info-hint-card-feat
|
||||
const PILL_GAP = 8;
|
||||
const PILL_MIN_SIZE = 32;
|
||||
|
||||
const supportsLightColorFavoritesCardFeatureFromState = (
|
||||
stateObj: HassEntity
|
||||
) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "light" && lightSupportsFavoriteColors(stateObj);
|
||||
};
|
||||
|
||||
export const supportsLightColorFavoritesCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -37,8 +60,7 @@ export const supportsLightColorFavoritesCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "light" && lightSupportsFavoriteColors(stateObj);
|
||||
return supportsLightColorFavoritesCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-light-color-favorites-card-feature")
|
||||
@@ -46,10 +68,27 @@ class HuiLightColorFavoritesCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: LightEntity;
|
||||
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: connectionContext, subscribe: true })
|
||||
@transform<HomeAssistantConnection, Connection>({
|
||||
transformer: ({ connection }) => connection,
|
||||
})
|
||||
private _connection?: Connection;
|
||||
|
||||
@state() private _config?: LightColorFavoritesCardFeatureConfig;
|
||||
|
||||
@state() private _entry?: EntityRegistryEntry | null;
|
||||
@@ -86,11 +125,11 @@ class HuiLightColorFavoritesCardFeature
|
||||
}
|
||||
|
||||
private _subscribeEntityEntry() {
|
||||
if (this.hass && this.context?.entity_id) {
|
||||
if (this._connection && this.context?.entity_id) {
|
||||
const id = this.context.entity_id;
|
||||
try {
|
||||
this._unsubEntityRegistry = subscribeEntityRegistry(
|
||||
this.hass!.connection,
|
||||
this._connection,
|
||||
(entries) => {
|
||||
const entry = entries.find((e) => e.entity_id === id);
|
||||
if (entry) {
|
||||
@@ -108,15 +147,8 @@ class HuiLightColorFavoritesCardFeature
|
||||
return this._resizeController.value ?? 0;
|
||||
}
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id] as LightEntity | undefined;
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
if (changedProps.has("context")) {
|
||||
if (changedProps.has("context") || changedProps.has("_connection")) {
|
||||
this._unsubscribeEntityRegistry();
|
||||
this._subscribeEntityEntry();
|
||||
}
|
||||
@@ -150,10 +182,9 @@ class HuiLightColorFavoritesCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsLightColorFavoritesCardFeature(this.hass, this.context)
|
||||
!supportsLightColorFavoritesCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -165,7 +196,7 @@ class HuiLightColorFavoritesCardFeature
|
||||
${visibleColors.map(
|
||||
(color, index) => html`
|
||||
<ha-favorite-color-button
|
||||
.label=${this.hass!.localize(
|
||||
.label=${this._localize(
|
||||
`ui.dialogs.more_info_control.light.favorite_color.set`,
|
||||
{ number: index }
|
||||
)}
|
||||
@@ -189,7 +220,7 @@ class HuiLightColorFavoritesCardFeature
|
||||
const index = (ev.target! as any).index!;
|
||||
|
||||
const favorite = this._favoriteColors[index];
|
||||
this.hass!.callService("light", "turn_on", {
|
||||
this._api.callService("light", "turn_on", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
...favorite,
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
@@ -6,9 +8,16 @@ import {
|
||||
DEFAULT_MAX_KELVIN,
|
||||
DEFAULT_MIN_KELVIN,
|
||||
} from "../../../common/color/convert-light-color";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateActive } from "../../../common/entity/state_active";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-slider";
|
||||
import { apiContext, internationalizationContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../data/entity/entity_attributes";
|
||||
import {
|
||||
@@ -16,8 +25,13 @@ import {
|
||||
lightSupportsColorMode,
|
||||
type LightEntity,
|
||||
} from "../../../data/light";
|
||||
import type { FrontendLocaleData } from "../../../data/translation";
|
||||
import { generateColorTemperatureGradient } from "../../../dialogs/more-info/components/lights/light-color-temp-picker";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantInternationalization,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -25,6 +39,14 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsLightColorTempCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "light" &&
|
||||
lightSupportsColorMode(stateObj, LightColorMode.COLOR_TEMP)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsLightColorTempCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -33,11 +55,7 @@ export const supportsLightColorTempCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "light" &&
|
||||
lightSupportsColorMode(stateObj, LightColorMode.COLOR_TEMP)
|
||||
);
|
||||
return supportsLightColorTempCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-light-color-temp-card-feature")
|
||||
@@ -45,18 +63,28 @@ class HuiLightColorTempCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: LightColorTempCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: LightEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as LightEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
@transform<HomeAssistantInternationalization, FrontendLocaleData>({
|
||||
transformer: ({ locale }) => locale,
|
||||
})
|
||||
private _locale?: FrontendLocaleData;
|
||||
|
||||
@state() private _config?: LightColorTempCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): LightColorTempCardFeatureConfig {
|
||||
return {
|
||||
@@ -74,10 +102,9 @@ class HuiLightColorTempCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsLightColorTempCardFeature(this.hass, this.context)
|
||||
!supportsLightColorTempCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -101,14 +128,14 @@ class HuiLightColorTempCardFeature
|
||||
.showHandle=${stateActive(this._stateObj)}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.localize("ui.card.light.color_temperature")}
|
||||
.label=${this._localize("ui.card.light.color_temperature")}
|
||||
.min=${minKelvin}
|
||||
.max=${maxKelvin}
|
||||
style=${styleMap({
|
||||
"--gradient": gradient,
|
||||
})}
|
||||
.unit=${DOMAIN_ATTRIBUTES_UNITS.light.color_temp_kelvin}
|
||||
.locale=${this.hass.locale}
|
||||
.locale=${this._locale}
|
||||
></ha-control-slider>
|
||||
`;
|
||||
}
|
||||
@@ -121,7 +148,7 @@ class HuiLightColorTempCardFeature
|
||||
ev.stopPropagation();
|
||||
const value = ev.detail.value;
|
||||
|
||||
this.hass!.callService("light", "turn_on", {
|
||||
this._api.callService("light", "turn_on", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
color_temp_kelvin: value,
|
||||
});
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { mdiLock, mdiLockOpenVariant } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import { apiContext } from "../../../data/context";
|
||||
import { forwardHaptic } from "../../../data/haptics";
|
||||
import {
|
||||
callProtectedLockService,
|
||||
@@ -12,7 +20,7 @@ import {
|
||||
canUnlock,
|
||||
type LockEntity,
|
||||
} from "../../../data/lock";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HomeAssistant, HomeAssistantApi } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -20,6 +28,11 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsLockCommandsCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "lock";
|
||||
};
|
||||
|
||||
export const supportsLockCommandsCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -28,8 +41,7 @@ export const supportsLockCommandsCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "lock";
|
||||
return supportsLockCommandsCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-lock-commands-card-feature")
|
||||
@@ -37,18 +49,21 @@ class HuiLockCommandsCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: LockCommandsCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: LockEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as LockEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state() private _config?: LockCommandsCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): LockCommandsCardFeatureConfig {
|
||||
return {
|
||||
@@ -66,20 +81,28 @@ class HuiLockCommandsCardFeature
|
||||
private _onTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
const service = ev.target.dataset.service;
|
||||
if (!this.hass || !this._stateObj || !service) {
|
||||
if (!this._stateObj || !service) {
|
||||
return;
|
||||
}
|
||||
forwardHaptic(this, "light");
|
||||
callProtectedLockService(this, this.hass, this._stateObj, service);
|
||||
callProtectedLockService(
|
||||
this,
|
||||
{
|
||||
callService: this._api.callService,
|
||||
callWS: this._api.callWS,
|
||||
localize: this._localize,
|
||||
},
|
||||
this._stateObj,
|
||||
service
|
||||
);
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsLockCommandsCardFeature(this.hass, this.context)
|
||||
!supportsLockCommandsCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -87,7 +110,7 @@ class HuiLockCommandsCardFeature
|
||||
return html`
|
||||
<ha-control-button-group>
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.lock.lock")}
|
||||
.label=${this._localize("ui.card.lock.lock")}
|
||||
.disabled=${!canLock(this._stateObj)}
|
||||
@click=${this._onTap}
|
||||
data-service="lock"
|
||||
@@ -95,7 +118,7 @@ class HuiLockCommandsCardFeature
|
||||
<ha-svg-icon .path=${mdiLock}></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.lock.unlock")}
|
||||
.label=${this._localize("ui.card.lock.unlock")}
|
||||
.disabled=${!canUnlock(this._stateObj)}
|
||||
@click=${this._onTap}
|
||||
data-service="unlock"
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { mdiCheck } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import { apiContext } from "../../../data/context";
|
||||
import {
|
||||
callProtectedLockService,
|
||||
canOpen,
|
||||
LockEntityFeature,
|
||||
type LockEntity,
|
||||
} from "../../../data/lock";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HomeAssistant, HomeAssistantApi } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -20,6 +28,11 @@ import type {
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const supportsLockOpenDoorCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "lock" && supportsFeature(stateObj, LockEntityFeature.OPEN);
|
||||
};
|
||||
|
||||
export const supportsLockOpenDoorCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -28,8 +41,7 @@ export const supportsLockOpenDoorCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "lock" && supportsFeature(stateObj, LockEntityFeature.OPEN);
|
||||
return supportsLockOpenDoorCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
const CONFIRM_TIMEOUT_SECOND = 5;
|
||||
@@ -42,23 +54,26 @@ class HuiLockOpenDoorCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: LockEntity;
|
||||
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state() public _buttonState: ButtonState = "normal";
|
||||
|
||||
@state() private _config?: LockOpenDoorCardFeatureConfig;
|
||||
|
||||
private _buttonTimeout?: number;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as LockEntity | undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): LockOpenDoorCardFeatureConfig {
|
||||
return {
|
||||
type: "lock-open-door",
|
||||
@@ -87,10 +102,19 @@ class HuiLockOpenDoorCardFeature
|
||||
this._setButtonState("confirm", CONFIRM_TIMEOUT_SECOND);
|
||||
return;
|
||||
}
|
||||
if (!this.hass || !this._stateObj) {
|
||||
if (!this._stateObj) {
|
||||
return;
|
||||
}
|
||||
callProtectedLockService(this, this.hass, this._stateObj!, "open");
|
||||
callProtectedLockService(
|
||||
this,
|
||||
{
|
||||
callService: this._api.callService,
|
||||
callWS: this._api.callWS,
|
||||
localize: this._localize,
|
||||
},
|
||||
this._stateObj,
|
||||
"open"
|
||||
);
|
||||
|
||||
this._setButtonState("done", DONE_TIMEOUT_SECOND);
|
||||
}
|
||||
@@ -98,10 +122,9 @@ class HuiLockOpenDoorCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsLockOpenDoorCardFeature(this.hass, this.context)
|
||||
!supportsLockOpenDoorCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -112,7 +135,7 @@ class HuiLockOpenDoorCardFeature
|
||||
? html`
|
||||
<p class="open-done">
|
||||
<ha-svg-icon path=${mdiCheck}></ha-svg-icon>
|
||||
${this.hass.localize("ui.card.lock.open_door_done")}
|
||||
${this._localize("ui.card.lock.open_door_done")}
|
||||
</p>
|
||||
`
|
||||
: html`
|
||||
@@ -124,8 +147,8 @@ class HuiLockOpenDoorCardFeature
|
||||
>
|
||||
${
|
||||
this._buttonState === "confirm"
|
||||
? this.hass.localize("ui.card.lock.open_door_confirm")
|
||||
: this.hass.localize("ui.card.lock.open_door")
|
||||
? this._localize("ui.card.lock.open_door_confirm")
|
||||
: this._localize("ui.card.lock.open_door")
|
||||
}
|
||||
</ha-control-button>
|
||||
</ha-control-button-group>
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import { apiContext } from "../../../data/context";
|
||||
import type {
|
||||
ControlButton,
|
||||
MediaPlayerEntity,
|
||||
} from "../../../data/media-player";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HomeAssistant, HomeAssistantApi } from "../../../types";
|
||||
import { hasConfigChanged } from "../common/has-changed";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
@@ -21,6 +29,13 @@ import type {
|
||||
MediaPlayerPlaybackCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
const supportsMediaPlayerPlaybackCardFeatureFromState = (
|
||||
stateObj: HassEntity
|
||||
) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "media_player";
|
||||
};
|
||||
|
||||
export const supportsMediaPlayerPlaybackCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -29,8 +44,7 @@ export const supportsMediaPlayerPlaybackCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "media_player";
|
||||
return supportsMediaPlayerPlaybackCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-media-player-playback-card-feature")
|
||||
@@ -38,24 +52,26 @@ class HuiMediaPlayerPlaybackCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@property({ attribute: false }) public color?: string;
|
||||
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: MediaPlayerEntity;
|
||||
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state() private _config?: MediaPlayerPlaybackCardFeatureConfig;
|
||||
|
||||
@state() private _narrow?: boolean = false;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id] as
|
||||
MediaPlayerEntity | undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): MediaPlayerPlaybackCardFeatureConfig {
|
||||
return {
|
||||
type: "media-player-playback",
|
||||
@@ -82,25 +98,18 @@ class HuiMediaPlayerPlaybackCardFeature
|
||||
}
|
||||
}
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues<this>): boolean {
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
const entityId = this.context?.entity_id;
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
return (
|
||||
hasConfigChanged(this, changedProps) ||
|
||||
(changedProps.has("hass") &&
|
||||
(!oldHass ||
|
||||
!entityId ||
|
||||
oldHass.states[entityId] !== this.hass!.states[entityId]))
|
||||
hasConfigChanged(this, changedProps) || changedProps.has("_stateObj")
|
||||
);
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!supportsMediaPlayerPlaybackCardFeature(this.hass, this.context) ||
|
||||
!this._stateObj
|
||||
!this._stateObj ||
|
||||
!supportsMediaPlayerPlaybackCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -113,9 +122,7 @@ class HuiMediaPlayerPlaybackCardFeature
|
||||
(button) => html`
|
||||
<ha-control-button
|
||||
key=${button.action}
|
||||
.label=${this.hass?.localize(
|
||||
`ui.card.media_player.${button.action}`
|
||||
)}
|
||||
.label=${this._localize(`ui.card.media_player.${button.action}`)}
|
||||
.disabled=${button.disabled}
|
||||
@click=${this._action}
|
||||
>
|
||||
@@ -166,7 +173,7 @@ class HuiMediaPlayerPlaybackCardFeature
|
||||
if (!action) return;
|
||||
|
||||
if (action === "volume_mute") {
|
||||
this.hass!.callService("media_player", "volume_mute", {
|
||||
this._api.callService("media_player", "volume_mute", {
|
||||
entity_id: this._stateObj.entity_id,
|
||||
is_volume_muted: !this._stateObj.attributes.is_volume_muted,
|
||||
});
|
||||
@@ -174,7 +181,7 @@ class HuiMediaPlayerPlaybackCardFeature
|
||||
}
|
||||
|
||||
if (action === "shuffle") {
|
||||
this.hass!.callService("media_player", "shuffle_set", {
|
||||
this._api.callService("media_player", "shuffle_set", {
|
||||
entity_id: this._stateObj.entity_id,
|
||||
shuffle: !this._stateObj.attributes.shuffle,
|
||||
});
|
||||
@@ -183,14 +190,14 @@ class HuiMediaPlayerPlaybackCardFeature
|
||||
|
||||
if (action === "repeat") {
|
||||
const repeat = this._stateObj.attributes.repeat ?? "off";
|
||||
this.hass!.callService("media_player", "repeat_set", {
|
||||
this._api.callService("media_player", "repeat_set", {
|
||||
entity_id: this._stateObj.entity_id,
|
||||
repeat: repeat === "off" ? "one" : repeat === "one" ? "all" : "off",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.hass!.callService("media_player", action, {
|
||||
this._api.callService("media_player", action, {
|
||||
entity_id: this._stateObj.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { PropertyValues } from "lit";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
@@ -15,6 +16,17 @@ import type {
|
||||
MediaPlayerSoundModeCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
const supportsMediaPlayerSoundModeCardFeatureFromState = (
|
||||
stateObj: HassEntity
|
||||
) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "media_player" &&
|
||||
supportsFeature(stateObj, MediaPlayerEntityFeature.SELECT_SOUND_MODE) &&
|
||||
!!stateObj.attributes.sound_mode_list?.length
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsMediaPlayerSoundModeCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -23,12 +35,7 @@ export const supportsMediaPlayerSoundModeCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "media_player" &&
|
||||
supportsFeature(stateObj, MediaPlayerEntityFeature.SELECT_SOUND_MODE) &&
|
||||
!!stateObj.attributes.sound_mode_list?.length
|
||||
);
|
||||
return supportsMediaPlayerSoundModeCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-media-player-sound-mode-card-feature")
|
||||
@@ -48,7 +55,7 @@ class HuiMediaPlayerSoundModeCardFeature
|
||||
protected readonly _serviceAction = "select_sound_mode";
|
||||
|
||||
protected get _label(): string {
|
||||
return this.hass!.localize("ui.card.media_player.sound_mode");
|
||||
return this._localize("ui.card.media_player.sound_mode");
|
||||
}
|
||||
|
||||
protected readonly _hideLabel = false;
|
||||
@@ -76,25 +83,18 @@ class HuiMediaPlayerSoundModeCardFeature
|
||||
}
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
const entityId = this.context?.entity_id;
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
|
||||
return (
|
||||
changedProps.has("_currentValue") ||
|
||||
changedProps.has("context") ||
|
||||
hasConfigChanged(this, changedProps) ||
|
||||
(changedProps.has("hass") &&
|
||||
(!oldHass ||
|
||||
!entityId ||
|
||||
oldHass.states[entityId] !== this.hass?.states[entityId]))
|
||||
changedProps.has("_stateObj") ||
|
||||
hasConfigChanged(this, changedProps)
|
||||
);
|
||||
}
|
||||
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsMediaPlayerSoundModeCardFeature(this.hass, this.context)
|
||||
this._stateObj &&
|
||||
supportsMediaPlayerSoundModeCardFeatureFromState(this._stateObj)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { PropertyValues } from "lit";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
@@ -15,6 +16,16 @@ import type {
|
||||
MediaPlayerSourceCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
const supportsMediaPlayerSourceCardFeatureFromState = (
|
||||
stateObj: HassEntity
|
||||
) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "media_player" &&
|
||||
supportsFeature(stateObj, MediaPlayerEntityFeature.SELECT_SOURCE)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsMediaPlayerSourceCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -23,11 +34,7 @@ export const supportsMediaPlayerSourceCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "media_player" &&
|
||||
supportsFeature(stateObj, MediaPlayerEntityFeature.SELECT_SOURCE)
|
||||
);
|
||||
return supportsMediaPlayerSourceCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-media-player-source-card-feature")
|
||||
@@ -52,7 +59,7 @@ class HuiMediaPlayerSourceCardFeature
|
||||
protected readonly _serviceAction = "select_source";
|
||||
|
||||
protected get _label(): string {
|
||||
return this.hass!.localize("ui.card.media_player.source");
|
||||
return this._localize("ui.card.media_player.source");
|
||||
}
|
||||
|
||||
protected readonly _hideLabel = false;
|
||||
@@ -75,25 +82,18 @@ class HuiMediaPlayerSourceCardFeature
|
||||
}
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
const entityId = this.context?.entity_id;
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
|
||||
return (
|
||||
changedProps.has("_currentValue") ||
|
||||
changedProps.has("context") ||
|
||||
hasConfigChanged(this, changedProps) ||
|
||||
(changedProps.has("hass") &&
|
||||
(!oldHass ||
|
||||
!entityId ||
|
||||
oldHass.states[entityId] !== this.hass?.states[entityId]))
|
||||
changedProps.has("_stateObj") ||
|
||||
hasConfigChanged(this, changedProps)
|
||||
);
|
||||
}
|
||||
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsMediaPlayerSourceCardFeature(this.hass, this.context)
|
||||
this._stateObj &&
|
||||
supportsMediaPlayerSourceCardFeatureFromState(this._stateObj)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import { clamp } from "../../../common/number/clamp";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-number-buttons";
|
||||
import { apiContext, internationalizationContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import {
|
||||
MediaPlayerEntityFeature,
|
||||
type MediaPlayerEntity,
|
||||
} from "../../../data/media-player";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { FrontendLocaleData } from "../../../data/translation";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantInternationalization,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import {
|
||||
@@ -21,6 +35,16 @@ import type {
|
||||
MediaPlayerVolumeButtonsCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
const supportsMediaPlayerVolumeButtonsCardFeatureFromState = (
|
||||
stateObj: HassEntity
|
||||
) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "media_player" &&
|
||||
supportsFeature(stateObj, MediaPlayerEntityFeature.VOLUME_SET)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsMediaPlayerVolumeButtonsCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -29,11 +53,7 @@ export const supportsMediaPlayerVolumeButtonsCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "media_player" &&
|
||||
supportsFeature(stateObj, MediaPlayerEntityFeature.VOLUME_SET)
|
||||
);
|
||||
return supportsMediaPlayerVolumeButtonsCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-media-player-volume-buttons-card-feature")
|
||||
@@ -41,19 +61,28 @@ class HuiMediaPlayerVolumeButtonsCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: MediaPlayerVolumeButtonsCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: MediaPlayerEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id] as
|
||||
MediaPlayerEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
@transform<HomeAssistantInternationalization, FrontendLocaleData>({
|
||||
transformer: ({ locale }) => locale,
|
||||
})
|
||||
private _locale?: FrontendLocaleData;
|
||||
|
||||
@state() private _config?: MediaPlayerVolumeButtonsCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): MediaPlayerVolumeButtonsCardFeatureConfig {
|
||||
return {
|
||||
@@ -79,10 +108,9 @@ class HuiMediaPlayerVolumeButtonsCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsMediaPlayerVolumeButtonsCardFeature(this.hass, this.context)
|
||||
!supportsMediaPlayerVolumeButtonsCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -98,7 +126,7 @@ class HuiMediaPlayerVolumeButtonsCardFeature
|
||||
return html`
|
||||
<ha-control-number-buttons
|
||||
.disabled=${disabled}
|
||||
.locale=${this.hass.locale}
|
||||
.locale=${this._locale}
|
||||
min="0"
|
||||
max="100"
|
||||
.step=${this._config.step ?? 5}
|
||||
@@ -107,7 +135,7 @@ class HuiMediaPlayerVolumeButtonsCardFeature
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-control-number-buttons>
|
||||
${renderMuteButton(
|
||||
this.hass,
|
||||
this._localize,
|
||||
stateObj,
|
||||
this._config.show_mute_button,
|
||||
disabled,
|
||||
@@ -119,14 +147,14 @@ class HuiMediaPlayerVolumeButtonsCardFeature
|
||||
private _valueChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
|
||||
this.hass!.callService("media_player", "volume_set", {
|
||||
this._api.callService("media_player", "volume_set", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
volume_level: clamp(ev.detail.value, 0, 100) / 100,
|
||||
});
|
||||
}
|
||||
|
||||
private _toggleMute = (ev: Event) => {
|
||||
toggleMediaPlayerMute(ev, this.hass!, this._stateObj!, this);
|
||||
toggleMediaPlayerMute(ev, this._api!.callService, this._stateObj!, this);
|
||||
};
|
||||
|
||||
static get styles() {
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateActive } from "../../../common/entity/state_active";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-slider";
|
||||
import { apiContext, internationalizationContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import {
|
||||
MediaPlayerEntityFeature,
|
||||
type MediaPlayerEntity,
|
||||
} from "../../../data/media-player";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { FrontendLocaleData } from "../../../data/translation";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantInternationalization,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import {
|
||||
@@ -21,6 +35,16 @@ import type {
|
||||
MediaPlayerVolumeSliderCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
const supportsMediaPlayerVolumeSliderCardFeatureFromState = (
|
||||
stateObj: HassEntity
|
||||
) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "media_player" &&
|
||||
supportsFeature(stateObj, MediaPlayerEntityFeature.VOLUME_SET)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsMediaPlayerVolumeSliderCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -29,11 +53,7 @@ export const supportsMediaPlayerVolumeSliderCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "media_player" &&
|
||||
supportsFeature(stateObj, MediaPlayerEntityFeature.VOLUME_SET)
|
||||
);
|
||||
return supportsMediaPlayerVolumeSliderCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-media-player-volume-slider-card-feature")
|
||||
@@ -41,19 +61,28 @@ class HuiMediaPlayerVolumeSliderCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: MediaPlayerVolumeSliderCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: MediaPlayerEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
MediaPlayerEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
@transform<HomeAssistantInternationalization, FrontendLocaleData>({
|
||||
transformer: ({ locale }) => locale,
|
||||
})
|
||||
private _locale?: FrontendLocaleData;
|
||||
|
||||
@state() private _config?: MediaPlayerVolumeSliderCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): MediaPlayerVolumeSliderCardFeatureConfig {
|
||||
return {
|
||||
@@ -78,10 +107,9 @@ class HuiMediaPlayerVolumeSliderCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsMediaPlayerVolumeSliderCardFeature(this.hass, this.context)
|
||||
!supportsMediaPlayerVolumeSliderCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -103,10 +131,10 @@ class HuiMediaPlayerVolumeSliderCardFeature
|
||||
.disabled=${disabled}
|
||||
@value-changed=${this._valueChanged}
|
||||
unit="%"
|
||||
.locale=${this.hass.locale}
|
||||
.locale=${this._locale}
|
||||
></ha-control-slider>
|
||||
${renderMuteButton(
|
||||
this.hass,
|
||||
this._localize,
|
||||
stateObj,
|
||||
this._config.show_mute_button,
|
||||
disabled,
|
||||
@@ -119,14 +147,14 @@ class HuiMediaPlayerVolumeSliderCardFeature
|
||||
ev.stopPropagation();
|
||||
const value = ev.detail.value;
|
||||
|
||||
this.hass!.callService("media_player", "volume_set", {
|
||||
this._api.callService("media_player", "volume_set", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
volume_level: value / 100,
|
||||
});
|
||||
}
|
||||
|
||||
private _toggleMute = (ev: Event) => {
|
||||
toggleMediaPlayerMute(ev, this.hass!, this._stateObj!, this);
|
||||
toggleMediaPlayerMute(ev, this._api!.callService, this._stateObj!, this);
|
||||
};
|
||||
|
||||
static get styles() {
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-attribute-icon";
|
||||
import "../../../components/ha-control-select";
|
||||
import "../../../components/ha-control-select-menu";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { apiContext, formattersContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HomeAssistantApi, HomeAssistantFormatters } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
@@ -38,10 +45,24 @@ export abstract class HuiModeSelectCardFeatureBase<
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
protected _stateObj?: TEntity;
|
||||
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
protected _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
protected _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: formattersContext, subscribe: true })
|
||||
protected _formatters!: HomeAssistantFormatters;
|
||||
|
||||
@state() protected _config?: TConfig;
|
||||
|
||||
@state() protected _currentValue?: string;
|
||||
@@ -63,7 +84,7 @@ export abstract class HuiModeSelectCardFeatureBase<
|
||||
protected abstract _isSupported(): boolean;
|
||||
|
||||
protected get _label(): string {
|
||||
return this.hass!.formatEntityAttributeName(
|
||||
return this._formatters.formatEntityAttributeName(
|
||||
this._stateObj!,
|
||||
this._attribute
|
||||
);
|
||||
@@ -90,14 +111,6 @@ export abstract class HuiModeSelectCardFeatureBase<
|
||||
return true;
|
||||
}
|
||||
|
||||
protected get _stateObj(): TEntity | undefined {
|
||||
if (!this.hass || !this.context?.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.hass.states[this.context.entity_id] as TEntity | undefined;
|
||||
}
|
||||
|
||||
public setConfig(config: TConfig): void {
|
||||
if (!config) {
|
||||
throw new Error("Invalid configuration");
|
||||
@@ -106,28 +119,17 @@ export abstract class HuiModeSelectCardFeatureBase<
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues<this>): void {
|
||||
protected willUpdate(changedProps: PropertyValues): void {
|
||||
super.willUpdate(changedProps);
|
||||
|
||||
if (
|
||||
(changedProps.has("hass") || changedProps.has("context")) &&
|
||||
this._stateObj
|
||||
) {
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
const oldStateObj = this.context?.entity_id
|
||||
? (oldHass?.states[this.context.entity_id] as TEntity | undefined)
|
||||
: undefined;
|
||||
|
||||
if (oldStateObj !== this._stateObj) {
|
||||
this._currentValue = this._getValue(this._stateObj);
|
||||
}
|
||||
if (changedProps.has("_stateObj") && this._stateObj) {
|
||||
this._currentValue = this._getValue(this._stateObj);
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | null {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!this._isSupported()
|
||||
@@ -191,7 +193,7 @@ export abstract class HuiModeSelectCardFeatureBase<
|
||||
}
|
||||
|
||||
protected _getOptions(): HuiModeSelectOption[] {
|
||||
if (!this._stateObj || !this.hass) {
|
||||
if (!this._stateObj) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -200,7 +202,7 @@ export abstract class HuiModeSelectCardFeatureBase<
|
||||
this._configuredModes
|
||||
).map((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
label: this._formatters.formatEntityAttributeValue(
|
||||
this._stateObj!,
|
||||
this._attribute,
|
||||
mode
|
||||
@@ -225,7 +227,7 @@ export abstract class HuiModeSelectCardFeatureBase<
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
private async _valueChanged(ev: AttributeModeChangeEvent) {
|
||||
if (!this.hass || !this._stateObj) {
|
||||
if (!this._stateObj) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -243,7 +245,7 @@ export abstract class HuiModeSelectCardFeatureBase<
|
||||
this._currentValue = value;
|
||||
|
||||
try {
|
||||
await this.hass.callService(
|
||||
await this._api.callService(
|
||||
this._getServiceDomain(this._stateObj),
|
||||
this._serviceAction,
|
||||
{
|
||||
|
||||
@@ -1,22 +1,40 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { ResizeController } from "@lit-labs/observers/resize-controller";
|
||||
import type { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type {
|
||||
Connection,
|
||||
HassEntity,
|
||||
UnsubscribeFunc,
|
||||
} from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import type { LocalizeKeys } from "../../../common/translations/localize";
|
||||
import type {
|
||||
LocalizeFunc,
|
||||
LocalizeKeys,
|
||||
} from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-select";
|
||||
import { apiContext, connectionContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import type { ExtEntityRegistryEntry } from "../../../data/entity/entity_registry";
|
||||
import {
|
||||
getExtendedEntityRegistryEntry,
|
||||
subscribeEntityRegistry,
|
||||
} from "../../../data/entity/entity_registry";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantConnection,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -50,6 +68,15 @@ export interface NumericFavoriteCardFeatureDefinition<
|
||||
featureLabelKey: LocalizeKeys;
|
||||
}
|
||||
|
||||
const supportsNumericFavoriteCardFeatureFromState = <
|
||||
TEntity extends NumericFavoriteEntity,
|
||||
>(
|
||||
stateObj: TEntity,
|
||||
definition: NumericFavoriteCardFeatureDefinition<TEntity>
|
||||
) =>
|
||||
computeDomain(stateObj.entity_id) === definition.domain &&
|
||||
definition.supportsPosition(stateObj);
|
||||
|
||||
export const supportsNumericFavoriteCardFeature = <
|
||||
TEntity extends NumericFavoriteEntity,
|
||||
>(
|
||||
@@ -65,10 +92,7 @@ export const supportsNumericFavoriteCardFeature = <
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
computeDomain(stateObj.entity_id) === definition.domain &&
|
||||
definition.supportsPosition(stateObj)
|
||||
);
|
||||
return supportsNumericFavoriteCardFeatureFromState(stateObj, definition);
|
||||
};
|
||||
|
||||
export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
@@ -78,12 +102,29 @@ export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@property({ attribute: false }) public color?: string;
|
||||
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
protected _stateObj?: TEntity;
|
||||
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
protected _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
protected _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: connectionContext, subscribe: true })
|
||||
@transform<HomeAssistantConnection, Connection>({
|
||||
transformer: ({ connection }) => connection,
|
||||
})
|
||||
protected _connection?: Connection;
|
||||
|
||||
@state() protected _config?: TConfig;
|
||||
|
||||
@state() protected _entry?: ExtEntityRegistryEntry | null;
|
||||
@@ -108,14 +149,6 @@ export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
|
||||
protected abstract get _definition(): NumericFavoriteCardFeatureDefinition<TEntity>;
|
||||
|
||||
protected get _stateObj(): TEntity | undefined {
|
||||
if (!this.hass || !this.context?.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.hass.states[this.context.entity_id] as TEntity | undefined;
|
||||
}
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._refreshEntitySubscription();
|
||||
@@ -134,38 +167,14 @@ export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
this._config = config as TConfig;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues<this>): void {
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
|
||||
if (
|
||||
(changedProp.has("hass") || changedProp.has("context")) &&
|
||||
this._stateObj
|
||||
) {
|
||||
const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
|
||||
const oldStateObj = this.context?.entity_id
|
||||
? (oldHass?.states[this.context.entity_id] as TEntity | undefined)
|
||||
: undefined;
|
||||
|
||||
if (oldStateObj !== this._stateObj) {
|
||||
this._currentPosition = this._definition.getCurrentValue(
|
||||
this._stateObj
|
||||
);
|
||||
}
|
||||
if (changedProp.has("_stateObj") && this._stateObj) {
|
||||
this._currentPosition = this._definition.getCurrentValue(this._stateObj);
|
||||
}
|
||||
|
||||
if (
|
||||
changedProp.has("context") &&
|
||||
(changedProp.get("context") as LovelaceCardFeatureContext | undefined)
|
||||
?.entity_id !== this.context?.entity_id
|
||||
) {
|
||||
this._refreshEntitySubscription();
|
||||
}
|
||||
|
||||
if (
|
||||
changedProp.has("hass") &&
|
||||
(changedProp.get("hass") as HomeAssistant | undefined)?.connection !==
|
||||
this.hass?.connection
|
||||
) {
|
||||
if (changedProp.has("context") || changedProp.has("_connection")) {
|
||||
this._refreshEntitySubscription();
|
||||
}
|
||||
}
|
||||
@@ -182,12 +191,8 @@ export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
}
|
||||
|
||||
private async _loadEntityEntry(entityId: string): Promise<void> {
|
||||
if (!this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const entry = await getExtendedEntityRegistryEntry(this.hass, entityId);
|
||||
const entry = await getExtendedEntityRegistryEntry(this._api, entityId);
|
||||
|
||||
if (this.context?.entity_id === entityId) {
|
||||
this._entry = entry;
|
||||
@@ -206,7 +211,7 @@ export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
|
||||
try {
|
||||
this._unsubEntityRegistry = subscribeEntityRegistry(
|
||||
this.hass!.connection,
|
||||
this._connection!,
|
||||
async (entries) => {
|
||||
if (this.context?.entity_id !== entityId) {
|
||||
return;
|
||||
@@ -227,9 +232,9 @@ export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
|
||||
private async _ensureEntitySubscription(): Promise<void> {
|
||||
const entityId = this.context?.entity_id;
|
||||
const connection = this.hass?.connection;
|
||||
const connection = this._connection;
|
||||
|
||||
if (!this.hass || !entityId || !connection) {
|
||||
if (!entityId || !connection) {
|
||||
this._unsubscribeEntityRegistry();
|
||||
this._subscribedEntityId = undefined;
|
||||
this._subscribedConnection = undefined;
|
||||
@@ -256,7 +261,7 @@ export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
) {
|
||||
const value = ev.detail.value;
|
||||
|
||||
if (value == null || !this.hass || !this._stateObj) {
|
||||
if (value == null || !this._stateObj) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -275,7 +280,7 @@ export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
this._currentPosition = position;
|
||||
|
||||
try {
|
||||
await this.hass.callService(
|
||||
await this._api.callService(
|
||||
this._definition.domain,
|
||||
this._definition.setPositionService,
|
||||
{
|
||||
@@ -291,12 +296,10 @@ export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
protected render(): TemplateResult | null {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsNumericFavoriteCardFeature(
|
||||
this.hass,
|
||||
this.context,
|
||||
!supportsNumericFavoriteCardFeatureFromState(
|
||||
this._stateObj,
|
||||
this._definition
|
||||
)
|
||||
) {
|
||||
@@ -308,9 +311,7 @@ export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
this._definition.defaultFavoritePositions
|
||||
);
|
||||
|
||||
const hass = this.hass;
|
||||
|
||||
if (positions.length === 0 || !hass) {
|
||||
if (positions.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -321,7 +322,7 @@ export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
const options = visiblePositions.map((position) => ({
|
||||
value: String(position),
|
||||
label: `${position}%`,
|
||||
ariaLabel: hass.localize(this._definition.setPositionLabelKey, {
|
||||
ariaLabel: this._localize(this._definition.setPositionLabelKey, {
|
||||
value: `${position}%`,
|
||||
}),
|
||||
}));
|
||||
@@ -339,7 +340,7 @@ export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
.options=${options}
|
||||
.value=${currentValue}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${hass.localize(this._definition.featureLabelKey)}
|
||||
.label=${this._localize(this._definition.featureLabelKey)}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { consumeEntityState } from "../../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-control-number-buttons";
|
||||
import "../../../components/ha-control-slider";
|
||||
import "../../../components/ha-icon";
|
||||
import { apiContext, internationalizationContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { FrontendLocaleData } from "../../../data/translation";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantInternationalization,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -17,6 +26,11 @@ import type {
|
||||
NumericInputCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
const supportsNumericInputCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "input_number" || domain === "number";
|
||||
};
|
||||
|
||||
export const supportsNumericInputCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -25,8 +39,7 @@ export const supportsNumericInputCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "input_number" || domain === "number";
|
||||
return supportsNumericInputCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-numeric-input-card-feature")
|
||||
@@ -34,10 +47,23 @@ class HuiNumericInputCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: HassEntity;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
@transform<HomeAssistantInternationalization, FrontendLocaleData>({
|
||||
transformer: ({ locale }) => locale,
|
||||
})
|
||||
private _locale?: FrontendLocaleData;
|
||||
|
||||
@state() private _config?: NumericInputCardFeatureConfig;
|
||||
|
||||
@state() _currentState?: string;
|
||||
@@ -49,13 +75,6 @@ class HuiNumericInputCardFeature
|
||||
};
|
||||
}
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as HassEntity | undefined;
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import("../editor/config-elements/hui-numeric-input-card-feature-editor");
|
||||
return document.createElement("hui-numeric-input-card-feature-editor");
|
||||
@@ -68,17 +87,10 @@ class HuiNumericInputCardFeature
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues<this>): void {
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (
|
||||
(changedProp.has("hass") || changedProp.has("context")) &&
|
||||
this._stateObj
|
||||
) {
|
||||
const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
|
||||
const oldStateObj = oldHass?.states[this.context!.entity_id!];
|
||||
if (oldStateObj !== this._stateObj) {
|
||||
this._currentState = this._stateObj.state;
|
||||
}
|
||||
if (changedProp.has("_stateObj") && this._stateObj) {
|
||||
this._currentState = this._stateObj.state;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +99,7 @@ class HuiNumericInputCardFeature
|
||||
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
|
||||
await this.hass!.callService(domain, "set_value", {
|
||||
await this._api.callService(domain, "set_value", {
|
||||
entity_id: stateObj.entity_id,
|
||||
value: ev.detail.value,
|
||||
});
|
||||
@@ -96,10 +108,9 @@ class HuiNumericInputCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsNumericInputCardFeature(this.hass, this.context)
|
||||
!supportsNumericInputCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -119,7 +130,7 @@ class HuiNumericInputCardFeature
|
||||
@value-changed=${this._setValue}
|
||||
.disabled=${stateObj.state === UNAVAILABLE}
|
||||
.unit=${stateObj.attributes.unit_of_measurement}
|
||||
.locale=${this.hass.locale}
|
||||
.locale=${this._locale}
|
||||
></ha-control-number-buttons>
|
||||
`;
|
||||
}
|
||||
@@ -132,7 +143,7 @@ class HuiNumericInputCardFeature
|
||||
@value-changed=${this._setValue}
|
||||
.disabled=${stateObj.state === UNAVAILABLE}
|
||||
.unit=${stateObj.attributes.unit_of_measurement}
|
||||
.locale=${this.hass.locale}
|
||||
.locale=${this._locale}
|
||||
></ha-control-slider>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import type { InputSelectEntity } from "../../../data/input_select";
|
||||
@@ -16,6 +17,11 @@ import type {
|
||||
|
||||
type SelectOptionEntity = SelectEntity | InputSelectEntity;
|
||||
|
||||
const supportsSelectOptionsCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "select" || domain === "input_select";
|
||||
};
|
||||
|
||||
export const supportsSelectOptionsCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -24,8 +30,7 @@ export const supportsSelectOptionsCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "select" || domain === "input_select";
|
||||
return supportsSelectOptionsCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-select-options-card-feature")
|
||||
@@ -49,7 +54,7 @@ class HuiSelectOptionsCardFeature
|
||||
protected readonly _serviceAction = "select_option";
|
||||
|
||||
protected get _label(): string {
|
||||
return this.hass!.localize("ui.card.select.option");
|
||||
return this._localize("ui.card.select.option");
|
||||
}
|
||||
|
||||
protected readonly _allowIconsStyle = false;
|
||||
@@ -72,7 +77,7 @@ class HuiSelectOptionsCardFeature
|
||||
}
|
||||
|
||||
protected _getOptions(): HuiModeSelectOption[] {
|
||||
if (!this._stateObj || !this.hass) {
|
||||
if (!this._stateObj) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -81,7 +86,7 @@ class HuiSelectOptionsCardFeature
|
||||
this._config?.options
|
||||
).map((option) => ({
|
||||
value: option,
|
||||
label: this.hass!.formatEntityState(this._stateObj!, option),
|
||||
label: this._formatters.formatEntityState(this._stateObj!, option),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -98,9 +103,8 @@ class HuiSelectOptionsCardFeature
|
||||
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsSelectOptionsCardFeature(this.hass, this.context)
|
||||
this._stateObj &&
|
||||
supportsSelectOptionsCardFeatureFromState(this._stateObj)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { consumeEntityState } from "../../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-control-slider";
|
||||
import {
|
||||
apiContext,
|
||||
formattersContext,
|
||||
internationalizationContext,
|
||||
} from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import type { HumidifierEntity } from "../../../data/humidifier";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { FrontendLocaleData } from "../../../data/translation";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantFormatters,
|
||||
HomeAssistantInternationalization,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -14,6 +29,11 @@ import type {
|
||||
TargetHumidityCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
const supportsTargetHumidityCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "humidifier";
|
||||
};
|
||||
|
||||
export const supportsTargetHumidityCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -22,8 +42,7 @@ export const supportsTargetHumidityCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "humidifier";
|
||||
return supportsTargetHumidityCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-target-humidity-card-feature")
|
||||
@@ -31,22 +50,31 @@ class HuiTargetHumidityCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: HumidifierEntity;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: formattersContext, subscribe: true })
|
||||
private _formatters!: HomeAssistantFormatters;
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
@transform<HomeAssistantInternationalization, FrontendLocaleData>({
|
||||
transformer: ({ locale }) => locale,
|
||||
})
|
||||
private _locale?: FrontendLocaleData;
|
||||
|
||||
@state() private _config?: TargetHumidityCardFeatureConfig;
|
||||
|
||||
@state() private _targetHumidity?: number;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
HumidifierEntity | undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): TargetHumidityCardFeatureConfig {
|
||||
return {
|
||||
type: "target-humidity",
|
||||
@@ -60,17 +88,10 @@ class HuiTargetHumidityCardFeature
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues<this>): void {
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (
|
||||
(changedProp.has("hass") || changedProp.has("context")) &&
|
||||
this._stateObj
|
||||
) {
|
||||
const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
|
||||
const oldStateObj = oldHass?.states[this.context!.entity_id!];
|
||||
if (oldStateObj !== this._stateObj) {
|
||||
this._targetHumidity = this._stateObj!.attributes.humidity;
|
||||
}
|
||||
if (changedProp.has("_stateObj") && this._stateObj) {
|
||||
this._targetHumidity = this._stateObj.attributes.humidity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +113,7 @@ class HuiTargetHumidityCardFeature
|
||||
}
|
||||
|
||||
private _callService() {
|
||||
this.hass!.callService("humidifier", "set_humidity", {
|
||||
this._api.callService("humidifier", "set_humidity", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
humidity: this._targetHumidity,
|
||||
});
|
||||
@@ -101,10 +122,9 @@ class HuiTargetHumidityCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsTargetHumidityCardFeature(this.hass, this.context)
|
||||
!supportsTargetHumidityCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -117,12 +137,12 @@ class HuiTargetHumidityCardFeature
|
||||
.step=${this._step}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
.label=${this._formatters.formatEntityAttributeName(
|
||||
this._stateObj,
|
||||
"humidity"
|
||||
)}
|
||||
unit="%"
|
||||
.locale=${this.hass.locale}
|
||||
.locale=${this._locale}
|
||||
></ha-control-slider>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassConfig, HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { UNIT_F } from "../../../common/const";
|
||||
import { consumeEntityState } from "../../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||
@@ -13,10 +17,23 @@ import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-control-number-buttons";
|
||||
import type { ClimateEntity } from "../../../data/climate";
|
||||
import { ClimateEntityFeature } from "../../../data/climate";
|
||||
import {
|
||||
apiContext,
|
||||
configContext,
|
||||
formattersContext,
|
||||
internationalizationContext,
|
||||
} from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import type { FrontendLocaleData } from "../../../data/translation";
|
||||
import type { WaterHeaterEntity } from "../../../data/water_heater";
|
||||
import { WaterHeaterEntityFeature } from "../../../data/water_heater";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantConfig,
|
||||
HomeAssistantFormatters,
|
||||
HomeAssistantInternationalization,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -26,14 +43,9 @@ import type {
|
||||
|
||||
type Target = "value" | "low" | "high";
|
||||
|
||||
export const supportsTargetTemperatureCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
const supportsTargetTemperatureCardFeatureFromState = (
|
||||
stateObj: HassEntity
|
||||
) => {
|
||||
const stateObj = context.entity_id
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
(domain === "climate" &&
|
||||
@@ -47,27 +59,54 @@ export const supportsTargetTemperatureCardFeature = (
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsTargetTemperatureCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
) => {
|
||||
const stateObj = context.entity_id
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
return supportsTargetTemperatureCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-target-temperature-card-feature")
|
||||
class HuiTargetTemperatureCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: WaterHeaterEntity | ClimateEntity;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: formattersContext, subscribe: true })
|
||||
private _formatters!: HomeAssistantFormatters;
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
@transform<HomeAssistantInternationalization, FrontendLocaleData>({
|
||||
transformer: ({ locale }) => locale,
|
||||
})
|
||||
private _locale?: FrontendLocaleData;
|
||||
|
||||
@state()
|
||||
@consume({ context: configContext, subscribe: true })
|
||||
@transform<HomeAssistantConfig, HassConfig>({
|
||||
transformer: ({ config }) => config,
|
||||
})
|
||||
private _hassConfig?: HassConfig;
|
||||
|
||||
@state() private _config?: TargetTemperatureCardFeatureConfig;
|
||||
|
||||
@state() private _targetTemperature: Partial<Record<Target, number>> = {};
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
WaterHeaterEntity | ClimateEntity | undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): TargetTemperatureCardFeatureConfig {
|
||||
return {
|
||||
type: "target-temperature",
|
||||
@@ -81,34 +120,27 @@ class HuiTargetTemperatureCardFeature
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues<this>): void {
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (
|
||||
(changedProp.has("hass") || changedProp.has("context")) &&
|
||||
this._stateObj
|
||||
) {
|
||||
const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
|
||||
const oldStateObj = oldHass?.states[this.context!.entity_id!];
|
||||
if (oldStateObj !== this._stateObj) {
|
||||
this._targetTemperature = {
|
||||
value: this._stateObj!.attributes.temperature,
|
||||
low:
|
||||
"target_temp_low" in this._stateObj!.attributes
|
||||
? this._stateObj!.attributes.target_temp_low
|
||||
: undefined,
|
||||
high:
|
||||
"target_temp_high" in this._stateObj!.attributes
|
||||
? this._stateObj!.attributes.target_temp_high
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
if (changedProp.has("_stateObj") && this._stateObj) {
|
||||
this._targetTemperature = {
|
||||
value: this._stateObj.attributes.temperature,
|
||||
low:
|
||||
"target_temp_low" in this._stateObj.attributes
|
||||
? this._stateObj.attributes.target_temp_low
|
||||
: undefined,
|
||||
high:
|
||||
"target_temp_high" in this._stateObj.attributes
|
||||
? this._stateObj.attributes.target_temp_high
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private get _step() {
|
||||
return (
|
||||
this._stateObj!.attributes.target_temp_step ||
|
||||
(this.hass!.config.unit_system.temperature === UNIT_F ? 1 : 0.5)
|
||||
(this._hassConfig?.unit_system.temperature === UNIT_F ? 1 : 0.5)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -143,14 +175,14 @@ class HuiTargetTemperatureCardFeature
|
||||
private _callService(type: string) {
|
||||
const domain = computeStateDomain(this._stateObj!);
|
||||
if (type === "high" || type === "low") {
|
||||
this.hass!.callService(domain, "set_temperature", {
|
||||
this._api.callService(domain, "set_temperature", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
target_temp_low: this._targetTemperature.low,
|
||||
target_temp_high: this._targetTemperature.high,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.hass!.callService(domain, "set_temperature", {
|
||||
this._api.callService(domain, "set_temperature", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
temperature: this._targetTemperature.value,
|
||||
});
|
||||
@@ -186,10 +218,9 @@ class HuiTargetTemperatureCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsTargetTemperatureCardFeature(this.hass, this.context)
|
||||
!supportsTargetTemperatureCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -213,12 +244,12 @@ class HuiTargetTemperatureCardFeature
|
||||
.formatOptions=${options}
|
||||
.target=${"value"}
|
||||
.value=${this._stateObj.attributes.temperature}
|
||||
.unit=${this.hass.config.unit_system.temperature}
|
||||
.unit=${this._hassConfig?.unit_system.temperature}
|
||||
.min=${this._min}
|
||||
.max=${this._max}
|
||||
.step=${this._step}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
.label=${this._formatters.formatEntityAttributeName(
|
||||
this._stateObj,
|
||||
"temperature"
|
||||
)}
|
||||
@@ -226,7 +257,7 @@ class HuiTargetTemperatureCardFeature
|
||||
"--control-number-buttons-focus-color": stateColor,
|
||||
})}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
.locale=${this.hass.locale}
|
||||
.locale=${this._locale}
|
||||
>
|
||||
</ha-control-number-buttons>
|
||||
</ha-control-button-group>
|
||||
@@ -245,7 +276,7 @@ class HuiTargetTemperatureCardFeature
|
||||
.formatOptions=${options}
|
||||
.target=${"low"}
|
||||
.value=${this._targetTemperature.low}
|
||||
.unit=${this.hass.config.unit_system.temperature}
|
||||
.unit=${this._hassConfig?.unit_system.temperature}
|
||||
.min=${this._min}
|
||||
.max=${Math.min(
|
||||
this._max,
|
||||
@@ -253,7 +284,7 @@ class HuiTargetTemperatureCardFeature
|
||||
)}
|
||||
.step=${this._step}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
.label=${this._formatters.formatEntityAttributeName(
|
||||
this._stateObj,
|
||||
"target_temp_low"
|
||||
)}
|
||||
@@ -261,14 +292,14 @@ class HuiTargetTemperatureCardFeature
|
||||
"--control-number-buttons-focus-color": stateColor,
|
||||
})}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
.locale=${this.hass.locale}
|
||||
.locale=${this._locale}
|
||||
>
|
||||
</ha-control-number-buttons>
|
||||
<ha-control-number-buttons
|
||||
.formatOptions=${options}
|
||||
.target=${"high"}
|
||||
.value=${this._targetTemperature.high}
|
||||
.unit=${this.hass.config.unit_system.temperature}
|
||||
.unit=${this._hassConfig?.unit_system.temperature}
|
||||
.min=${Math.max(
|
||||
this._min,
|
||||
this._targetTemperature.low ?? this._min
|
||||
@@ -276,7 +307,7 @@ class HuiTargetTemperatureCardFeature
|
||||
.max=${this._max}
|
||||
.step=${this._step}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
.label=${this._formatters.formatEntityAttributeName(
|
||||
this._stateObj,
|
||||
"target_temp_high"
|
||||
)}
|
||||
@@ -284,7 +315,7 @@ class HuiTargetTemperatureCardFeature
|
||||
"--control-number-buttons-focus-color": stateColor,
|
||||
})}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
.locale=${this.hass.locale}
|
||||
.locale=${this._locale}
|
||||
>
|
||||
</ha-control-number-buttons>
|
||||
</ha-control-button-group>
|
||||
@@ -295,15 +326,15 @@ class HuiTargetTemperatureCardFeature
|
||||
<ha-control-button-group>
|
||||
<ha-control-number-buttons
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
.unit=${this.hass.config.unit_system.temperature}
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
.unit=${this._hassConfig?.unit_system.temperature}
|
||||
.label=${this._formatters.formatEntityAttributeName(
|
||||
this._stateObj,
|
||||
"temperature"
|
||||
)}
|
||||
style=${styleMap({
|
||||
"--control-number-buttons-focus-color": stateColor,
|
||||
})}
|
||||
.locale=${this.hass.locale}
|
||||
.locale=${this._locale}
|
||||
>
|
||||
</ha-control-number-buttons>
|
||||
</ha-control-button-group>
|
||||
|
||||
@@ -8,19 +8,26 @@ import {
|
||||
mdiVolumeHigh,
|
||||
mdiVolumeOff,
|
||||
} from "@mdi/js";
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-control-switch";
|
||||
import { apiContext } from "../../../data/context";
|
||||
import { UNAVAILABLE, UNKNOWN } from "../../../data/entity/entity";
|
||||
import { forwardHaptic } from "../../../data/haptics";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HomeAssistant, HomeAssistantApi } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -28,14 +35,7 @@ import type {
|
||||
ToggleCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
export const supportsToggleCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
) => {
|
||||
const stateObj = context.entity_id
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const supportsToggleCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return [
|
||||
"switch",
|
||||
@@ -47,6 +47,17 @@ export const supportsToggleCardFeature = (
|
||||
].includes(domain);
|
||||
};
|
||||
|
||||
export const supportsToggleCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
) => {
|
||||
const stateObj = context.entity_id
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
return supportsToggleCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
const DOMAIN_ICONS: Record<string, { on: string; off: string }> = {
|
||||
siren: {
|
||||
on: mdiVolumeHigh,
|
||||
@@ -64,18 +75,21 @@ const DOMAIN_ICONS: Record<string, { on: string; off: string }> = {
|
||||
|
||||
@customElement("hui-toggle-card-feature")
|
||||
class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: ToggleCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: HassEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as HassEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state() private _config?: ToggleCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): ToggleCardFeatureConfig {
|
||||
return {
|
||||
@@ -109,7 +123,7 @@ class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
}
|
||||
|
||||
private async _callService(turnOn): Promise<void> {
|
||||
if (!this.hass || !this._stateObj) {
|
||||
if (!this._stateObj) {
|
||||
return;
|
||||
}
|
||||
forwardHaptic(this, "light");
|
||||
@@ -117,7 +131,7 @@ class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
const serviceDomain = stateDomain;
|
||||
const service = turnOn ? "turn_on" : "turn_off";
|
||||
|
||||
await this.hass.callService(serviceDomain, service, {
|
||||
await this._api.callService(serviceDomain, service, {
|
||||
entity_id: this._stateObj.entity_id,
|
||||
});
|
||||
}
|
||||
@@ -125,10 +139,9 @@ class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsToggleCardFeature(this.hass, this.context)
|
||||
!supportsToggleCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -150,7 +163,7 @@ class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
return html`
|
||||
<ha-control-button-group>
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.common.turn_off")}
|
||||
.label=${this._localize("ui.card.common.turn_off")}
|
||||
@click=${this._turnOff}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
class=${classMap({
|
||||
@@ -163,7 +176,7 @@ class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
<ha-svg-icon .path=${offIcon}></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.common.turn_on")}
|
||||
.label=${this._localize("ui.card.common.turn_on")}
|
||||
@click=${this._turnOn}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
class=${classMap({
|
||||
@@ -185,7 +198,7 @@ class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
.pathOff=${offIcon}
|
||||
.checked=${isOn}
|
||||
@change=${this._valueChanged}
|
||||
.label=${this.hass.localize("ui.card.common.toggle")}
|
||||
.label=${this._localize("ui.card.common.toggle")}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-switch>
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { mdiCancel, mdiCellphoneArrowDown } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateActive } from "../../../common/entity/state_active";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import { apiContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import type { UpdateEntity } from "../../../data/update";
|
||||
import { UpdateEntityFeature, updateIsInstalling } from "../../../data/update";
|
||||
import { showUpdateBackupDialogParams } from "../../../dialogs/update_backup/show-update-backup-dialog";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HomeAssistant, HomeAssistantApi } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -20,6 +28,14 @@ import type {
|
||||
|
||||
export const DEFAULT_UPDATE_BACKUP_OPTION = "no";
|
||||
|
||||
const supportsUpdateActionsCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "update" &&
|
||||
supportsFeature(stateObj, UpdateEntityFeature.INSTALL)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsUpdateActionsCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -28,11 +44,7 @@ export const supportsUpdateActionsCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "update" &&
|
||||
supportsFeature(stateObj, UpdateEntityFeature.INSTALL)
|
||||
);
|
||||
return supportsUpdateActionsCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-update-actions-card-feature")
|
||||
@@ -40,19 +52,21 @@ class HuiUpdateActionsCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: UpdateActionsCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: UpdateEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
UpdateEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state() private _config?: UpdateActionsCardFeatureConfig;
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import("../editor/config-elements/hui-update-actions-card-feature-editor");
|
||||
@@ -115,14 +129,14 @@ class HuiUpdateActionsCardFeature
|
||||
backup = response;
|
||||
}
|
||||
|
||||
this.hass!.callService("update", "install", {
|
||||
this._api.callService("update", "install", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
backup: backup,
|
||||
});
|
||||
}
|
||||
|
||||
private async _skip(): Promise<void> {
|
||||
this.hass!.callService("update", "skip", {
|
||||
this._api.callService("update", "skip", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
@@ -130,10 +144,9 @@ class HuiUpdateActionsCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsUpdateActionsCardFeature(this.hass, this.context)
|
||||
!supportsUpdateActionsCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -141,16 +154,14 @@ class HuiUpdateActionsCardFeature
|
||||
return html`
|
||||
<ha-control-button-group>
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.update.skip"
|
||||
)}
|
||||
.label=${this._localize("ui.dialogs.more_info_control.update.skip")}
|
||||
@click=${this._skip}
|
||||
.disabled=${this._skipDisabled}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiCancel}></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize(
|
||||
.label=${this._localize(
|
||||
"ui.dialogs.more_info_control.update.install"
|
||||
)}
|
||||
@click=${this._install}
|
||||
|
||||
@@ -7,14 +7,21 @@ import {
|
||||
mdiStop,
|
||||
mdiTargetVariant,
|
||||
} from "@mdi/js";
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { apiContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import type { VacuumEntity } from "../../../data/vacuum";
|
||||
import {
|
||||
@@ -24,7 +31,7 @@ import {
|
||||
canStop,
|
||||
isCleaning,
|
||||
} from "../../../data/vacuum";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HomeAssistant, HomeAssistantApi } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -125,6 +132,14 @@ export const VACUUM_COMMANDS_BUTTONS: Record<
|
||||
}),
|
||||
};
|
||||
|
||||
const supportsVacuumCommandsCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "vacuum" &&
|
||||
VACUUM_COMMANDS.some((c) => supportsVacuumCommand(stateObj, c))
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsVacuumCommandsCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -133,11 +148,7 @@ export const supportsVacuumCommandsCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "vacuum" &&
|
||||
VACUUM_COMMANDS.some((c) => supportsVacuumCommand(stateObj, c))
|
||||
);
|
||||
return supportsVacuumCommandsCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-vacuum-commands-card-feature")
|
||||
@@ -145,19 +156,21 @@ class HuiVacuumCommandCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: VacuumCommandsCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: VacuumEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
VacuumEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state() private _config?: VacuumCommandsCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): VacuumCommandsCardFeatureConfig {
|
||||
return {
|
||||
@@ -180,7 +193,7 @@ class HuiVacuumCommandCardFeature
|
||||
private _onCommandTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
const entry = (ev.target! as any).entry as VacuumButton;
|
||||
this.hass!.callService("vacuum", entry.serviceName, {
|
||||
this._api.callService("vacuum", entry.serviceName, {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
@@ -188,10 +201,9 @@ class HuiVacuumCommandCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsVacuumCommandsCardFeature(this.hass, this.context)
|
||||
!supportsVacuumCommandsCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -209,7 +221,7 @@ class HuiVacuumCommandCardFeature
|
||||
return html`
|
||||
<ha-control-button
|
||||
.entry=${button}
|
||||
.label=${this.hass!.localize(
|
||||
.label=${this._localize(
|
||||
// @ts-ignore
|
||||
`ui.dialogs.more_info_control.vacuum.${button.translationKey}`
|
||||
)}
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { mdiStop, mdiValveClosed, mdiValveOpen } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-control-switch";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { apiContext } from "../../../data/context";
|
||||
import { UNAVAILABLE, UNKNOWN } from "../../../data/entity/entity";
|
||||
import {
|
||||
canClose,
|
||||
@@ -18,7 +26,7 @@ import {
|
||||
ValveEntityFeature,
|
||||
type ValveEntity,
|
||||
} from "../../../data/valve";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HomeAssistant, HomeAssistantApi } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -26,6 +34,15 @@ import type {
|
||||
ValveOpenCloseCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
const supportsValveOpenCloseCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "valve" &&
|
||||
(supportsFeature(stateObj, ValveEntityFeature.OPEN) ||
|
||||
supportsFeature(stateObj, ValveEntityFeature.CLOSE))
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsValveOpenCloseCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -34,12 +51,7 @@ export const supportsValveOpenCloseCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "valve" &&
|
||||
(supportsFeature(stateObj, ValveEntityFeature.OPEN) ||
|
||||
supportsFeature(stateObj, ValveEntityFeature.CLOSE))
|
||||
);
|
||||
return supportsValveOpenCloseCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-valve-open-close-card-feature")
|
||||
@@ -47,18 +59,21 @@ class HuiValveOpenCloseCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: ValveOpenCloseCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: ValveEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as ValveEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state() private _config?: ValveOpenCloseCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): ValveOpenCloseCardFeatureConfig {
|
||||
return {
|
||||
@@ -74,13 +89,13 @@ class HuiValveOpenCloseCardFeature
|
||||
}
|
||||
|
||||
private _onOpenValve(): void {
|
||||
this.hass!.callService("valve", "open_valve", {
|
||||
this._api.callService("valve", "open_valve", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _onCloseValve(): void {
|
||||
this.hass!.callService("valve", "close_valve", {
|
||||
this._api.callService("valve", "close_valve", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
@@ -97,7 +112,7 @@ class HuiValveOpenCloseCardFeature
|
||||
|
||||
private _onStopTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this.hass!.callService("valve", "stop_valve", {
|
||||
this._api.callService("valve", "stop_valve", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
@@ -116,10 +131,9 @@ class HuiValveOpenCloseCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsValveOpenCloseCardFeature(this.hass, this.context)
|
||||
!supportsValveOpenCloseCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -146,7 +160,7 @@ class HuiValveOpenCloseCardFeature
|
||||
supportsFeature(this._stateObj, ValveEntityFeature.CLOSE)
|
||||
? html`
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.valve.close_valve")}
|
||||
.label=${this._localize("ui.card.valve.close_valve")}
|
||||
@click=${this._onCloseTap}
|
||||
.disabled=${!canClose(this._stateObj)}
|
||||
class=${classMap({
|
||||
@@ -165,7 +179,7 @@ class HuiValveOpenCloseCardFeature
|
||||
supportsFeature(this._stateObj, ValveEntityFeature.STOP)
|
||||
? html`
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.valve.stop_valve")}
|
||||
.label=${this._localize("ui.card.valve.stop_valve")}
|
||||
@click=${this._onStopTap}
|
||||
.disabled=${!canStop(this._stateObj)}
|
||||
>
|
||||
@@ -178,7 +192,7 @@ class HuiValveOpenCloseCardFeature
|
||||
supportsFeature(this._stateObj, ValveEntityFeature.OPEN)
|
||||
? html`
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.valve.open_valve")}
|
||||
.label=${this._localize("ui.card.valve.open_valve")}
|
||||
@click=${this._onOpenTap}
|
||||
.disabled=${!canOpen(this._stateObj)}
|
||||
class=${classMap({
|
||||
@@ -203,7 +217,7 @@ class HuiValveOpenCloseCardFeature
|
||||
.pathOff=${closedIcon}
|
||||
.checked=${isOpen}
|
||||
@change=${this._valueChanged}
|
||||
.label=${this.hass.localize("ui.card.common.toggle")}
|
||||
.label=${this._localize("ui.card.common.toggle")}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-switch>
|
||||
|
||||
@@ -1,18 +1,36 @@
|
||||
import { consume } from "@lit/context";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { computeAttributeNameDisplay } from "../../../common/entity/compute_attribute_display";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateActive } from "../../../common/entity/state_active";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-slider";
|
||||
import {
|
||||
apiContext,
|
||||
entitiesContext,
|
||||
internationalizationContext,
|
||||
} from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../data/entity/entity_attributes";
|
||||
import type { FrontendLocaleData } from "../../../data/translation";
|
||||
import { ValveEntityFeature, type ValveEntity } from "../../../data/valve";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantInternationalization,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
@@ -20,6 +38,14 @@ import type {
|
||||
ValvePositionCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
const supportsValvePositionCardFeatureFromState = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "valve" &&
|
||||
supportsFeature(stateObj, ValveEntityFeature.SET_POSITION)
|
||||
);
|
||||
};
|
||||
|
||||
export const supportsValvePositionCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -28,11 +54,7 @@ export const supportsValvePositionCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "valve" &&
|
||||
supportsFeature(stateObj, ValveEntityFeature.SET_POSITION)
|
||||
);
|
||||
return supportsValvePositionCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-valve-position-card-feature")
|
||||
@@ -40,20 +62,34 @@ class HuiValvePositionCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@property({ attribute: false }) public color?: string;
|
||||
|
||||
@state() private _config?: ValvePositionCardFeatureConfig;
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "entity_id"] })
|
||||
private _stateObj?: ValveEntity;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as ValveEntity | undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: HomeAssistantApi;
|
||||
|
||||
@state()
|
||||
@consume({ context: entitiesContext, subscribe: true })
|
||||
private _entities!: HomeAssistant["entities"];
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
@transform<HomeAssistantInternationalization, FrontendLocaleData>({
|
||||
transformer: ({ locale }) => locale,
|
||||
})
|
||||
private _locale?: FrontendLocaleData;
|
||||
|
||||
@state() private _config?: ValvePositionCardFeatureConfig;
|
||||
|
||||
static getStubConfig(): ValvePositionCardFeatureConfig {
|
||||
return {
|
||||
@@ -71,10 +107,9 @@ class HuiValvePositionCardFeature
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsValvePositionCardFeature(this.hass, this.context)
|
||||
!supportsValvePositionCardFeatureFromState(this._stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -108,14 +143,14 @@ class HuiValvePositionCardFeature
|
||||
show-handle
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${computeAttributeNameDisplay(
|
||||
this.hass.localize,
|
||||
this._localize,
|
||||
this._stateObj,
|
||||
this.hass.entities,
|
||||
this._entities,
|
||||
"current_position"
|
||||
)}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
.unit=${DOMAIN_ATTRIBUTES_UNITS.valve.current_position}
|
||||
.locale=${this.hass.locale}
|
||||
.locale=${this._locale}
|
||||
></ha-control-slider>
|
||||
`;
|
||||
}
|
||||
@@ -124,7 +159,7 @@ class HuiValvePositionCardFeature
|
||||
const { value } = ev.detail;
|
||||
if (typeof value !== "number" || isNaN(value)) return;
|
||||
|
||||
this.hass!.callService("valve", "set_valve_position", {
|
||||
this._api.callService("valve", "set_valve_position", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
position: value,
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { mdiWaterBoiler } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
@@ -13,6 +14,13 @@ import type {
|
||||
WaterHeaterOperationModesCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
const supportsWaterHeaterOperationModesCardFeatureFromState = (
|
||||
stateObj: HassEntity
|
||||
) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "water_heater";
|
||||
};
|
||||
|
||||
export const supportsWaterHeaterOperationModesCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
@@ -21,8 +29,7 @@ export const supportsWaterHeaterOperationModesCardFeature = (
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "water_heater";
|
||||
return supportsWaterHeaterOperationModesCardFeatureFromState(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-water-heater-operation-modes-card-feature")
|
||||
@@ -48,7 +55,7 @@ class HuiWaterHeaterOperationModeCardFeature
|
||||
protected readonly _serviceAction = "set_operation_mode";
|
||||
|
||||
protected get _label(): string {
|
||||
return this.hass!.localize("ui.card.water_heater.mode");
|
||||
return this._localize("ui.card.water_heater.mode");
|
||||
}
|
||||
|
||||
protected readonly _defaultStyle = "icons";
|
||||
@@ -82,7 +89,7 @@ class HuiWaterHeaterOperationModeCardFeature
|
||||
}
|
||||
|
||||
protected _getOptions() {
|
||||
if (!this._stateObj || !this.hass) {
|
||||
if (!this._stateObj) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -94,16 +101,15 @@ class HuiWaterHeaterOperationModeCardFeature
|
||||
return filterModes(orderedModes, this._config?.operation_modes).map(
|
||||
(mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityState(this._stateObj!, mode),
|
||||
label: this._formatters.formatEntityState(this._stateObj!, mode),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsWaterHeaterOperationModesCardFeature(this.hass, this.context)
|
||||
this._stateObj &&
|
||||
supportsWaterHeaterOperationModesCardFeatureFromState(this._stateObj)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues<this>): boolean {
|
||||
// Side Effect used to update footer hass while keeping optimizations
|
||||
if (this._footerElement) {
|
||||
if (this._footerElement && "hass" in this._footerElement) {
|
||||
this._footerElement.hass = this.hass;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { mdiAlertCircleOutline, mdiAlertOutline } from "@mdi/js";
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { consumeLocalize } from "../../../common/decorators/consume-context-entry";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { configContext } from "../../../data/context";
|
||||
import type { LovelaceCard, LovelaceGridOptions } from "../types";
|
||||
import type { ErrorCardConfig } from "./types";
|
||||
|
||||
@@ -14,7 +17,13 @@ const ERROR_ICONS = {
|
||||
|
||||
@customElement("hui-error-card")
|
||||
export class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
@state()
|
||||
@consumeLocalize()
|
||||
private _localize?: LocalizeFunc;
|
||||
|
||||
@state()
|
||||
@consume({ context: configContext, subscribe: true })
|
||||
private _hassConfig?: ContextType<typeof configContext>;
|
||||
|
||||
@property({ attribute: false }) public preview = false;
|
||||
|
||||
@@ -45,10 +54,12 @@ export class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||
const error =
|
||||
this._config?.error ||
|
||||
(this.severity === "warning" &&
|
||||
this.hass?.localize("ui.errors.config.configuration_warning")) ||
|
||||
this.hass?.localize("ui.errors.config.configuration_error");
|
||||
this._localize?.("ui.errors.config.configuration_warning")) ||
|
||||
this._localize?.("ui.errors.config.configuration_error");
|
||||
const showTitle =
|
||||
this.hass === undefined || this.hass?.user?.is_admin || this.preview;
|
||||
this._hassConfig === undefined ||
|
||||
this._hassConfig.user?.is_admin ||
|
||||
this.preview;
|
||||
const showMessage = this.preview;
|
||||
|
||||
return html`
|
||||
|
||||
@@ -68,9 +68,6 @@ export abstract class HuiStackCard<T extends StackCardConfig = StackCardConfig>
|
||||
this._cards.forEach((card) => {
|
||||
card.hass = this.hass;
|
||||
});
|
||||
if (this._errorCard) {
|
||||
this._errorCard.hass = this.hass;
|
||||
}
|
||||
}
|
||||
if (changedProperties.has("preview")) {
|
||||
this._cards.forEach((card) => {
|
||||
|
||||
@@ -254,7 +254,7 @@ export class HuiStatisticCard extends LitElement implements LovelaceCard {
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
// Side Effect used to update footer hass while keeping optimizations
|
||||
if (this._footerElement) {
|
||||
if (this._footerElement && "hass" in this._footerElement) {
|
||||
this._footerElement.hass = this.hass;
|
||||
}
|
||||
if (
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { STATE_NOT_RUNNING } from "home-assistant-js-websocket";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../components/ha-alert";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "../cards/hui-error-card";
|
||||
|
||||
export const createEntityNotFoundWarning = (
|
||||
hass: HomeAssistant,
|
||||
hass: Pick<HomeAssistant, "config" | "localize">,
|
||||
// left for backwards compatibility for custom cards
|
||||
_entityId: string
|
||||
) =>
|
||||
@@ -17,10 +17,8 @@ export const createEntityNotFoundWarning = (
|
||||
|
||||
@customElement("hui-warning")
|
||||
export class HuiWarning extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<hui-error-card .hass=${this.hass} severity="warning"
|
||||
return html`<hui-error-card severity="warning"
|
||||
><slot></slot
|
||||
></hui-error-card>`;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,9 @@ class EntityRowDirective extends Directive {
|
||||
}
|
||||
this._entityId = entityId;
|
||||
this._name = name;
|
||||
this._element.hass = hass;
|
||||
if ("hass" in this._element) {
|
||||
this._element.hass = hass;
|
||||
}
|
||||
return this._element;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,13 @@ export const getMyRedirects = (): Redirects => ({
|
||||
component: "energy",
|
||||
redirect: "/config/energy",
|
||||
},
|
||||
config_infrared: {
|
||||
redirect: "/config/infrared",
|
||||
},
|
||||
config_radiofrequency: {
|
||||
component: "radio_frequency",
|
||||
redirect: "/config/radio-frequency",
|
||||
},
|
||||
config_ssdp: {
|
||||
component: "ssdp",
|
||||
redirect: "/config/ssdp",
|
||||
|
||||
@@ -3904,7 +3904,9 @@
|
||||
},
|
||||
"templates": {
|
||||
"title": "Template",
|
||||
"description": "Templates are rendered using the Jinja2 template engine with some Home Assistant specific extensions.",
|
||||
"description": "Templates let you generate dynamic content from your Home Assistant data, such as a notification that lists which lights are on, or a sensor whose value is calculated from several other entities.",
|
||||
"engine_info": "Home Assistant uses the Jinja templating engine, extended with functions for working with your entities, areas, devices, and more. Write a template in the editor below and its result updates live as your states change.",
|
||||
"learn_more": "Learn more",
|
||||
"about": "About templates",
|
||||
"editor": "Template editor",
|
||||
"result": "Result",
|
||||
@@ -3912,8 +3914,14 @@
|
||||
"confirm_reset": "Do you want to reset your current template back to the demo template?",
|
||||
"confirm_clear": "Do you want to clear your current template?",
|
||||
"result_type": "Result type",
|
||||
"jinja_documentation": "Jinja2 template documentation",
|
||||
"template_extensions": "Home Assistant template extensions",
|
||||
"docs_introduction": "Introduction to templating",
|
||||
"docs_introduction_description": "Start here for a step-by-step guide.",
|
||||
"docs_states": "Working with states",
|
||||
"docs_states_description": "Read entity states and attributes in templates.",
|
||||
"docs_debugging": "Debugging templates",
|
||||
"docs_debugging_description": "Find and fix problems in your templates.",
|
||||
"docs_functions": "Template functions reference",
|
||||
"docs_functions_description": "Search every available function, filter, and test.",
|
||||
"unknown_error_template": "Unknown error rendering template",
|
||||
"time": "This template updates at the start of each minute.",
|
||||
"all_listeners": "This template listens for all state changed events.",
|
||||
|
||||
@@ -4618,22 +4618,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rsdoctor/client@npm:1.5.16":
|
||||
version: 1.5.16
|
||||
resolution: "@rsdoctor/client@npm:1.5.16"
|
||||
checksum: 10/dcda4e8034a296090b073102423050764e636f9801f2e5a5904e1f2744b1fe26d5f8202aed7a785c9fc809044e1aef5c4b6a16b63974caec79d1b5996ec35d34
|
||||
"@rsdoctor/client@npm:1.5.17":
|
||||
version: 1.5.17
|
||||
resolution: "@rsdoctor/client@npm:1.5.17"
|
||||
checksum: 10/0eb788455390a1b41aa31d982d93ceab3dd30671776e40e8a4ea3256b4713f6441066e079ff9a14413825e21d547b9b7d4ba52059f8995644e26724ff07bbf56
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rsdoctor/core@npm:1.5.16":
|
||||
version: 1.5.16
|
||||
resolution: "@rsdoctor/core@npm:1.5.16"
|
||||
"@rsdoctor/core@npm:1.5.17":
|
||||
version: 1.5.17
|
||||
resolution: "@rsdoctor/core@npm:1.5.17"
|
||||
dependencies:
|
||||
"@rsbuild/plugin-check-syntax": "npm:^1.6.1"
|
||||
"@rsdoctor/graph": "npm:1.5.16"
|
||||
"@rsdoctor/sdk": "npm:1.5.16"
|
||||
"@rsdoctor/types": "npm:1.5.16"
|
||||
"@rsdoctor/utils": "npm:1.5.16"
|
||||
"@rsdoctor/graph": "npm:1.5.17"
|
||||
"@rsdoctor/sdk": "npm:1.5.17"
|
||||
"@rsdoctor/types": "npm:1.5.17"
|
||||
"@rsdoctor/utils": "npm:1.5.17"
|
||||
"@rspack/resolver": "npm:^0.2.8"
|
||||
browserslist-load-config: "npm:^1.0.2"
|
||||
es-toolkit: "npm:^1.47.0"
|
||||
@@ -4641,60 +4641,60 @@ __metadata:
|
||||
fs-extra: "npm:^11.1.1"
|
||||
semver: "npm:^7.7.4"
|
||||
source-map: "npm:^0.7.6"
|
||||
checksum: 10/be7b03b5a5a8a9be47f94159469c35488f98046c99e2ccd7daed325c3dd2a8b21c654c12ac6d40c2775546efade373f429f630e8905cc17a5c9151978a0caaf9
|
||||
checksum: 10/a797d5243d1d3f758d8b38cea1a3195345525c3158c4061f5e78d875fe2001198c8967587153059eb7c5ff764f27b4685ce13fd55a27dccdcfe7cd8061fb30c9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rsdoctor/graph@npm:1.5.16":
|
||||
version: 1.5.16
|
||||
resolution: "@rsdoctor/graph@npm:1.5.16"
|
||||
"@rsdoctor/graph@npm:1.5.17":
|
||||
version: 1.5.17
|
||||
resolution: "@rsdoctor/graph@npm:1.5.17"
|
||||
dependencies:
|
||||
"@rsdoctor/types": "npm:1.5.16"
|
||||
"@rsdoctor/utils": "npm:1.5.16"
|
||||
"@rsdoctor/types": "npm:1.5.17"
|
||||
"@rsdoctor/utils": "npm:1.5.17"
|
||||
es-toolkit: "npm:^1.47.0"
|
||||
path-browserify: "npm:1.0.1"
|
||||
source-map: "npm:^0.7.6"
|
||||
checksum: 10/949e3a2cc48ccbb2d554becb2270c4df4b4fc8a6e10bd55bf9dc4d5f9a5fb2823c3e11a30dce890beaaa1b0ab0039bba1554ad0e7ddc5e2ea47641222d454633
|
||||
checksum: 10/e58ed532ea8cc743e45dd66b678e1da3d48939fe711ebfade47834ffc581be6089d611d18c97c3e12010e2c609049cb325d07021a5dc51ef651314f8fe9f5741
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rsdoctor/rspack-plugin@npm:1.5.16":
|
||||
version: 1.5.16
|
||||
resolution: "@rsdoctor/rspack-plugin@npm:1.5.16"
|
||||
"@rsdoctor/rspack-plugin@npm:1.5.17":
|
||||
version: 1.5.17
|
||||
resolution: "@rsdoctor/rspack-plugin@npm:1.5.17"
|
||||
dependencies:
|
||||
"@rsdoctor/core": "npm:1.5.16"
|
||||
"@rsdoctor/graph": "npm:1.5.16"
|
||||
"@rsdoctor/sdk": "npm:1.5.16"
|
||||
"@rsdoctor/types": "npm:1.5.16"
|
||||
"@rsdoctor/utils": "npm:1.5.16"
|
||||
"@rsdoctor/core": "npm:1.5.17"
|
||||
"@rsdoctor/graph": "npm:1.5.17"
|
||||
"@rsdoctor/sdk": "npm:1.5.17"
|
||||
"@rsdoctor/types": "npm:1.5.17"
|
||||
"@rsdoctor/utils": "npm:1.5.17"
|
||||
peerDependencies:
|
||||
"@rspack/core": "*"
|
||||
peerDependenciesMeta:
|
||||
"@rspack/core":
|
||||
optional: true
|
||||
checksum: 10/2bebf2b8dfc5ffde77b46b45fedc7d5d9b96f4fe3e5e1b3762bff5de6f278d9288fe6c361fa1df5bb17926a45ae3c6038e165d4f1f5e867724df0a530590b36a
|
||||
checksum: 10/336bd813010a7c164770033ae5a30644bf165ce0dff250b9160c7c003401c214bfd96e528c5941c09834bb21949a4815c46ecae0ca4d9200b2b946b9c3164f8a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rsdoctor/sdk@npm:1.5.16":
|
||||
version: 1.5.16
|
||||
resolution: "@rsdoctor/sdk@npm:1.5.16"
|
||||
"@rsdoctor/sdk@npm:1.5.17":
|
||||
version: 1.5.17
|
||||
resolution: "@rsdoctor/sdk@npm:1.5.17"
|
||||
dependencies:
|
||||
"@rsdoctor/client": "npm:1.5.16"
|
||||
"@rsdoctor/graph": "npm:1.5.16"
|
||||
"@rsdoctor/types": "npm:1.5.16"
|
||||
"@rsdoctor/utils": "npm:1.5.16"
|
||||
"@rsdoctor/client": "npm:1.5.17"
|
||||
"@rsdoctor/graph": "npm:1.5.17"
|
||||
"@rsdoctor/types": "npm:1.5.17"
|
||||
"@rsdoctor/utils": "npm:1.5.17"
|
||||
launch-editor: "npm:^2.13.2"
|
||||
safer-buffer: "npm:2.1.2"
|
||||
socket.io: "npm:4.8.1"
|
||||
tapable: "npm:2.3.3"
|
||||
checksum: 10/8a845468e13c66b93f9784c7887f7040b1df24f43e9304b50c3a7258c6b172c2bc3ca5f6e5e9d15801d1634e3e48f6bc78115a7a0242890548d111ae512caf7d
|
||||
checksum: 10/d8a146a43726d61a9d7d2cfca7e2cd48c42a7ba28c9d280353f986f1647c0a33f05ec68a45af4f22104df8a8dae7dc14d621e56a15f11c3967d78c21216be234
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rsdoctor/types@npm:1.5.16":
|
||||
version: 1.5.16
|
||||
resolution: "@rsdoctor/types@npm:1.5.16"
|
||||
"@rsdoctor/types@npm:1.5.17":
|
||||
version: 1.5.17
|
||||
resolution: "@rsdoctor/types@npm:1.5.17"
|
||||
dependencies:
|
||||
"@types/connect": "npm:3.4.38"
|
||||
"@types/estree": "npm:1.0.5"
|
||||
@@ -4708,16 +4708,16 @@ __metadata:
|
||||
optional: true
|
||||
webpack:
|
||||
optional: true
|
||||
checksum: 10/f470a7047474669bd466c9cee15b5ef3e4b854d4347e3dd4f251f1d1f92bd9b7b5e6863349f5d3550485c3484497a1e8be71cefc56e883c81f8a734960d1fc5e
|
||||
checksum: 10/4767825ae55498e25d1dfbecc0aebe2685b67701dae004617f4d95a68b85f19f29afe75f72b8efeab4fe49185bb9a3c85dcfce8f99852c49d4ee37a4f6b7d888
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rsdoctor/utils@npm:1.5.16":
|
||||
version: 1.5.16
|
||||
resolution: "@rsdoctor/utils@npm:1.5.16"
|
||||
"@rsdoctor/utils@npm:1.5.17":
|
||||
version: 1.5.17
|
||||
resolution: "@rsdoctor/utils@npm:1.5.17"
|
||||
dependencies:
|
||||
"@babel/code-frame": "npm:7.26.2"
|
||||
"@rsdoctor/types": "npm:1.5.16"
|
||||
"@rsdoctor/types": "npm:1.5.17"
|
||||
"@types/estree": "npm:1.0.5"
|
||||
acorn: "npm:^8.10.0"
|
||||
acorn-import-attributes: "npm:^1.9.5"
|
||||
@@ -4731,7 +4731,7 @@ __metadata:
|
||||
picocolors: "npm:^1.1.1"
|
||||
rslog: "npm:^2.1.2"
|
||||
strip-ansi: "npm:^6.0.1"
|
||||
checksum: 10/d73062cc01f4e2def276d6515f2810f54bfd7f819f1d3a00dd884621f9c008eda4b31ae6e6d96efaec81d1bbad98f0f49cb77e14a028d5f53c97aca84b6b07d4
|
||||
checksum: 10/7c9b4a3824de61f6254df50f80c5efe53df662f30cb07047afa956a9bc3917dc71bf0aa73c8edde7f73f9577a9cb051e1a81caaffa118db414a4b655223fe92a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -8565,9 +8565,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-plugin-import-x@npm:4.17.0":
|
||||
version: 4.17.0
|
||||
resolution: "eslint-plugin-import-x@npm:4.17.0"
|
||||
"eslint-plugin-import-x@npm:4.17.1":
|
||||
version: 4.17.1
|
||||
resolution: "eslint-plugin-import-x@npm:4.17.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:^8.56.0"
|
||||
comment-parser: "npm:^1.4.1"
|
||||
@@ -8587,7 +8587,7 @@ __metadata:
|
||||
optional: true
|
||||
eslint-import-resolver-node:
|
||||
optional: true
|
||||
checksum: 10/143081e0a2cb418990d5d61c08ad4dd46f4f10dd7664939cc4be8454c2f51cd69134746d2d8b7534786f3a13857d176456bb0c1d1ffc9c168830b4ce93d2c0a8
|
||||
checksum: 10/1cb95284765cf0ff937f7ab44cf965278939c25dedc72ac41d00d954f4c0bd607bcaffacdeb22a3796aa8f2775c63dd25f818c7f897f061f2339035826cbaf5c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -9771,7 +9771,7 @@ __metadata:
|
||||
"@octokit/rest": "npm:22.0.1"
|
||||
"@playwright/test": "npm:1.61.1"
|
||||
"@replit/codemirror-indentation-markers": "npm:6.5.3"
|
||||
"@rsdoctor/rspack-plugin": "npm:1.5.16"
|
||||
"@rsdoctor/rspack-plugin": "npm:1.5.17"
|
||||
"@rspack/core": "npm:2.1.1"
|
||||
"@rspack/dev-server": "npm:2.1.0"
|
||||
"@swc/helpers": "npm:0.5.23"
|
||||
@@ -9817,7 +9817,7 @@ __metadata:
|
||||
eslint: "npm:10.6.0"
|
||||
eslint-config-prettier: "npm:10.1.8"
|
||||
eslint-import-resolver-webpack: "npm:0.13.11"
|
||||
eslint-plugin-import-x: "npm:4.17.0"
|
||||
eslint-plugin-import-x: "npm:4.17.1"
|
||||
eslint-plugin-lit: "npm:2.3.1"
|
||||
eslint-plugin-lit-a11y: "npm:5.1.1"
|
||||
eslint-plugin-unused-imports: "npm:4.4.1"
|
||||
@@ -9837,7 +9837,7 @@ __metadata:
|
||||
home-assistant-js-websocket: "npm:9.6.0"
|
||||
html-minifier-terser: "npm:7.2.0"
|
||||
husky: "npm:9.1.7"
|
||||
idb-keyval: "npm:6.2.5"
|
||||
idb-keyval: "npm:6.2.6"
|
||||
intl-messageformat: "npm:11.2.9"
|
||||
js-yaml: "npm:5.2.0"
|
||||
jsdom: "npm:29.1.1"
|
||||
@@ -9860,7 +9860,7 @@ __metadata:
|
||||
node-vibrant: "npm:4.0.4"
|
||||
object-hash: "npm:3.0.0"
|
||||
pinst: "npm:3.0.0"
|
||||
prettier: "npm:3.9.1"
|
||||
prettier: "npm:3.9.3"
|
||||
punycode: "npm:2.3.1"
|
||||
qr-scanner: "npm:1.4.2"
|
||||
qrcode: "npm:1.5.4"
|
||||
@@ -10056,10 +10056,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"idb-keyval@npm:6.2.5":
|
||||
version: 6.2.5
|
||||
resolution: "idb-keyval@npm:6.2.5"
|
||||
checksum: 10/ac645882b3258ff07347d085baab91b871bac7be4f46ff8e20a7c036c2df35d3f695a30050009f27237b99045203568f2a842a35295a48f9b815959ee51a347e
|
||||
"idb-keyval@npm:6.2.6":
|
||||
version: 6.2.6
|
||||
resolution: "idb-keyval@npm:6.2.6"
|
||||
checksum: 10/8d0f8b9bd5eead685731a900510095dbc58936968739755bfd1de1c69a710daa5eb2b5cf185d0a7c7e9ce1daf4544fa5f58a2c7a37258a6826dd40f9e2614245
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -12746,12 +12746,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prettier@npm:3.9.1":
|
||||
version: 3.9.1
|
||||
resolution: "prettier@npm:3.9.1"
|
||||
"prettier@npm:3.9.3":
|
||||
version: 3.9.3
|
||||
resolution: "prettier@npm:3.9.3"
|
||||
bin:
|
||||
prettier: bin/prettier.cjs
|
||||
checksum: 10/1b4317674aa9e90ff79c347fd19f91bb305df98b3122e7131d6815291707781305c45a13cd982474c2f74ed748f2fd0a9aa094f9856609ed1b6f092de8152058
|
||||
checksum: 10/2aa4232a7ae2204a6d0758e8083117509f13a499ae49f87ed8e4a9c15967083f400e4e189a64948de60987974d3441f4b5e5110e7a3e56f9b83f4e2904cef376
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user