2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.mqtt.homeassistant.internal.component;
15 import org.eclipse.jdt.annotation.NonNullByDefault;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
18 import org.openhab.binding.mqtt.generic.values.OnOffValue;
19 import org.openhab.binding.mqtt.generic.values.TextValue;
20 import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
21 import org.openhab.core.library.types.OnOffType;
22 import org.openhab.core.library.types.StringType;
23 import org.openhab.core.thing.ChannelUID;
24 import org.openhab.core.thing.type.AutoUpdatePolicy;
26 import com.google.gson.annotations.SerializedName;
29 * A MQTT lock, following the https://www.home-assistant.io/integrations/lock.mqtt specification.
31 * @author David Graeff - Initial contribution
32 * @author Cody Cutrer - Support OPEN, full state, and optimistic mode.
35 public class Lock extends AbstractComponent<Lock.ChannelConfiguration> {
36 public static final String LOCK_CHANNEL_ID = "lock";
37 public static final String STATE_CHANNEL_ID = "state";
40 * Configuration class for MQTT component
42 static class ChannelConfiguration extends AbstractChannelConfiguration {
43 ChannelConfiguration() {
47 protected boolean optimistic = false;
49 @SerializedName("command_topic")
50 protected @Nullable String commandTopic;
51 @SerializedName("state_topic")
52 protected String stateTopic = "";
53 @SerializedName("payload_lock")
54 protected String payloadLock = "LOCK";
55 @SerializedName("payload_unlock")
56 protected String payloadUnlock = "UNLOCK";
57 @SerializedName("payload_open")
58 protected @Nullable String payloadOpen;
59 @SerializedName("state_jammed")
60 protected String stateJammed = "JAMMED";
61 @SerializedName("state_locked")
62 protected String stateLocked = "LOCKED";
63 @SerializedName("state_locking")
64 protected String stateLocking = "LOCKING";
65 @SerializedName("state_unlocked")
66 protected String stateUnlocked = "UNLOCKED";
67 @SerializedName("state_unlocking")
68 protected String stateUnlocking = "UNLOCKING";
71 private boolean optimistic = false;
72 private OnOffValue lockValue;
73 private TextValue stateValue;
75 public Lock(ComponentFactory.ComponentConfiguration componentConfiguration) {
76 super(componentConfiguration, ChannelConfiguration.class);
78 this.optimistic = channelConfiguration.optimistic || channelConfiguration.stateTopic.isBlank();
80 lockValue = new OnOffValue(new String[] { channelConfiguration.stateLocked },
81 new String[] { channelConfiguration.stateUnlocked, channelConfiguration.stateLocking,
82 channelConfiguration.stateUnlocking, channelConfiguration.stateJammed },
83 channelConfiguration.payloadLock, channelConfiguration.payloadUnlock);
85 buildChannel(LOCK_CHANNEL_ID, lockValue, "Lock", componentConfiguration.getUpdateListener())
86 .stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
87 .commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
88 channelConfiguration.getQos())
89 .withAutoUpdatePolicy(AutoUpdatePolicy.VETO).commandFilter(command -> {
90 if (command instanceof OnOffType) {
91 autoUpdate(command.equals(OnOffType.ON));
97 if (channelConfiguration.payloadOpen == null) {
98 commands = new String[] { channelConfiguration.payloadLock, channelConfiguration.payloadUnlock, };
100 commands = new String[] { channelConfiguration.payloadLock, channelConfiguration.payloadUnlock,
101 channelConfiguration.payloadOpen };
103 stateValue = new TextValue(new String[] { channelConfiguration.stateJammed, channelConfiguration.stateLocked,
104 channelConfiguration.stateLocking, channelConfiguration.stateUnlocked,
105 channelConfiguration.stateUnlocking }, commands);
106 buildChannel(STATE_CHANNEL_ID, stateValue, "State", componentConfiguration.getUpdateListener())
107 .stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
108 .commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
109 channelConfiguration.getQos())
110 .isAdvanced(true).withAutoUpdatePolicy(AutoUpdatePolicy.VETO).commandFilter(command -> {
111 if (command instanceof StringType stringCommand) {
112 if (stringCommand.toString().equals(channelConfiguration.payloadLock)) {
114 } else if (stringCommand.toString().equals(channelConfiguration.payloadUnlock)
115 || stringCommand.toString().equals(channelConfiguration.payloadOpen)) {
123 private void autoUpdate(boolean locking) {
128 final ChannelUID lockChannelUID = buildChannelUID(LOCK_CHANNEL_ID);
129 final ChannelUID stateChannelUID = buildChannelUID(STATE_CHANNEL_ID);
130 final ChannelStateUpdateListener updateListener = componentConfiguration.getUpdateListener();
133 stateValue.update(new StringType(channelConfiguration.stateLocked));
134 updateListener.updateChannelState(stateChannelUID, stateValue.getChannelState());
135 lockValue.update(OnOffType.ON);
136 updateListener.updateChannelState(lockChannelUID, OnOffType.ON);
138 stateValue.update(new StringType(channelConfiguration.stateUnlocked));
139 updateListener.updateChannelState(stateChannelUID, stateValue.getChannelState());
140 lockValue.update(OnOffType.OFF);
141 updateListener.updateChannelState(lockChannelUID, OnOffType.OFF);