]> git.basschouten.com Git - openhab-addons.git/blob
b4fed2ccb4ad76504bdac77f3f21b1c7db2e8b28
[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.enocean.internal.handler;
14
15 import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
16
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.Comparator;
20 import java.util.Hashtable;
21 import java.util.Set;
22 import java.util.concurrent.ScheduledFuture;
23 import java.util.concurrent.TimeUnit;
24 import java.util.function.Predicate;
25
26 import org.apache.commons.lang3.NotImplementedException;
27 import org.openhab.binding.enocean.internal.config.EnOceanBaseConfig;
28 import org.openhab.binding.enocean.internal.eep.EEP;
29 import org.openhab.binding.enocean.internal.eep.EEPFactory;
30 import org.openhab.binding.enocean.internal.eep.EEPType;
31 import org.openhab.binding.enocean.internal.messages.BasePacket;
32 import org.openhab.binding.enocean.internal.messages.ERP1Message;
33 import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG;
34 import org.openhab.binding.enocean.internal.transceiver.PacketListener;
35 import org.openhab.core.config.core.Configuration;
36 import org.openhab.core.thing.Channel;
37 import org.openhab.core.thing.ChannelUID;
38 import org.openhab.core.thing.Thing;
39 import org.openhab.core.thing.ThingTypeUID;
40 import org.openhab.core.thing.link.ItemChannelLinkRegistry;
41 import org.openhab.core.thing.type.ChannelKind;
42 import org.openhab.core.thing.type.ChannelTypeUID;
43 import org.openhab.core.types.Command;
44 import org.openhab.core.types.State;
45 import org.openhab.core.types.UnDefType;
46 import org.openhab.core.util.HexUtils;
47
48 /**
49  * @author Daniel Weber - Initial contribution
50  *         This class defines base functionality for receiving eep messages.
51  */
52 public class EnOceanBaseSensorHandler extends EnOceanBaseThingHandler implements PacketListener {
53
54     // List of all thing types which support receiving of eep messages
55     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_ROOMOPERATINGPANEL,
56             THING_TYPE_MECHANICALHANDLE, THING_TYPE_CONTACT, THING_TYPE_TEMPERATURESENSOR,
57             THING_TYPE_TEMPERATUREHUMIDITYSENSOR, THING_TYPE_ROCKERSWITCH, THING_TYPE_OCCUPANCYSENSOR,
58             THING_TYPE_LIGHTTEMPERATUREOCCUPANCYSENSOR, THING_TYPE_LIGHTSENSOR, THING_TYPE_PUSHBUTTON,
59             THING_TYPE_AUTOMATEDMETERSENSOR, THING_TYPE_ENVIRONMENTALSENSOR, THING_TYPE_MULTFUNCTIONSMOKEDETECTOR);
60
61     protected final Hashtable<RORG, EEPType> receivingEEPTypes = new Hashtable<>();
62
63     protected ScheduledFuture<?> responseFuture = null;
64
65     public EnOceanBaseSensorHandler(Thing thing, ItemChannelLinkRegistry itemChannelLinkRegistry) {
66         super(thing, itemChannelLinkRegistry);
67     }
68
69     @Override
70     void initializeConfig() {
71         config = getConfigAs(EnOceanBaseConfig.class);
72     }
73
74     @Override
75     Collection<EEPType> getEEPTypes() {
76         return Collections.unmodifiableCollection(receivingEEPTypes.values());
77     }
78
79     @Override
80     boolean validateConfig() {
81         receivingEEPTypes.clear();
82         try {
83             config.receivingEEPId.forEach(receivingEEP -> {
84                 EEPType receivingEEPType = EEPType.getType(receivingEEP);
85                 if (receivingEEPTypes.putIfAbsent(receivingEEPType.getRORG(), receivingEEPType) != null) {
86                     throw new IllegalArgumentException("Receiving more than one EEP of the same RORG is not supported");
87                 }
88             });
89             if (config.receivingSIGEEP) {
90                 receivingEEPTypes.put(EEPType.SigBatteryStatus.getRORG(), EEPType.SigBatteryStatus);
91             }
92         } catch (IllegalArgumentException e) {
93             configurationErrorDescription = e.getMessage();
94             return false;
95         }
96
97         updateChannels();
98
99         if (!validateEnoceanId(config.enoceanId)) {
100             configurationErrorDescription = "EnOceanId is not a valid EnOceanId";
101             return false;
102         }
103
104         if (!config.enoceanId.equals(EMPTYENOCEANID)) {
105             getBridgeHandler().addPacketListener(this);
106         }
107
108         return true;
109     }
110
111     @Override
112     public long getEnOceanIdToListenTo() {
113         return Long.parseLong(config.enoceanId, 16);
114     }
115
116     @Override
117     public void handleRemoval() {
118         if (getBridgeHandler() != null) {
119             getBridgeHandler().removePacketListener(this);
120         }
121         super.handleRemoval();
122     }
123
124     @Override
125     public void handleCommand(ChannelUID channelUID, Command command) {
126         // sensor things cannot send any messages, hence they are not allowed to handle any command
127         // The only possible command would be "Refresh"
128     }
129
130     protected Predicate<Channel> channelFilter(EEPType eepType, byte[] senderId) {
131         return c -> {
132             boolean result = eepType.GetSupportedChannels().containsKey(c.getUID().getId());
133             return (isLinked(c.getUID()) || c.getKind() == ChannelKind.TRIGGER) && result;
134         };
135     }
136
137     protected void sendRequestResponse() {
138         throw new NotImplementedException("Sensor cannot send responses");
139     }
140
141     @Override
142     public void packetReceived(BasePacket packet) {
143         ERP1Message msg = (ERP1Message) packet;
144         EEPType receivingEEPType = receivingEEPTypes.get(msg.getRORG());
145         if (receivingEEPType == null) {
146             return;
147         }
148
149         EEP eep = EEPFactory.buildEEP(receivingEEPType, (ERP1Message) packet);
150         logger.debug("ESP Packet payload {} for {} received", HexUtils.bytesToHex(packet.getPayload()),
151                 HexUtils.bytesToHex(msg.getSenderId()));
152
153         if (eep.isValid()) {
154             byte[] senderId = msg.getSenderId();
155
156             // try to interpret received message for all linked or trigger channels
157             getThing().getChannels().stream().filter(channelFilter(receivingEEPType, senderId))
158                     .sorted(Comparator.comparing(Channel::getKind)) // handle state channels first
159                     .forEachOrdered(channel -> {
160
161                         ChannelTypeUID channelTypeUID = channel.getChannelTypeUID();
162                         String channelTypeId = (channelTypeUID != null) ? channelTypeUID.getId() : "";
163
164                         String channelId = channel.getUID().getId();
165                         Configuration channelConfig = channel.getConfiguration();
166
167                         switch (channel.getKind()) {
168                             case STATE:
169                                 State result = eep.convertToState(channelId, channelTypeId, channelConfig,
170                                         this::getCurrentState);
171
172                                 // if message can be interpreted (result != UnDefType.UNDEF) => update item state
173                                 if (result != null && result != UnDefType.UNDEF) {
174                                     updateState(channelId, result);
175                                 }
176                                 break;
177                             case TRIGGER:
178                                 String lastEvent = lastEvents.get(channelId);
179                                 String event = eep.convertToEvent(channelId, channelTypeId, lastEvent, channelConfig);
180                                 if (event != null) {
181                                     triggerChannel(channel.getUID(), event);
182                                     lastEvents.put(channelId, event);
183                                 }
184                                 break;
185                         }
186                     });
187
188             if (receivingEEPType.getRequstesResponse()) {
189                 // fire trigger for receive
190                 triggerChannel(prepareAnswer, "requestAnswer");
191                 // Send response after 100ms
192                 if (responseFuture == null || responseFuture.isDone()) {
193                     responseFuture = scheduler.schedule(this::sendRequestResponse, 100, TimeUnit.MILLISECONDS);
194                 }
195             }
196         }
197     }
198 }