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