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.ComponentChannelType;
21 import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
22 import org.openhab.core.library.types.OnOffType;
23 import org.openhab.core.library.types.StringType;
24 import org.openhab.core.thing.ChannelUID;
25 import org.openhab.core.thing.type.AutoUpdatePolicy;
27 import com.google.gson.annotations.SerializedName;
30 * A MQTT lock, following the https://www.home-assistant.io/integrations/lock.mqtt specification.
32 * @author David Graeff - Initial contribution
33 * @author Cody Cutrer - Support OPEN, full state, and optimistic mode.
36 public class Lock extends AbstractComponent<Lock.ChannelConfiguration> {
37 public static final String LOCK_CHANNEL_ID = "lock";
38 public static final String STATE_CHANNEL_ID = "state";
41 * Configuration class for MQTT component
43 static class ChannelConfiguration extends AbstractChannelConfiguration {
44 ChannelConfiguration() {
48 protected boolean optimistic = false;
50 @SerializedName("command_topic")
51 protected @Nullable String commandTopic;
52 @SerializedName("state_topic")
53 protected String stateTopic = "";
54 @SerializedName("payload_lock")
55 protected String payloadLock = "LOCK";
56 @SerializedName("payload_unlock")
57 protected String payloadUnlock = "UNLOCK";
58 @SerializedName("payload_open")
59 protected @Nullable String payloadOpen;
60 @SerializedName("state_jammed")
61 protected String stateJammed = "JAMMED";
62 @SerializedName("state_locked")
63 protected String stateLocked = "LOCKED";
64 @SerializedName("state_locking")
65 protected String stateLocking = "LOCKING";
66 @SerializedName("state_unlocked")
67 protected String stateUnlocked = "UNLOCKED";
68 @SerializedName("state_unlocking")
69 protected String stateUnlocking = "UNLOCKING";
72 private boolean optimistic = false;
73 private OnOffValue lockValue;
74 private TextValue stateValue;
76 public Lock(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
77 super(componentConfiguration, ChannelConfiguration.class, newStyleChannels);
79 this.optimistic = channelConfiguration.optimistic || channelConfiguration.stateTopic.isBlank();
81 lockValue = new OnOffValue(new String[] { channelConfiguration.stateLocked },
82 new String[] { channelConfiguration.stateUnlocked, channelConfiguration.stateLocking,
83 channelConfiguration.stateUnlocking, channelConfiguration.stateJammed },
84 channelConfiguration.payloadLock, channelConfiguration.payloadUnlock);
86 buildChannel(LOCK_CHANNEL_ID, ComponentChannelType.SWITCH, lockValue, "Lock",
87 componentConfiguration.getUpdateListener())
88 .stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
89 .commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
90 channelConfiguration.getQos())
91 .withAutoUpdatePolicy(AutoUpdatePolicy.VETO).commandFilter(command -> {
92 if (command instanceof OnOffType) {
93 autoUpdate(command.equals(OnOffType.ON));
99 if (channelConfiguration.payloadOpen == null) {
100 commands = new String[] { channelConfiguration.payloadLock, channelConfiguration.payloadUnlock, };
102 commands = new String[] { channelConfiguration.payloadLock, channelConfiguration.payloadUnlock,
103 channelConfiguration.payloadOpen };
105 stateValue = new TextValue(new String[] { channelConfiguration.stateJammed, channelConfiguration.stateLocked,
106 channelConfiguration.stateLocking, channelConfiguration.stateUnlocked,
107 channelConfiguration.stateUnlocking }, commands);
108 buildChannel(STATE_CHANNEL_ID, ComponentChannelType.STRING, stateValue, "State",
109 componentConfiguration.getUpdateListener())
110 .stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
111 .commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
112 channelConfiguration.getQos())
113 .isAdvanced(true).withAutoUpdatePolicy(AutoUpdatePolicy.VETO).commandFilter(command -> {
114 if (command instanceof StringType stringCommand) {
115 if (stringCommand.toString().equals(channelConfiguration.payloadLock)) {
117 } else if (stringCommand.toString().equals(channelConfiguration.payloadUnlock)
118 || stringCommand.toString().equals(channelConfiguration.payloadOpen)) {
127 private void autoUpdate(boolean locking) {
132 final ChannelUID lockChannelUID = buildChannelUID(LOCK_CHANNEL_ID);
133 final ChannelUID stateChannelUID = buildChannelUID(STATE_CHANNEL_ID);
134 final ChannelStateUpdateListener updateListener = componentConfiguration.getUpdateListener();
137 stateValue.update(new StringType(channelConfiguration.stateLocked));
138 updateListener.updateChannelState(stateChannelUID, stateValue.getChannelState());
139 lockValue.update(OnOffType.ON);
140 updateListener.updateChannelState(lockChannelUID, OnOffType.ON);
142 stateValue.update(new StringType(channelConfiguration.stateUnlocked));
143 updateListener.updateChannelState(stateChannelUID, stateValue.getChannelState());
144 lockValue.update(OnOffType.OFF);
145 updateListener.updateChannelState(lockChannelUID, OnOffType.OFF);