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