202 lines
5.3 KiB
TypeScript
202 lines
5.3 KiB
TypeScript
import {
|
|
css,
|
|
CSSResultGroup,
|
|
html,
|
|
LitElement,
|
|
PropertyValues,
|
|
TemplateResult,
|
|
} from "lit";
|
|
import { customElement, property, state } from "lit/decorators";
|
|
import { classMap } from "lit/directives/class-map";
|
|
import { styleMap } from "lit/directives/style-map";
|
|
import { stateColorCss } from "../../common/entity/state_color";
|
|
import "../../components/ha-control-button";
|
|
import "../../components/ha-control-switch";
|
|
import "../../components/ha-state-icon";
|
|
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
|
|
import { forwardHaptic } from "../../data/haptics";
|
|
import { callProtectedLockService, LockEntity } from "../../data/lock";
|
|
import { HomeAssistant } from "../../types";
|
|
|
|
@customElement("ha-state-control-lock-toggle")
|
|
export class HaStateControlLockToggle extends LitElement {
|
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
|
|
@property({ attribute: false }) public stateObj!: LockEntity;
|
|
|
|
@state() private _isOn = false;
|
|
|
|
public willUpdate(changedProps: PropertyValues): void {
|
|
super.willUpdate(changedProps);
|
|
if (changedProps.has("stateObj")) {
|
|
this._isOn =
|
|
this.stateObj.state === "locked" || this.stateObj.state === "locking";
|
|
}
|
|
}
|
|
|
|
private _valueChanged(ev) {
|
|
const checked = ev.target.checked as boolean;
|
|
|
|
if (checked) {
|
|
this._turnOn();
|
|
} else {
|
|
this._turnOff();
|
|
}
|
|
}
|
|
|
|
private async _turnOn() {
|
|
this._isOn = true;
|
|
try {
|
|
await this._callService(true);
|
|
} catch (err) {
|
|
this._isOn = false;
|
|
}
|
|
}
|
|
|
|
private async _turnOff() {
|
|
this._isOn = false;
|
|
try {
|
|
await this._callService(false);
|
|
} catch (err) {
|
|
this._isOn = true;
|
|
}
|
|
}
|
|
|
|
private async _callService(turnOn: boolean): Promise<void> {
|
|
if (!this.hass || !this.stateObj) {
|
|
return;
|
|
}
|
|
forwardHaptic("light");
|
|
callProtectedLockService(
|
|
this,
|
|
this.hass,
|
|
this.stateObj,
|
|
turnOn ? "lock" : "unlock"
|
|
);
|
|
}
|
|
|
|
protected render(): TemplateResult {
|
|
const locking = this.stateObj.state === "locking";
|
|
const unlocking = this.stateObj.state === "unlocking";
|
|
|
|
const color = stateColorCss(this.stateObj);
|
|
|
|
if (this.stateObj.state === UNKNOWN) {
|
|
return html`
|
|
<div class="buttons">
|
|
<ha-control-button
|
|
.label=${this.hass.localize("ui.card.lock.lock")}
|
|
@click=${this._turnOn}
|
|
>
|
|
<ha-state-icon
|
|
.hass=${this.hass}
|
|
.stateObj=${this.stateObj}
|
|
.stateValue=${locking ? "locking" : "locked"}
|
|
></ha-state-icon>
|
|
</ha-control-button>
|
|
<ha-control-button
|
|
.label=${this.hass.localize("ui.card.lock.unlock")}
|
|
@click=${this._turnOff}
|
|
>
|
|
<ha-state-icon
|
|
.hass=${this.hass}
|
|
.stateObj=${this.stateObj}
|
|
.stateValue=${unlocking ? "unlocking" : "unlocked"}
|
|
></ha-state-icon>
|
|
</ha-control-button>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
return html`
|
|
<ha-control-switch
|
|
vertical
|
|
reversed
|
|
.checked=${this._isOn}
|
|
@change=${this._valueChanged}
|
|
.ariaLabel=${this._isOn
|
|
? this.hass.localize("ui.card.lock.unlock")
|
|
: this.hass.localize("ui.card.lock.lock")}
|
|
style=${styleMap({
|
|
"--control-switch-on-color": color,
|
|
"--control-switch-off-color": color,
|
|
})}
|
|
.disabled=${this.stateObj.state === UNAVAILABLE}
|
|
>
|
|
<ha-state-icon
|
|
slot="icon-on"
|
|
.hass=${this.hass}
|
|
.stateObj=${this.stateObj}
|
|
.stateValue=${locking ? "locking" : "locked"}
|
|
class=${classMap({ pulse: locking })}
|
|
></ha-state-icon>
|
|
<ha-state-icon
|
|
slot="icon-off"
|
|
.hass=${this.hass}
|
|
.stateObj=${this.stateObj}
|
|
.stateValue=${unlocking ? "unlocking" : "unlocked"}
|
|
class=${classMap({ pulse: unlocking })}
|
|
></ha-state-icon>
|
|
</ha-control-switch>
|
|
`;
|
|
}
|
|
|
|
static get styles(): CSSResultGroup {
|
|
return css`
|
|
@keyframes pulse {
|
|
0% {
|
|
opacity: 1;
|
|
}
|
|
50% {
|
|
opacity: 0;
|
|
}
|
|
100% {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
ha-control-switch {
|
|
height: 45vh;
|
|
max-height: 320px;
|
|
min-height: 200px;
|
|
--control-switch-thickness: 130px;
|
|
--control-switch-border-radius: 48px;
|
|
--control-switch-padding: 6px;
|
|
--mdc-icon-size: 24px;
|
|
}
|
|
.pulse {
|
|
animation: pulse 1s infinite;
|
|
}
|
|
.buttons {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 130px;
|
|
height: 45vh;
|
|
max-height: 320px;
|
|
min-height: 200px;
|
|
padding: 6px;
|
|
box-sizing: border-box;
|
|
}
|
|
ha-control-button {
|
|
flex: 1;
|
|
width: 100%;
|
|
--control-button-border-radius: 48px;
|
|
--mdc-icon-size: 24px;
|
|
}
|
|
ha-control-button.active {
|
|
--control-button-icon-color: white;
|
|
--control-button-background-color: var(--color);
|
|
--control-button-background-opacity: 1;
|
|
}
|
|
ha-control-button:not(:last-child) {
|
|
margin-bottom: 6px;
|
|
}
|
|
`;
|
|
}
|
|
}
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
"ha-state-control-lock-toggle": HaStateControlLockToggle;
|
|
}
|
|
}
|