]> git.basschouten.com Git - openhab-addons.git/blob
d14f07263c88b5c0f9cfdc960a460ce0e6c06837
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.satel.internal.handler;
14
15 import java.util.Collection;
16 import java.util.LinkedList;
17 import java.util.Optional;
18 import java.util.concurrent.atomic.AtomicBoolean;
19
20 import org.apache.commons.lang.StringUtils;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.satel.internal.command.IntegraStateCommand;
24 import org.openhab.binding.satel.internal.command.SatelCommand;
25 import org.openhab.binding.satel.internal.event.ConnectionStatusEvent;
26 import org.openhab.binding.satel.internal.event.IntegraStateEvent;
27 import org.openhab.binding.satel.internal.event.NewStatesEvent;
28 import org.openhab.binding.satel.internal.types.StateType;
29 import org.openhab.core.thing.Channel;
30 import org.openhab.core.thing.ChannelUID;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.thing.ThingStatus;
33 import org.openhab.core.types.Command;
34 import org.openhab.core.types.RefreshType;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 /**
39  * The {@link SatelStateThingHandler} is base thing handler class for all state holding things.
40  *
41  * @author Krzysztof Goworek - Initial contribution
42  */
43 @NonNullByDefault
44 public abstract class SatelStateThingHandler extends SatelThingHandler {
45
46     private final Logger logger = LoggerFactory.getLogger(SatelStateThingHandler.class);
47
48     private final AtomicBoolean requiresRefresh = new AtomicBoolean(true);
49
50     public SatelStateThingHandler(Thing thing) {
51         super(thing);
52     }
53
54     @Override
55     public void handleCommand(ChannelUID channelUID, Command command) {
56         logger.debug("New command for {}: {}", channelUID, command);
57
58         if (command == RefreshType.REFRESH) {
59             this.requiresRefresh.set(true);
60         } else {
61             withBridgeHandlerPresent(bridgeHandler -> {
62                 if (StringUtils.isEmpty(bridgeHandler.getUserCode())) {
63                     logger.info("Cannot control devices without providing valid user code. Command has not been sent.");
64                 } else {
65                     convertCommand(channelUID, command)
66                             .ifPresent(satelCommand -> bridgeHandler.sendCommand(satelCommand, true));
67                 }
68             });
69         }
70     }
71
72     @Override
73     public void incomingEvent(ConnectionStatusEvent event) {
74         logger.trace("Handling incoming event: {}", event);
75         // we have just connected, change thing's status and force refreshing
76         if (event.isConnected()) {
77             updateStatus(ThingStatus.ONLINE);
78             requiresRefresh.set(true);
79         }
80     }
81
82     @Override
83     public void incomingEvent(NewStatesEvent event) {
84         logger.trace("Handling incoming event: {}", event);
85         // refresh all states that have changed
86         withBridgeHandlerPresent(bridgeHandler -> {
87             for (SatelCommand command : getRefreshCommands(event)) {
88                 bridgeHandler.sendCommand(command, true);
89             }
90         });
91     }
92
93     @Override
94     public void incomingEvent(IntegraStateEvent event) {
95         logger.trace("Handling incoming event: {}", event);
96         // update thing's state unless it should accept commands only
97         if (getThingConfig().isCommandOnly()) {
98             return;
99         }
100         for (Channel channel : getThing().getChannels()) {
101             ChannelUID channelUID = channel.getUID();
102             if (isLinked(channel.getUID())) {
103                 StateType stateType = getStateType(channelUID.getId());
104                 if (stateType != StateType.NONE && event.hasDataForState(stateType)) {
105                     int bitNbr = getStateBitNbr(stateType);
106                     boolean invertState = getThingConfig().isStateInverted();
107                     updateSwitch(channelUID, event.isSet(stateType, bitNbr) ^ invertState);
108                 }
109             }
110         }
111     }
112
113     /**
114      * Returns bit number of given state type for this thing.
115      * This number addresses the bit in bit set sent to or received from alarm system.
116      * Usually this number is the device identifier.
117      *
118      * @param stateType state type
119      * @return bit number in state bit set
120      */
121     protected int getStateBitNbr(StateType stateType) {
122         return getThingConfig().getId() - 1;
123     }
124
125     /**
126      * Converts openHAB command sent to a channel into Satel message.
127      *
128      * @param channel channel the command was sent to
129      * @param command sent command
130      * @return Satel message that reflects sent command
131      */
132     protected abstract Optional<SatelCommand> convertCommand(ChannelUID channel, Command command);
133
134     /**
135      * Derived handlers must return appropriate state type for channels they support.
136      * If given channel is not supported by a handler, it should return {@linkplain StateType#NONE}.
137      *
138      * @param channelId channel identifier to get state type for
139      * @return object that represents state type
140      * @see #getChannel(StateType)
141      */
142     protected abstract StateType getStateType(String channelId);
143
144     /**
145      * Returns channel for given state type. Usually channels have the same ID as state type name they represent.
146      *
147      * @param stateType state type to get channel for
148      * @return channel object
149      * @see #getStateType(String)
150      */
151     protected @Nullable Channel getChannel(StateType stateType) {
152         final String channelId = stateType.toString().toLowerCase();
153         final Channel channel = getThing().getChannel(channelId);
154         if (channel == null) {
155             logger.debug("Missing channel for {}", stateType);
156         }
157         return channel;
158     }
159
160     /**
161      * Returns list of commands required to update thing state basing on event describing changes since last refresh.
162      *
163      * @param event list of state changes since last refresh
164      * @return collection of {@link IntegraStateCommand}
165      */
166     protected Collection<SatelCommand> getRefreshCommands(NewStatesEvent event) {
167         final boolean hasExtPayload = getBridgeHandler().getIntegraType().hasExtPayload();
168         final Collection<SatelCommand> result = new LinkedList<>();
169         final boolean forceRefresh = requiresRefresh();
170         for (Channel channel : getThing().getChannels()) {
171             StateType stateType = getStateType(channel.getUID().getId());
172             if (stateType != StateType.NONE && isLinked(channel.getUID())) {
173                 if (forceRefresh || event.isNew(stateType.getRefreshCommand())) {
174                     result.add(new IntegraStateCommand(stateType, hasExtPayload));
175                 }
176             }
177         }
178         return result;
179     }
180
181     /**
182      * Checks if this thing requires unconditional refresh of all its channels.
183      * Clears the flag afterwards.
184      *
185      * @return if <code>true</code> this thing requires full refresh
186      */
187     protected boolean requiresRefresh() {
188         return requiresRefresh.getAndSet(false);
189     }
190 }