]> git.basschouten.com Git - openhab-addons.git/blob
0e2c9538e41a90caf48efb18a1afda7c3113a702
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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     @SuppressWarnings("PMD.CompareObjectsWithEquals")
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     @SuppressWarnings("PMD.CompareObjectsWithEquals")
167     protected Collection<SatelCommand> getRefreshCommands(NewStatesEvent event) {
168         final boolean hasExtPayload = getBridgeHandler().getIntegraType().hasExtPayload();
169         final Collection<SatelCommand> result = new LinkedList<>();
170         final boolean forceRefresh = requiresRefresh();
171         for (Channel channel : getThing().getChannels()) {
172             StateType stateType = getStateType(channel.getUID().getId());
173             if (stateType != StateType.NONE && isLinked(channel.getUID())) {
174                 if (forceRefresh || event.isNew(stateType.getRefreshCommand())) {
175                     result.add(new IntegraStateCommand(stateType, hasExtPayload));
176                 }
177             }
178         }
179         return result;
180     }
181
182     /**
183      * Checks if this thing requires unconditional refresh of all its channels.
184      * Clears the flag afterwards.
185      *
186      * @return if <code>true</code> this thing requires full refresh
187      */
188     protected boolean requiresRefresh() {
189         return requiresRefresh.getAndSet(false);
190     }
191 }