]> git.basschouten.com Git - openhab-addons.git/blob
65b8c9b9c6392ac1ce4de3d6ad6ed23c888f860f
[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.zoneminder.internal.handler;
14
15 import java.io.IOException;
16 import java.math.BigDecimal;
17 import java.security.GeneralSecurityException;
18 import java.util.List;
19 import java.util.concurrent.locks.Lock;
20 import java.util.concurrent.locks.ReentrantLock;
21
22 import org.openhab.binding.zoneminder.internal.DataRefreshPriorityEnum;
23 import org.openhab.binding.zoneminder.internal.ZoneMinderConstants;
24 import org.openhab.binding.zoneminder.internal.config.ZoneMinderThingConfig;
25 import org.openhab.core.library.types.OnOffType;
26 import org.openhab.core.library.types.StringType;
27 import org.openhab.core.thing.Bridge;
28 import org.openhab.core.thing.Channel;
29 import org.openhab.core.thing.ChannelUID;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.thing.ThingStatus;
32 import org.openhab.core.thing.ThingStatusDetail;
33 import org.openhab.core.thing.ThingStatusInfo;
34 import org.openhab.core.thing.binding.BaseThingHandler;
35 import org.openhab.core.thing.binding.ThingHandler;
36 import org.openhab.core.types.Command;
37 import org.openhab.core.types.State;
38 import org.openhab.core.types.UnDefType;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 import name.eskildsen.zoneminder.IZoneMinderConnectionInfo;
43 import name.eskildsen.zoneminder.IZoneMinderSession;
44 import name.eskildsen.zoneminder.ZoneMinderFactory;
45 import name.eskildsen.zoneminder.exception.ZoneMinderUrlNotFoundException;
46
47 /**
48  * The {@link ZoneMinderBaseThingHandler} is responsible for handling commands, which are
49  * sent to one of the channels.
50  *
51  * @author Martin S. Eskildsen - Initial contribution
52  */
53 public abstract class ZoneMinderBaseThingHandler extends BaseThingHandler implements ZoneMinderHandler {
54
55     /** Logger for the Thing. */
56     private final Logger logger = LoggerFactory.getLogger(ZoneMinderBaseThingHandler.class);
57
58     /** Bridge Handler for the Thing. */
59     public ZoneMinderServerBridgeHandler zoneMinderBridgeHandler;
60
61     /** This refresh status. */
62     private boolean thingRefreshed;
63
64     private Lock lockSession = new ReentrantLock();
65     private IZoneMinderSession zoneMinderSession;
66
67     /** Configuration from openHAB */
68     protected ZoneMinderThingConfig configuration;
69
70     private DataRefreshPriorityEnum refreshPriority = DataRefreshPriorityEnum.SCHEDULED;
71
72     protected boolean isOnline() {
73         if (zoneMinderSession == null) {
74             return false;
75         }
76
77         if (!zoneMinderSession.isConnected()) {
78             return false;
79         }
80
81         return true;
82     }
83
84     public DataRefreshPriorityEnum getRefreshPriority() {
85         return refreshPriority;
86     }
87
88     public ZoneMinderBaseThingHandler(Thing thing) {
89         super(thing);
90     }
91
92     /**
93      * Initializes the monitor.
94      *
95      * @author Martin S. Eskildsen
96      *
97      */
98     @Override
99     public void initialize() {
100         updateStatus(ThingStatus.ONLINE);
101     }
102
103     protected boolean isConnected() {
104         if (zoneMinderSession == null) {
105             return false;
106         }
107         return zoneMinderSession.isConnected();
108     }
109
110     protected IZoneMinderSession aquireSession() {
111         lockSession.lock();
112         return zoneMinderSession;
113     }
114
115     protected void releaseSession() {
116         lockSession.unlock();
117     }
118
119     /**
120      * Method to start a priority data refresh task.
121      */
122
123     protected boolean startPriorityRefresh() {
124         logger.info("[MONITOR-{}]: Starting High Priority Refresh", getZoneMinderId());
125         refreshPriority = DataRefreshPriorityEnum.HIGH_PRIORITY;
126         return true;
127     }
128
129     /**
130      * Method to stop the data Refresh task.
131      */
132     protected void stopPriorityRefresh() {
133         logger.info("{}: Stopping Priority Refresh for Monitor", getLogIdentifier());
134         refreshPriority = DataRefreshPriorityEnum.SCHEDULED;
135     }
136
137     @Override
138     public void dispose() {
139     }
140
141     /**
142      * Helper method for getting ChannelUID from ChannelId.
143      *
144      */
145     public ChannelUID getChannelUIDFromChannelId(String id) {
146         Channel ch = thing.getChannel(id);
147         if (ch == null) {
148             return null;
149         } else {
150             return ch.getUID();
151         }
152     }
153
154     protected abstract void onFetchData();
155
156     /**
157      * Method to Refresh Thing Handler.
158      */
159     public final synchronized void refreshThing(IZoneMinderSession session, DataRefreshPriorityEnum refreshPriority) {
160         if ((refreshPriority != getRefreshPriority()) && (!isConnected())) {
161             return;
162         }
163
164         if (refreshPriority == DataRefreshPriorityEnum.HIGH_PRIORITY) {
165             logger.debug("{}: Performing HIGH PRIORITY refresh", getLogIdentifier());
166         } else {
167             logger.debug("{}: Performing refresh", getLogIdentifier());
168         }
169
170         if (getZoneMinderBridgeHandler() != null) {
171             if (isConnected()) {
172                 logger.debug("{}: refreshThing(): Bridge '{}' Found for Thing '{}'!", getLogIdentifier(),
173                         getThing().getUID(), this.getThing().getUID());
174
175                 onFetchData();
176             }
177         }
178
179         Thing thing = getThing();
180         List<Channel> channels = thing.getChannels();
181         logger.debug("{}: refreshThing(): Refreshing Thing - {}", getLogIdentifier(), thing.getUID());
182
183         for (Channel channel : channels) {
184             updateChannel(channel.getUID());
185         }
186
187         this.setThingRefreshed(true);
188         logger.debug("[{}: refreshThing(): Thing Refreshed - {}", getLogIdentifier(), thing.getUID());
189     }
190
191     /**
192      * Get the Bridge Handler for ZoneMinder.
193      *
194      * @return zoneMinderBridgeHandler
195      */
196     public synchronized ZoneMinderServerBridgeHandler getZoneMinderBridgeHandler() {
197         if (this.zoneMinderBridgeHandler == null) {
198             Bridge bridge = getBridge();
199
200             if (bridge == null) {
201                 logger.debug("{}: getZoneMinderBridgeHandler(): Unable to get bridge!", getLogIdentifier());
202                 return null;
203             }
204
205             logger.debug("{}: getZoneMinderBridgeHandler(): Bridge for '{}' - '{}'", getLogIdentifier(),
206                     getThing().getUID(), bridge.getUID());
207             ThingHandler handler = null;
208             try {
209                 handler = bridge.getHandler();
210             } catch (Exception ex) {
211                 logger.debug("{}: Exception in 'getZoneMinderBridgeHandler()': {}", getLogIdentifier(),
212                         ex.getMessage());
213             }
214
215             if (handler instanceof ZoneMinderServerBridgeHandler) {
216                 this.zoneMinderBridgeHandler = (ZoneMinderServerBridgeHandler) handler;
217             } else {
218                 logger.debug("{}: getZoneMinderBridgeHandler(): Unable to get bridge handler!", getLogIdentifier());
219             }
220         }
221
222         return this.zoneMinderBridgeHandler;
223     }
224
225     /**
226      * Method to Update a Channel
227      *
228      * @param channel
229      */
230     @Override
231     public void updateChannel(ChannelUID channel) {
232         switch (channel.getId()) {
233             case ZoneMinderConstants.CHANNEL_ONLINE:
234                 updateState(channel, getChannelBoolAsOnOffState(isOnline()));
235                 break;
236             default:
237                 logger.error(
238                         "{}: updateChannel() in base class, called for an unknown channel '{}', this channel must be handled in super class.",
239                         getLogIdentifier(), channel.getId());
240         }
241     }
242
243     @Override
244     public void handleCommand(ChannelUID channelUID, Command command) {
245     }
246
247     @Override
248     public void onBridgeConnected(ZoneMinderServerBridgeHandler bridge, IZoneMinderConnectionInfo connection)
249             throws IllegalArgumentException, GeneralSecurityException, IOException, ZoneMinderUrlNotFoundException {
250         lockSession.lock();
251         try {
252             zoneMinderSession = ZoneMinderFactory.CreateSession(connection);
253
254         } finally {
255             lockSession.unlock();
256         }
257     }
258
259     @Override
260     public void onBridgeDisconnected(ZoneMinderServerBridgeHandler bridge) {
261         if (bridge.getThing().getUID().equals(getThing().getBridgeUID())) {
262             this.setThingRefreshed(false);
263         }
264
265         lockSession.lock();
266         try {
267             zoneMinderSession = null;
268         } finally {
269             lockSession.unlock();
270         }
271     }
272
273     /**
274      * Get Channel by ChannelUID.
275      *
276      * @param {ChannelUID} channelUID Identifier of Channel
277      */
278     public Channel getChannel(ChannelUID channelUID) {
279         Channel channel = null;
280
281         List<Channel> channels = getThing().getChannels();
282
283         for (Channel ch : channels) {
284             if (channelUID == ch.getUID()) {
285                 channel = ch;
286                 break;
287             }
288         }
289
290         return channel;
291     }
292
293     /**
294      * Get Thing Handler refresh status.
295      *
296      * @return thingRefresh
297      */
298     public boolean isThingRefreshed() {
299         return thingRefreshed;
300     }
301
302     /**
303      * Set Thing Handler refresh status.
304      *
305      * @param {boolean} refreshed Sets status refreshed of thing
306      */
307     public void setThingRefreshed(boolean refreshed) {
308         this.thingRefreshed = refreshed;
309     }
310
311     protected abstract String getZoneMinderThingType();
312
313     private Object getConfigValue(String configKey) {
314         return getThing().getConfiguration().getProperties().get(configKey);
315     }
316
317     /*
318      * Helper to get a value from configuration as a String
319      *
320      * @author Martin S. Eskildsen
321      *
322      */
323     protected String getConfigValueAsString(String configKey) {
324         return (String) getConfigValue(configKey);
325     }
326
327     /*
328      * Helper to get a value from configuration as a Integer
329      *
330      * @author Martin S. Eskildsen
331      *
332      */
333     protected Integer getConfigValueAsInteger(String configKey) {
334         return (Integer) getConfigValue(configKey);
335     }
336
337     protected BigDecimal getConfigValueAsBigDecimal(String configKey) {
338         return (BigDecimal) getConfigValue(configKey);
339     }
340
341     protected State getChannelStringAsStringState(String channelValue) {
342         State state = UnDefType.UNDEF;
343
344         try {
345             if (isConnected()) {
346                 state = new StringType(channelValue);
347             }
348
349         } catch (Exception ex) {
350             logger.error("{}", ex.getMessage());
351         }
352
353         return state;
354     }
355
356     protected State getChannelBoolAsOnOffState(boolean value) {
357         State state = UnDefType.UNDEF;
358
359         try {
360             if (isConnected()) {
361                 state = value ? OnOffType.ON : OnOffType.OFF;
362             }
363
364         } catch (Exception ex) {
365             logger.error("{}: Exception occurred in 'getChannelBoolAsOnOffState()' (Exception='{}')",
366                     getLogIdentifier(), ex.getMessage());
367         }
368
369         return state;
370     }
371
372     @Override
373     public abstract String getLogIdentifier();
374
375     protected void updateThingStatus(ThingStatus thingStatus, ThingStatusDetail statusDetail,
376             String statusDescription) {
377         ThingStatusInfo curStatusInfo = thing.getStatusInfo();
378         String curDescription = ((curStatusInfo.getDescription() == null) ? "" : curStatusInfo.getDescription());
379         // Status changed
380         if ((curStatusInfo.getStatus() != thingStatus) || (curStatusInfo.getStatusDetail() != statusDetail)
381                 || (curDescription != statusDescription)) {
382             // Update Status correspondingly
383             if ((thingStatus == ThingStatus.OFFLINE) && (statusDetail != ThingStatusDetail.NONE)) {
384                 logger.info("{}: Thing status changed from '{}' to '{}' (DetailedStatus='{}', Description='{}')",
385                         getLogIdentifier(), thing.getStatus(), thingStatus, statusDetail, statusDescription);
386                 updateStatus(thingStatus, statusDetail, statusDescription);
387             } else {
388                 logger.info("{}: Thing status changed from '{}' to '{}'", getLogIdentifier(), thing.getStatus(),
389                         thingStatus);
390                 updateStatus(thingStatus);
391             }
392         }
393     }
394 }