]> git.basschouten.com Git - openhab-addons.git/blob
bce7cef22889b565e14547c4eed66cc271e6859b
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.mqtt.homeassistant.internal.component;
14
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;
26
27 import com.google.gson.annotations.SerializedName;
28
29 /**
30  * A MQTT lock, following the https://www.home-assistant.io/integrations/lock.mqtt specification.
31  *
32  * @author David Graeff - Initial contribution
33  * @author Cody Cutrer - Support OPEN, full state, and optimistic mode.
34  */
35 @NonNullByDefault
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";
39
40     /**
41      * Configuration class for MQTT component
42      */
43     static class ChannelConfiguration extends AbstractChannelConfiguration {
44         ChannelConfiguration() {
45             super("MQTT Lock");
46         }
47
48         protected boolean optimistic = false;
49
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";
70     }
71
72     private boolean optimistic = false;
73     private OnOffValue lockValue;
74     private TextValue stateValue;
75
76     public Lock(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
77         super(componentConfiguration, ChannelConfiguration.class, newStyleChannels);
78
79         this.optimistic = channelConfiguration.optimistic || channelConfiguration.stateTopic.isBlank();
80
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);
85
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));
94                     }
95                     return true;
96                 }).build();
97
98         String[] commands;
99         if (channelConfiguration.payloadOpen == null) {
100             commands = new String[] { channelConfiguration.payloadLock, channelConfiguration.payloadUnlock, };
101         } else {
102             commands = new String[] { channelConfiguration.payloadLock, channelConfiguration.payloadUnlock,
103                     channelConfiguration.payloadOpen };
104         }
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)) {
116                             autoUpdate(true);
117                         } else if (stringCommand.toString().equals(channelConfiguration.payloadUnlock)
118                                 || stringCommand.toString().equals(channelConfiguration.payloadOpen)) {
119                             autoUpdate(false);
120                         }
121                     }
122                     return true;
123                 }).build();
124         finalizeChannels();
125     }
126
127     private void autoUpdate(boolean locking) {
128         if (!optimistic) {
129             return;
130         }
131
132         final ChannelUID lockChannelUID = buildChannelUID(LOCK_CHANNEL_ID);
133         final ChannelUID stateChannelUID = buildChannelUID(STATE_CHANNEL_ID);
134         final ChannelStateUpdateListener updateListener = componentConfiguration.getUpdateListener();
135
136         if (locking) {
137             stateValue.update(new StringType(channelConfiguration.stateLocked));
138             updateListener.updateChannelState(stateChannelUID, stateValue.getChannelState());
139             lockValue.update(OnOffType.ON);
140             updateListener.updateChannelState(lockChannelUID, OnOffType.ON);
141         } else {
142             stateValue.update(new StringType(channelConfiguration.stateUnlocked));
143             updateListener.updateChannelState(stateChannelUID, stateValue.getChannelState());
144             lockValue.update(OnOffType.OFF);
145             updateListener.updateChannelState(lockChannelUID, OnOffType.OFF);
146         }
147     }
148 }