]> git.basschouten.com Git - openhab-addons.git/blob
eab24ceffc7d6397bab73884b76ef9942cbb47f9
[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.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;
25
26 import com.google.gson.annotations.SerializedName;
27
28 /**
29  * A MQTT lock, following the https://www.home-assistant.io/integrations/lock.mqtt specification.
30  *
31  * @author David Graeff - Initial contribution
32  * @author Cody Cutrer - Support OPEN, full state, and optimistic mode.
33  */
34 @NonNullByDefault
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";
38
39     /**
40      * Configuration class for MQTT component
41      */
42     static class ChannelConfiguration extends AbstractChannelConfiguration {
43         ChannelConfiguration() {
44             super("MQTT Lock");
45         }
46
47         protected boolean optimistic = false;
48
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";
69     }
70
71     private boolean optimistic = false;
72     private OnOffValue lockValue;
73     private TextValue stateValue;
74
75     public Lock(ComponentFactory.ComponentConfiguration componentConfiguration) {
76         super(componentConfiguration, ChannelConfiguration.class);
77
78         this.optimistic = channelConfiguration.optimistic || channelConfiguration.stateTopic.isBlank();
79
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);
84
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));
92                     }
93                     return true;
94                 }).build();
95
96         String[] commands;
97         if (channelConfiguration.payloadOpen == null) {
98             commands = new String[] { channelConfiguration.payloadLock, channelConfiguration.payloadUnlock, };
99         } else {
100             commands = new String[] { channelConfiguration.payloadLock, channelConfiguration.payloadUnlock,
101                     channelConfiguration.payloadOpen };
102         }
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)) {
113                             autoUpdate(true);
114                         } else if (stringCommand.toString().equals(channelConfiguration.payloadUnlock)
115                                 || stringCommand.toString().equals(channelConfiguration.payloadOpen)) {
116                             autoUpdate(false);
117                         }
118                     }
119                     return true;
120                 }).build();
121     }
122
123     private void autoUpdate(boolean locking) {
124         if (!optimistic) {
125             return;
126         }
127
128         final ChannelUID lockChannelUID = buildChannelUID(LOCK_CHANNEL_ID);
129         final ChannelUID stateChannelUID = buildChannelUID(STATE_CHANNEL_ID);
130         final ChannelStateUpdateListener updateListener = componentConfiguration.getUpdateListener();
131
132         if (locking) {
133             stateValue.update(new StringType(channelConfiguration.stateLocked));
134             updateListener.updateChannelState(stateChannelUID, stateValue.getChannelState());
135             lockValue.update(OnOffType.ON);
136             updateListener.updateChannelState(lockChannelUID, OnOffType.ON);
137         } else {
138             stateValue.update(new StringType(channelConfiguration.stateUnlocked));
139             updateListener.updateChannelState(stateChannelUID, stateValue.getChannelState());
140             lockValue.update(OnOffType.OFF);
141             updateListener.updateChannelState(lockChannelUID, OnOffType.OFF);
142         }
143     }
144 }