]> git.basschouten.com Git - openhab-addons.git/blob
c85aa460b32cf6d30e45ef61f706f5aabd1f90fc
[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.Arrays;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.Comparator;
21 import java.util.HashSet;
22 import java.util.Hashtable;
23 import java.util.Set;
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 = new HashSet<>(
55             Arrays.asList(THING_TYPE_ROOMOPERATINGPANEL, THING_TYPE_MECHANICALHANDLE, THING_TYPE_CONTACT,
56                     THING_TYPE_TEMPERATURESENSOR, THING_TYPE_TEMPERATUREHUMIDITYSENSOR, THING_TYPE_ROCKERSWITCH,
57                     THING_TYPE_OCCUPANCYSENSOR, THING_TYPE_LIGHTTEMPERATUREOCCUPANCYSENSOR, THING_TYPE_LIGHTSENSOR,
58                     THING_TYPE_PUSHBUTTON, THING_TYPE_AUTOMATEDMETERSENSOR, THING_TYPE_ENVIRONMENTALSENSOR,
59                     THING_TYPE_MULTFUNCTIONSMOKEDETECTOR));
60
61     protected final Hashtable<RORG, EEPType> receivingEEPTypes = new Hashtable<>();
62
63     public EnOceanBaseSensorHandler(Thing thing, ItemChannelLinkRegistry itemChannelLinkRegistry) {
64         super(thing, itemChannelLinkRegistry);
65     }
66
67     @Override
68     void initializeConfig() {
69         config = getConfigAs(EnOceanBaseConfig.class);
70     }
71
72     @Override
73     Collection<EEPType> getEEPTypes() {
74         return Collections.unmodifiableCollection(receivingEEPTypes.values());
75     }
76
77     @Override
78     boolean validateConfig() {
79         receivingEEPTypes.clear();
80         try {
81             config.receivingEEPId.forEach(receivingEEP -> {
82                 EEPType receivingEEPType = EEPType.getType(receivingEEP);
83                 if (receivingEEPTypes.putIfAbsent(receivingEEPType.getRORG(), receivingEEPType) != null) {
84                     throw new IllegalArgumentException("Receiving more than one EEP of the same RORG is not supported");
85                 }
86             });
87             if (config.receivingSIGEEP) {
88                 receivingEEPTypes.put(EEPType.SigBatteryStatus.getRORG(), EEPType.SigBatteryStatus);
89             }
90         } catch (IllegalArgumentException e) {
91             configurationErrorDescription = e.getMessage();
92             return false;
93         }
94
95         updateChannels();
96
97         if (!validateEnoceanId(config.enoceanId)) {
98             configurationErrorDescription = "EnOceanId is not a valid EnOceanId";
99             return false;
100         }
101
102         if (!config.enoceanId.equals(EMPTYENOCEANID)) {
103             getBridgeHandler().addPacketListener(this);
104         }
105
106         return true;
107     }
108
109     @Override
110     public long getSenderIdToListenTo() {
111         return Long.parseLong(config.enoceanId, 16);
112     }
113
114     @Override
115     public void handleRemoval() {
116         if (getBridgeHandler() != null) {
117             getBridgeHandler().removePacketListener(this);
118         }
119         super.handleRemoval();
120     }
121
122     @Override
123     public void handleCommand(ChannelUID channelUID, Command command) {
124         // sensor things cannot send any messages, hence they are not allowed to handle any command
125         // The only possible command would be "Refresh"
126     }
127
128     protected Predicate<Channel> channelFilter(EEPType eepType, byte[] senderId) {
129         return c -> {
130             boolean result = eepType.GetSupportedChannels().containsKey(c.getUID().getId());
131             return (isLinked(c.getUID()) || c.getKind() == ChannelKind.TRIGGER) && result;
132         };
133     }
134
135     @Override
136     public void packetReceived(BasePacket packet) {
137         ERP1Message msg = (ERP1Message) packet;
138         EEPType receivingEEPType = receivingEEPTypes.get(msg.getRORG());
139         if (receivingEEPType == null) {
140             return;
141         }
142
143         EEP eep = EEPFactory.buildEEP(receivingEEPType, (ERP1Message) packet);
144         logger.debug("ESP Packet payload {} for {} received", HexUtils.bytesToHex(packet.getPayload()),
145                 HexUtils.bytesToHex(msg.getSenderId()));
146
147         if (eep.isValid()) {
148             byte[] senderId = msg.getSenderId();
149
150             // try to interpret received message for all linked or trigger channels
151             getThing().getChannels().stream().filter(channelFilter(receivingEEPType, senderId))
152                     .sorted(Comparator.comparing(Channel::getKind)) // handle state channels first
153                     .forEachOrdered(channel -> {
154
155                         ChannelTypeUID channelTypeUID = channel.getChannelTypeUID();
156                         String channelTypeId = (channelTypeUID != null) ? channelTypeUID.getId() : "";
157
158                         String channelId = channel.getUID().getId();
159                         Configuration channelConfig = channel.getConfiguration();
160
161                         switch (channel.getKind()) {
162                             case STATE:
163                                 State result = eep.convertToState(channelId, channelTypeId, channelConfig,
164                                         this::getCurrentState);
165
166                                 // if message can be interpreted (result != UnDefType.UNDEF) => update item state
167                                 if (result != null && result != UnDefType.UNDEF) {
168                                     updateState(channelId, result);
169                                 }
170                                 break;
171                             case TRIGGER:
172                                 String lastEvent = lastEvents.get(channelId);
173                                 String event = eep.convertToEvent(channelId, channelTypeId, lastEvent, channelConfig);
174                                 if (event != null) {
175                                     triggerChannel(channel.getUID(), event);
176                                     lastEvents.put(channelId, event);
177                                 }
178                                 break;
179                         }
180                     });
181         }
182     }
183 }