]> git.basschouten.com Git - openhab-addons.git/blob
4f871fb4a39ab0b342834ae673cba0964cacd1bf
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.enocean.internal.handler;
14
15 import java.util.AbstractMap.SimpleEntry;
16 import java.util.Collection;
17 import java.util.Hashtable;
18 import java.util.LinkedList;
19 import java.util.List;
20 import java.util.Set;
21 import java.util.concurrent.atomic.AtomicBoolean;
22
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.openhab.binding.enocean.internal.EnOceanChannelDescription;
25 import org.openhab.binding.enocean.internal.config.EnOceanBaseConfig;
26 import org.openhab.binding.enocean.internal.eep.EEPType;
27 import org.openhab.core.config.core.status.ConfigStatusMessage;
28 import org.openhab.core.items.Item;
29 import org.openhab.core.thing.Bridge;
30 import org.openhab.core.thing.Channel;
31 import org.openhab.core.thing.ChannelUID;
32 import org.openhab.core.thing.Thing;
33 import org.openhab.core.thing.ThingStatus;
34 import org.openhab.core.thing.ThingStatusDetail;
35 import org.openhab.core.thing.ThingStatusInfo;
36 import org.openhab.core.thing.binding.ConfigStatusThingHandler;
37 import org.openhab.core.thing.binding.ThingHandler;
38 import org.openhab.core.thing.binding.builder.ChannelBuilder;
39 import org.openhab.core.thing.binding.builder.ThingBuilder;
40 import org.openhab.core.thing.link.ItemChannelLinkRegistry;
41 import org.openhab.core.thing.type.ChannelKind;
42 import org.openhab.core.types.State;
43 import org.openhab.core.types.UnDefType;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 /**
48  *
49  * @author Daniel Weber - Initial contribution
50  */
51 public abstract class EnOceanBaseThingHandler extends ConfigStatusThingHandler {
52
53     private EnOceanBridgeHandler gateway = null;
54     protected Logger logger = LoggerFactory.getLogger(EnOceanBaseThingHandler.class);
55
56     protected String configurationErrorDescription;
57
58     // There is no data structure which holds the last triggered event, so we have to implement it by ourself
59     // This is especially needed for press and release events
60     protected Hashtable<String, String> lastEvents = new Hashtable<>();
61
62     protected EnOceanBaseConfig config = null;
63
64     private ItemChannelLinkRegistry itemChannelLinkRegistry;
65
66     public EnOceanBaseThingHandler(Thing thing, ItemChannelLinkRegistry itemChannelLinkRegistry) {
67         super(thing);
68         this.itemChannelLinkRegistry = itemChannelLinkRegistry;
69     }
70
71     @SuppressWarnings("null")
72     @Override
73     public void initialize() {
74         logger.debug("Initializing enocean base thing handler.");
75         this.gateway = null; // reset gateway in case we change the bridge
76         this.config = null;
77         initializeThing((getBridge() == null) ? null : getBridge().getStatus());
78     }
79
80     private void initializeThing(ThingStatus bridgeStatus) {
81         logger.debug("initializeThing thing {} bridge status {}", getThing().getUID(), bridgeStatus);
82
83         if (this.itemChannelLinkRegistry == null) {
84             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
85                     "ItemChannelLinkRegistry could not be found");
86         } else {
87             if (getBridgeHandler() != null) {
88                 if (bridgeStatus == ThingStatus.ONLINE) {
89                     initializeConfig();
90                     if (validateConfig()) {
91                         updateStatus(ThingStatus.ONLINE);
92                     } else {
93                         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
94                                 configurationErrorDescription);
95                     }
96                 } else {
97                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
98                 }
99             } else {
100                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "A bridge is required");
101             }
102         }
103     }
104
105     protected boolean validateEnoceanId(String id) {
106         if (id == null || id.isEmpty()) {
107             return false;
108         } else {
109             if (id.length() != 8) {
110                 return false;
111             }
112
113             try {
114                 Integer.parseUnsignedInt(id, 16);
115                 return true;
116             } catch (Exception e) {
117                 return false;
118             }
119         }
120     }
121
122     abstract void initializeConfig();
123
124     abstract boolean validateConfig();
125
126     abstract Collection<EEPType> getEEPTypes();
127
128     protected void updateChannels() {
129         @NonNull
130         List<@NonNull Channel> channelList = new LinkedList<>(this.getThing().getChannels());
131         Collection<EEPType> eeps = getEEPTypes();
132         if (eeps == null) {
133             return;
134         }
135
136         // First remove channels which are no longer supported by current selected eeps of thing
137         AtomicBoolean channelListChanged = new AtomicBoolean(
138                 channelList.removeIf(channel -> !eeps.stream().anyMatch(eep -> eep.isChannelSupported(channel))));
139
140         // Next create supported channels of each selected eep
141         eeps.stream().flatMap(eep -> eep.GetSupportedChannels().keySet().stream().map(id -> new SimpleEntry<>(id, eep)))
142                 .forEach(entry -> {
143                     String channelId = entry.getKey();
144                     EnOceanChannelDescription cd = entry.getValue().GetSupportedChannels().get(channelId);
145
146                     // if we do not need to auto create channel => skip
147                     if (!cd.autoCreate) {
148                         return;
149                     }
150
151                     // if we already created a channel with the same type and id => skip
152                     if (channelList.stream().anyMatch(channel -> cd.channelTypeUID.equals(channel.getChannelTypeUID())
153                             && channelId.equals(channel.getUID().getId()))) {
154                         return;
155                     }
156
157                     // create channel and add it to the channelList
158                     Channel channel = ChannelBuilder
159                             .create(new ChannelUID(this.getThing().getUID(), channelId), cd.acceptedItemType)
160                             .withConfiguration(entry.getValue().getChannelConfig(channelId)).withType(cd.channelTypeUID)
161                             .withKind(cd.isStateChannel ? ChannelKind.STATE : ChannelKind.TRIGGER).withLabel(cd.label)
162                             .build();
163
164                     channelList.add(channel);
165                     channelListChanged.set(true);
166
167                     if (!cd.isStateChannel) {
168                         lastEvents.putIfAbsent(channelId, "");
169                     }
170                 });
171
172         if (channelListChanged.get()) {
173             ThingBuilder thingBuilder = editThing();
174             thingBuilder.withChannels(channelList);
175             updateThing(thingBuilder.build());
176         }
177     }
178
179     protected State getCurrentState(Channel channel) {
180         if (channel != null) {
181             Set<Item> items = itemChannelLinkRegistry.getLinkedItems(channel.getUID());
182             for (Item item : items) {
183                 State state = item.getState();
184                 if (state != UnDefType.NULL && state != UnDefType.UNDEF) {
185                     return state;
186                 }
187             }
188         }
189
190         return UnDefType.UNDEF;
191     }
192
193     protected State getCurrentState(String channelId) {
194         return getCurrentState(getThing().getChannel(channelId));
195     }
196
197     protected synchronized EnOceanBridgeHandler getBridgeHandler() {
198         if (this.gateway == null) {
199             Bridge bridge = getBridge();
200             if (bridge == null) {
201                 return null;
202             }
203             ThingHandler handler = bridge.getHandler();
204             if (handler instanceof EnOceanBridgeHandler) {
205                 this.gateway = (EnOceanBridgeHandler) handler;
206             } else {
207                 return null;
208             }
209         }
210         return this.gateway;
211     }
212
213     @Override
214     public Collection<ConfigStatusMessage> getConfigStatus() {
215         // TODO
216         // Collection<ConfigStatusMessage> configStatusMessages;
217
218         // The senderId must be provided
219         /*
220          * final EnOceanActuatorConfig config = getConfigAs(EnOceanActuatorConfig.class);
221          * final String senderId = config.senderIdOffset;
222          * if (senderId == null || senderId.isEmpty()) {
223          * configStatusMessages = Collections.singletonList(ConfigStatusMessage.Builder.error(SENDERID)
224          * .withMessageKeySuffix(EnOceanConfigStatusMessage.SENDERID_MISSING.getMessageKey())
225          * .withArguments(SENDERID).build());
226          * } else {
227          * try {
228          * Integer.parseUnsignedInt(senderId, 16);
229          * } catch (Exception e) {
230          * configStatusMessages = Collections.singletonList(ConfigStatusMessage.Builder.error(SENDERID)
231          * .withMessageKeySuffix(EnOceanConfigStatusMessage.SENDERID_MALFORMED.getMessageKey())
232          * .withArguments(SENDERID).build());
233          * }
234          * configStatusMessages = Collections.emptyList();
235          * }
236          *
237          * return configStatusMessages;
238          */
239
240         return new LinkedList<>();
241     }
242
243     @Override
244     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
245         if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
246             initializeThing(bridgeStatusInfo.getStatus());
247         }
248     }
249 }