mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 19:56:42 +00:00
Improve open and opening state for lock (#20808)
* Add open and opening color * Split isAvailable into multiple function * Use done wording
This commit is contained in:
parent
79abcca3b3
commit
4cc5d2d04b
@ -26,6 +26,10 @@ export function isLocked(stateObj: LockEntity) {
|
|||||||
return stateObj.state === "locked";
|
return stateObj.state === "locked";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isUnlocked(stateObj: LockEntity) {
|
||||||
|
return stateObj.state === "unlocked";
|
||||||
|
}
|
||||||
|
|
||||||
export function isUnlocking(stateObj: LockEntity) {
|
export function isUnlocking(stateObj: LockEntity) {
|
||||||
return stateObj.state === "unlocking";
|
return stateObj.state === "unlocking";
|
||||||
}
|
}
|
||||||
@ -38,15 +42,40 @@ export function isJammed(stateObj: LockEntity) {
|
|||||||
return stateObj.state === "jammed";
|
return stateObj.state === "jammed";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isAvailable(stateObj: LockEntity) {
|
export function isOpen(stateObj: LockEntity) {
|
||||||
|
return stateObj.state === "open";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isOpening(stateObj: LockEntity) {
|
||||||
|
return stateObj.state === "opening";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isWaiting(stateObj: LockEntity) {
|
||||||
|
return ["opening", "unlocking", "locking"].includes(stateObj.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canOpen(stateObj: LockEntity) {
|
||||||
if (stateObj.state === UNAVAILABLE) {
|
if (stateObj.state === UNAVAILABLE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const assumedState = stateObj.attributes.assumed_state === true;
|
const assumedState = stateObj.attributes.assumed_state === true;
|
||||||
return (
|
return assumedState || (!isOpen(stateObj) && !isWaiting(stateObj));
|
||||||
assumedState ||
|
}
|
||||||
(!isLocking(stateObj) && !isUnlocking(stateObj) && !isJammed(stateObj))
|
|
||||||
);
|
export function canLock(stateObj: LockEntity) {
|
||||||
|
if (stateObj.state === UNAVAILABLE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const assumedState = stateObj.attributes.assumed_state === true;
|
||||||
|
return assumedState || (!isLocked(stateObj) && !isWaiting(stateObj));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canUnlock(stateObj: LockEntity) {
|
||||||
|
if (stateObj.state === UNAVAILABLE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const assumedState = stateObj.attributes.assumed_state === true;
|
||||||
|
return assumedState || (!isUnlocked(stateObj) && !isWaiting(stateObj));
|
||||||
}
|
}
|
||||||
|
|
||||||
export const callProtectedLockService = async (
|
export const callProtectedLockService = async (
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
LockEntity,
|
LockEntity,
|
||||||
LockEntityFeature,
|
LockEntityFeature,
|
||||||
callProtectedLockService,
|
callProtectedLockService,
|
||||||
isAvailable,
|
canOpen,
|
||||||
isJammed,
|
isJammed,
|
||||||
} from "../../../data/lock";
|
} from "../../../data/lock";
|
||||||
import "../../../state-control/lock/ha-state-control-lock-toggle";
|
import "../../../state-control/lock/ha-state-control-lock-toggle";
|
||||||
@ -22,9 +22,9 @@ import "../components/ha-more-info-state-header";
|
|||||||
import { moreInfoControlStyle } from "../components/more-info-control-style";
|
import { moreInfoControlStyle } from "../components/more-info-control-style";
|
||||||
|
|
||||||
const CONFIRM_TIMEOUT_SECOND = 5;
|
const CONFIRM_TIMEOUT_SECOND = 5;
|
||||||
const OPENED_TIMEOUT_SECOND = 3;
|
const DONE_TIMEOUT_SECOND = 2;
|
||||||
|
|
||||||
type ButtonState = "normal" | "confirm" | "success";
|
type ButtonState = "normal" | "confirm" | "done";
|
||||||
|
|
||||||
@customElement("more-info-lock")
|
@customElement("more-info-lock")
|
||||||
class MoreInfoLock extends LitElement {
|
class MoreInfoLock extends LitElement {
|
||||||
@ -54,7 +54,7 @@ class MoreInfoLock extends LitElement {
|
|||||||
|
|
||||||
callProtectedLockService(this, this.hass, this.stateObj!, "open");
|
callProtectedLockService(this, this.hass, this.stateObj!, "open");
|
||||||
|
|
||||||
this._setButtonState("success", OPENED_TIMEOUT_SECOND);
|
this._setButtonState("done", DONE_TIMEOUT_SECOND);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _resetButtonState() {
|
private _resetButtonState() {
|
||||||
@ -115,16 +115,16 @@ class MoreInfoLock extends LitElement {
|
|||||||
${supportsOpen
|
${supportsOpen
|
||||||
? html`
|
? html`
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
${this._buttonState === "success"
|
${this._buttonState === "done"
|
||||||
? html`
|
? html`
|
||||||
<p class="open-success">
|
<p class="open-done">
|
||||||
<ha-svg-icon path=${mdiCheck}></ha-svg-icon>
|
<ha-svg-icon path=${mdiCheck}></ha-svg-icon>
|
||||||
${this.hass.localize("ui.card.lock.open_door_success")}
|
${this.hass.localize("ui.card.lock.open_door_done")}
|
||||||
</p>
|
</p>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<ha-control-button
|
<ha-control-button
|
||||||
.disabled=${!isAvailable(this.stateObj)}
|
.disabled=${!canOpen(this.stateObj)}
|
||||||
class="open-button ${this._buttonState}"
|
class="open-button ${this._buttonState}"
|
||||||
@click=${this._open}
|
@click=${this._open}
|
||||||
>
|
>
|
||||||
@ -175,7 +175,7 @@ class MoreInfoLock extends LitElement {
|
|||||||
.open-button.confirm {
|
.open-button.confirm {
|
||||||
--control-button-background-color: var(--warning-color);
|
--control-button-background-color: var(--warning-color);
|
||||||
}
|
}
|
||||||
.open-success {
|
.open-done {
|
||||||
line-height: 60px;
|
line-height: 60px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -1,23 +1,20 @@
|
|||||||
import { mdiLock, mdiLockOpenVariant } from "@mdi/js";
|
import { mdiLock, mdiLockOpenVariant } from "@mdi/js";
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
|
||||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||||
|
|
||||||
import "../../../components/ha-control-button";
|
import "../../../components/ha-control-button";
|
||||||
import "../../../components/ha-control-button-group";
|
import "../../../components/ha-control-button-group";
|
||||||
|
import { forwardHaptic } from "../../../data/haptics";
|
||||||
import {
|
import {
|
||||||
callProtectedLockService,
|
callProtectedLockService,
|
||||||
isAvailable,
|
canLock,
|
||||||
isLocking,
|
canUnlock,
|
||||||
isUnlocking,
|
|
||||||
isLocked,
|
|
||||||
} from "../../../data/lock";
|
} from "../../../data/lock";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { LovelaceCardFeature } from "../types";
|
import { LovelaceCardFeature } from "../types";
|
||||||
import { LockCommandsCardFeatureConfig } from "./types";
|
import { LockCommandsCardFeatureConfig } from "./types";
|
||||||
import { forwardHaptic } from "../../../data/haptics";
|
|
||||||
|
|
||||||
export const supportsLockCommandsCardFeature = (stateObj: HassEntity) => {
|
export const supportsLockCommandsCardFeature = (stateObj: HassEntity) => {
|
||||||
const domain = computeDomain(stateObj.entity_id);
|
const domain = computeDomain(stateObj.entity_id);
|
||||||
@ -72,23 +69,17 @@ class HuiLockCommandsCardFeature
|
|||||||
<ha-control-button-group>
|
<ha-control-button-group>
|
||||||
<ha-control-button
|
<ha-control-button
|
||||||
.label=${this.hass.localize("ui.card.lock.lock")}
|
.label=${this.hass.localize("ui.card.lock.lock")}
|
||||||
.disabled=${!isAvailable(this.stateObj) || isLocked(this.stateObj)}
|
.disabled=${!canLock(this.stateObj)}
|
||||||
@click=${this._onTap}
|
@click=${this._onTap}
|
||||||
data-service="lock"
|
data-service="lock"
|
||||||
class=${classMap({
|
|
||||||
pulse: isLocking(this.stateObj) || isUnlocking(this.stateObj),
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiLock}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiLock}></ha-svg-icon>
|
||||||
</ha-control-button>
|
</ha-control-button>
|
||||||
<ha-control-button
|
<ha-control-button
|
||||||
.label=${this.hass.localize("ui.card.lock.unlock")}
|
.label=${this.hass.localize("ui.card.lock.unlock")}
|
||||||
.disabled=${!isAvailable(this.stateObj) || !isLocked(this.stateObj)}
|
.disabled=${!canUnlock(this.stateObj)}
|
||||||
@click=${this._onTap}
|
@click=${this._onTap}
|
||||||
data-service="unlock"
|
data-service="unlock"
|
||||||
class=${classMap({
|
|
||||||
pulse: isLocking(this.stateObj) || isUnlocking(this.stateObj),
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiLockOpenVariant}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiLockOpenVariant}></ha-svg-icon>
|
||||||
</ha-control-button>
|
</ha-control-button>
|
||||||
@ -98,20 +89,6 @@ class HuiLockCommandsCardFeature
|
|||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
@keyframes pulse {
|
|
||||||
0% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.pulse {
|
|
||||||
animation: pulse 1s infinite;
|
|
||||||
}
|
|
||||||
ha-control-button-group {
|
ha-control-button-group {
|
||||||
margin: 0 12px 12px 12px;
|
margin: 0 12px 12px 12px;
|
||||||
--control-button-group-spacing: 12px;
|
--control-button-group-spacing: 12px;
|
||||||
|
@ -8,9 +8,9 @@ import { supportsFeature } from "../../../common/entity/supports-feature";
|
|||||||
import "../../../components/ha-control-button";
|
import "../../../components/ha-control-button";
|
||||||
import "../../../components/ha-control-button-group";
|
import "../../../components/ha-control-button-group";
|
||||||
import {
|
import {
|
||||||
LockEntityFeature,
|
|
||||||
callProtectedLockService,
|
callProtectedLockService,
|
||||||
isAvailable,
|
canOpen,
|
||||||
|
LockEntityFeature,
|
||||||
} from "../../../data/lock";
|
} from "../../../data/lock";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { LovelaceCardFeature } from "../types";
|
import { LovelaceCardFeature } from "../types";
|
||||||
@ -22,9 +22,9 @@ export const supportsLockOpenDoorCardFeature = (stateObj: HassEntity) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CONFIRM_TIMEOUT_SECOND = 5;
|
const CONFIRM_TIMEOUT_SECOND = 5;
|
||||||
const OPENED_TIMEOUT_SECOND = 3;
|
const DONE_TIMEOUT_SECOND = 2;
|
||||||
|
|
||||||
type ButtonState = "normal" | "confirm" | "success";
|
type ButtonState = "normal" | "confirm" | "done";
|
||||||
|
|
||||||
@customElement("hui-lock-open-door-card-feature")
|
@customElement("hui-lock-open-door-card-feature")
|
||||||
class HuiLockOpenDoorCardFeature
|
class HuiLockOpenDoorCardFeature
|
||||||
@ -74,7 +74,7 @@ class HuiLockOpenDoorCardFeature
|
|||||||
}
|
}
|
||||||
callProtectedLockService(this, this.hass, this.stateObj!, "open");
|
callProtectedLockService(this, this.hass, this.stateObj!, "open");
|
||||||
|
|
||||||
this._setButtonState("success", OPENED_TIMEOUT_SECOND);
|
this._setButtonState("done", DONE_TIMEOUT_SECOND);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
@ -88,17 +88,17 @@ class HuiLockOpenDoorCardFeature
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this._buttonState === "success"
|
${this._buttonState === "done"
|
||||||
? html`
|
? html`
|
||||||
<p class="open-success">
|
<p class="open-done">
|
||||||
<ha-svg-icon path=${mdiCheck}></ha-svg-icon>
|
<ha-svg-icon path=${mdiCheck}></ha-svg-icon>
|
||||||
${this.hass.localize("ui.card.lock.open_door_success")}
|
${this.hass.localize("ui.card.lock.open_door_done")}
|
||||||
</p>
|
</p>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<ha-control-button-group>
|
<ha-control-button-group>
|
||||||
<ha-control-button
|
<ha-control-button
|
||||||
.disabled=${!isAvailable(this.stateObj)}
|
.disabled=${!canOpen(this.stateObj)}
|
||||||
class="open-button ${this._buttonState}"
|
class="open-button ${this._buttonState}"
|
||||||
@click=${this._open}
|
@click=${this._open}
|
||||||
>
|
>
|
||||||
@ -126,7 +126,7 @@ class HuiLockOpenDoorCardFeature
|
|||||||
.open-button.confirm {
|
.open-button.confirm {
|
||||||
--control-button-background-color: var(--warning-color);
|
--control-button-background-color: var(--warning-color);
|
||||||
}
|
}
|
||||||
.open-success {
|
.open-done {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 14px;
|
line-height: 14px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -140,9 +140,6 @@ class HuiLockOpenDoorCardFeature
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
ha-control-button-group + ha-attributes:not([empty]) {
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,6 +165,8 @@ const mainStyles = css`
|
|||||||
--state-lock-locked-color: var(--green-color);
|
--state-lock-locked-color: var(--green-color);
|
||||||
--state-lock-pending-color: var(--orange-color);
|
--state-lock-pending-color: var(--orange-color);
|
||||||
--state-lock-unlocked-color: var(--red-color);
|
--state-lock-unlocked-color: var(--red-color);
|
||||||
|
--state-lock-opening-color: var(--orange-color);
|
||||||
|
--state-lock-open-color: var(--red-color);
|
||||||
--state-media_player-active-color: var(--light-blue-color);
|
--state-media_player-active-color: var(--light-blue-color);
|
||||||
--state-person-active-color: var(--blue-color);
|
--state-person-active-color: var(--blue-color);
|
||||||
--state-person-home-color: var(--green-color);
|
--state-person-home-color: var(--green-color);
|
||||||
|
@ -190,7 +190,7 @@
|
|||||||
"open": "Open",
|
"open": "Open",
|
||||||
"open_door": "Open door",
|
"open_door": "Open door",
|
||||||
"open_door_confirm": "Really open?",
|
"open_door_confirm": "Really open?",
|
||||||
"open_door_success": "Door open"
|
"open_door_done": "Done"
|
||||||
},
|
},
|
||||||
"media_player": {
|
"media_player": {
|
||||||
"source": "Source",
|
"source": "Source",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user