]> git.basschouten.com Git - openhab-addons.git/blob
f51766be084e42f39cf6a162d53eb87af8719448
[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.plugwise.internal.handler;
14
15 import static org.openhab.binding.plugwise.internal.PlugwiseBindingConstants.CONFIG_PROPERTY_MAC_ADDRESS;
16 import static org.openhab.binding.plugwise.internal.protocol.field.DeviceType.STICK;
17 import static org.openhab.core.thing.ThingStatus.*;
18
19 import java.io.IOException;
20 import java.time.Duration;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.concurrent.CopyOnWriteArrayList;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.binding.plugwise.internal.PlugwiseCommunicationHandler;
28 import org.openhab.binding.plugwise.internal.PlugwiseDeviceTask;
29 import org.openhab.binding.plugwise.internal.PlugwiseInitializationException;
30 import org.openhab.binding.plugwise.internal.PlugwiseMessagePriority;
31 import org.openhab.binding.plugwise.internal.PlugwiseUtils;
32 import org.openhab.binding.plugwise.internal.config.PlugwiseStickConfig;
33 import org.openhab.binding.plugwise.internal.listener.PlugwiseMessageListener;
34 import org.openhab.binding.plugwise.internal.listener.PlugwiseStickStatusListener;
35 import org.openhab.binding.plugwise.internal.protocol.AcknowledgementMessage;
36 import org.openhab.binding.plugwise.internal.protocol.AcknowledgementMessage.ExtensionCode;
37 import org.openhab.binding.plugwise.internal.protocol.InformationRequestMessage;
38 import org.openhab.binding.plugwise.internal.protocol.InformationResponseMessage;
39 import org.openhab.binding.plugwise.internal.protocol.Message;
40 import org.openhab.binding.plugwise.internal.protocol.NetworkStatusRequestMessage;
41 import org.openhab.binding.plugwise.internal.protocol.NetworkStatusResponseMessage;
42 import org.openhab.binding.plugwise.internal.protocol.field.DeviceType;
43 import org.openhab.binding.plugwise.internal.protocol.field.MACAddress;
44 import org.openhab.core.io.transport.serial.SerialPortManager;
45 import org.openhab.core.thing.Bridge;
46 import org.openhab.core.thing.ChannelUID;
47 import org.openhab.core.thing.Thing;
48 import org.openhab.core.thing.ThingStatus;
49 import org.openhab.core.thing.ThingStatusDetail;
50 import org.openhab.core.thing.binding.BaseBridgeHandler;
51 import org.openhab.core.types.Command;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 /**
56  * <p>
57  * The {@link PlugwiseStickHandler} handles channel updates and commands for a Plugwise Stick device.
58  * </p>
59  * <p>
60  * The Stick is an USB ZigBee controller that communicates with the Circle+. It is a {@link Bridge} to the devices on a
61  * Plugwise ZigBee mesh network.
62  * </p>
63  *
64  * @author Wouter Born, Karel Goderis - Initial contribution
65  */
66 @NonNullByDefault
67 public class PlugwiseStickHandler extends BaseBridgeHandler implements PlugwiseMessageListener {
68
69     private final PlugwiseDeviceTask onlineStateUpdateTask = new PlugwiseDeviceTask("Online state update", scheduler) {
70         @Override
71         public Duration getConfiguredInterval() {
72             return Duration.ofSeconds(20);
73         }
74
75         @Override
76         public void runTask() {
77             initialize();
78         }
79
80         @Override
81         public boolean shouldBeScheduled() {
82             return thing.getStatus() == OFFLINE;
83         }
84     };
85
86     private final Logger logger = LoggerFactory.getLogger(PlugwiseStickHandler.class);
87     private final PlugwiseCommunicationHandler communicationHandler;
88     private final List<PlugwiseStickStatusListener> statusListeners = new CopyOnWriteArrayList<>();
89
90     private PlugwiseStickConfig configuration = new PlugwiseStickConfig();
91
92     private @Nullable MACAddress circlePlusMAC;
93     private @Nullable MACAddress stickMAC;
94
95     public PlugwiseStickHandler(Bridge bridge, SerialPortManager serialPortManager) {
96         super(bridge);
97         communicationHandler = new PlugwiseCommunicationHandler(bridge.getUID(), () -> configuration,
98                 serialPortManager);
99     }
100
101     public void addMessageListener(PlugwiseMessageListener listener) {
102         communicationHandler.addMessageListener(listener);
103     }
104
105     public void addMessageListener(PlugwiseMessageListener listener, MACAddress macAddress) {
106         communicationHandler.addMessageListener(listener, macAddress);
107     }
108
109     public void addStickStatusListener(PlugwiseStickStatusListener listener) {
110         statusListeners.add(listener);
111         listener.stickStatusChanged(thing.getStatus());
112     }
113
114     @Override
115     public void dispose() {
116         communicationHandler.stop();
117         communicationHandler.removeMessageListener(this);
118         onlineStateUpdateTask.stop();
119     }
120
121     public @Nullable MACAddress getCirclePlusMAC() {
122         return circlePlusMAC;
123     }
124
125     public @Nullable MACAddress getStickMAC() {
126         return stickMAC;
127     }
128
129     public @Nullable Thing getThingByMAC(MACAddress macAddress) {
130         for (Thing thing : getThing().getThings()) {
131             String thingMAC = (String) thing.getConfiguration().get(CONFIG_PROPERTY_MAC_ADDRESS);
132             if (thingMAC != null && macAddress.equals(new MACAddress(thingMAC))) {
133                 return thing;
134             }
135         }
136
137         return null;
138     }
139
140     private void handleAcknowledgement(AcknowledgementMessage acknowledge) {
141         if (acknowledge.isExtended() && acknowledge.getExtensionCode() == ExtensionCode.CIRCLE_PLUS) {
142             circlePlusMAC = acknowledge.getMACAddress();
143             logger.debug("Received extended acknowledgement, Circle+ MAC: {}", circlePlusMAC);
144         }
145     }
146
147     @Override
148     public void handleCommand(ChannelUID channelUID, Command command) {
149         logger.debug("Handling command, channelUID: {}, command: {}", channelUID, command);
150     }
151
152     private void handleDeviceInformationResponse(InformationResponseMessage message) {
153         if (message.getDeviceType() == STICK) {
154             updateProperties(message);
155         }
156     }
157
158     private void handleNetworkStatusResponse(NetworkStatusResponseMessage message) {
159         stickMAC = message.getMACAddress();
160         if (message.isOnline()) {
161             circlePlusMAC = message.getCirclePlusMAC();
162             logger.debug("The network is online: circlePlusMAC={}, stickMAC={}", circlePlusMAC, stickMAC);
163             updateStatus(ONLINE);
164             sendMessage(new InformationRequestMessage(stickMAC));
165         } else {
166             logger.debug("The network is offline: circlePlusMAC={}, stickMAC={}", circlePlusMAC, stickMAC);
167             updateStatus(OFFLINE);
168         }
169     }
170
171     @Override
172     public void handleReponseMessage(Message message) {
173         switch (message.getType()) {
174             case ACKNOWLEDGEMENT_V1:
175             case ACKNOWLEDGEMENT_V2:
176                 handleAcknowledgement((AcknowledgementMessage) message);
177                 break;
178             case DEVICE_INFORMATION_RESPONSE:
179                 handleDeviceInformationResponse((InformationResponseMessage) message);
180                 break;
181             case NETWORK_STATUS_RESPONSE:
182                 handleNetworkStatusResponse((NetworkStatusResponseMessage) message);
183                 break;
184             default:
185                 logger.trace("Received unhandled {} message from {}", message.getType(), message.getMACAddress());
186                 break;
187         }
188     }
189
190     @Override
191     public void initialize() {
192         configuration = getConfigAs(PlugwiseStickConfig.class);
193         communicationHandler.addMessageListener(this);
194
195         try {
196             communicationHandler.start();
197             sendMessage(new NetworkStatusRequestMessage());
198         } catch (PlugwiseInitializationException e) {
199             communicationHandler.stop();
200             communicationHandler.removeMessageListener(this);
201             updateStatus(OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
202         }
203     }
204
205     public void removeMessageListener(PlugwiseMessageListener listener) {
206         communicationHandler.removeMessageListener(listener);
207     }
208
209     public void removeMessageListener(PlugwiseMessageListener listener, MACAddress macAddress) {
210         communicationHandler.addMessageListener(listener, macAddress);
211     }
212
213     public void removeStickStatusListener(PlugwiseStickStatusListener listener) {
214         statusListeners.remove(listener);
215     }
216
217     private void sendMessage(Message message) {
218         sendMessage(message, PlugwiseMessagePriority.UPDATE_AND_DISCOVERY);
219     }
220
221     public void sendMessage(Message message, PlugwiseMessagePriority priority) {
222         try {
223             communicationHandler.sendMessage(message, priority);
224         } catch (IOException e) {
225             communicationHandler.stop();
226             communicationHandler.removeMessageListener(this);
227             updateStatus(OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
228         }
229     }
230
231     protected void updateProperties(InformationResponseMessage message) {
232         Map<String, String> properties = editProperties();
233         boolean update = PlugwiseUtils.updateProperties(properties, message);
234
235         if (update) {
236             updateProperties(properties);
237         }
238     }
239
240     @Override
241     protected void updateStatus(ThingStatus status, ThingStatusDetail detail, @Nullable String comment) {
242         ThingStatus oldStatus = thing.getStatus();
243         super.updateStatus(status, detail, comment);
244         ThingStatus newStatus = thing.getStatus();
245
246         if (!oldStatus.equals(newStatus)) {
247             logger.debug("Updating listeners with status {}", status);
248             for (PlugwiseStickStatusListener listener : statusListeners) {
249                 listener.stickStatusChanged(status);
250             }
251             updateTask(onlineStateUpdateTask);
252         }
253     }
254
255     protected void updateTask(PlugwiseDeviceTask task) {
256         if (task.shouldBeScheduled()) {
257             if (!task.isScheduled() || task.getConfiguredInterval() != task.getInterval()) {
258                 if (task.isScheduled()) {
259                     task.stop();
260                 }
261                 task.update(DeviceType.STICK, getStickMAC());
262                 task.start();
263             }
264         } else if (!task.shouldBeScheduled() && task.isScheduled()) {
265             task.stop();
266         }
267     }
268 }