]> git.basschouten.com Git - openhab-addons.git/blob
2cc70b05d2fda0bf89bbc6e087b1beee067a2479
[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.Collections;
19 import java.util.Map;
20 import java.util.Set;
21
22 import javax.security.auth.login.FailedLoginException;
23
24 import org.openhab.binding.zoneminder.internal.DataRefreshPriorityEnum;
25 import org.openhab.binding.zoneminder.internal.ZoneMinderConstants;
26 import org.openhab.binding.zoneminder.internal.ZoneMinderProperties;
27 import org.openhab.binding.zoneminder.internal.config.ZoneMinderThingMonitorConfig;
28 import org.openhab.core.library.types.OnOffType;
29 import org.openhab.core.thing.Bridge;
30 import org.openhab.core.thing.ChannelUID;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.thing.ThingStatus;
33 import org.openhab.core.thing.ThingStatusDetail;
34 import org.openhab.core.thing.ThingTypeUID;
35 import org.openhab.core.types.Command;
36 import org.openhab.core.types.RefreshType;
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.IZoneMinderDaemonStatus;
44 import name.eskildsen.zoneminder.IZoneMinderEventData;
45 import name.eskildsen.zoneminder.IZoneMinderEventSubscriber;
46 import name.eskildsen.zoneminder.IZoneMinderMonitor;
47 import name.eskildsen.zoneminder.IZoneMinderMonitorData;
48 import name.eskildsen.zoneminder.IZoneMinderSession;
49 import name.eskildsen.zoneminder.ZoneMinderFactory;
50 import name.eskildsen.zoneminder.api.event.ZoneMinderEvent;
51 import name.eskildsen.zoneminder.api.telnet.ZoneMinderTriggerEvent;
52 import name.eskildsen.zoneminder.common.ZoneMinderMonitorFunctionEnum;
53 import name.eskildsen.zoneminder.common.ZoneMinderMonitorStatusEnum;
54 import name.eskildsen.zoneminder.exception.ZoneMinderUrlNotFoundException;
55
56 /**
57  * The {@link ZoneMinderThingMonitorHandler} is responsible for handling commands, which are
58  * sent to one of the channels.
59  *
60  * @author Martin S. Eskildsen - Initial contribution
61  */
62 public class ZoneMinderThingMonitorHandler extends ZoneMinderBaseThingHandler implements IZoneMinderEventSubscriber {
63     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections
64             .singleton(ZoneMinderConstants.THING_TYPE_THING_ZONEMINDER_MONITOR);
65
66     /** Make sure we can log errors, warnings or what ever somewhere */
67     private final Logger logger = LoggerFactory.getLogger(ZoneMinderThingMonitorHandler.class);
68
69     private ZoneMinderThingMonitorConfig config;
70
71     private ZoneMinderEvent curEvent;
72
73     /**
74      * Channels
75      */
76     private ZoneMinderMonitorFunctionEnum channelFunction = ZoneMinderMonitorFunctionEnum.NONE;
77     private Boolean channelEnabled = false;
78     private boolean channelRecordingState;
79     private boolean channelAlarmedState;
80     private String channelEventCause = "";
81     private ZoneMinderMonitorStatusEnum channelMonitorStatus = ZoneMinderMonitorStatusEnum.UNKNOWN;
82     private boolean channelDaemonCapture;
83     private boolean channelDaemonAnalysis;
84     private boolean channelDaemonFrame;
85     private boolean channelForceAlarm;
86
87     private int forceAlarmManualState = -1;
88
89     public ZoneMinderThingMonitorHandler(Thing thing) {
90         super(thing);
91
92         logger.info("{}: Starting ZoneMinder Server Thing Handler (Thing='{}')", getLogIdentifier(), thing.getUID());
93     }
94
95     @Override
96     public void dispose() {
97     }
98
99     @Override
100     public String getZoneMinderId() {
101         if (config == null) {
102             logger.error("{}: Configuration for Thing '{}' is not loaded correctly.", getLogIdentifier(),
103                     getThing().getUID());
104             return "";
105         }
106         return config.getZoneMinderId().toString();
107     }
108
109     @Override
110     public void onBridgeConnected(ZoneMinderServerBridgeHandler bridge, IZoneMinderConnectionInfo connection)
111             throws IllegalArgumentException, GeneralSecurityException, IOException, ZoneMinderUrlNotFoundException {
112         try {
113             logger.info("{}: Bridge '{}' connected", getLogIdentifier(), bridge.getThing().getUID().getAsString());
114             super.onBridgeConnected(bridge, connection);
115
116             ZoneMinderFactory.SubscribeMonitorEvents(connection, config.getZoneMinderId(), this);
117             IZoneMinderSession session = aquireSession();
118             IZoneMinderMonitor monitor = ZoneMinderFactory.getMonitorProxy(session, config.getZoneMinderId());
119             IZoneMinderMonitorData monitorData = monitor.getMonitorData();
120
121             logger.debug("{}:    SourceType:         {}", getLogIdentifier(), monitorData.getSourceType().name());
122             logger.debug("{}:    Format:             {}", getLogIdentifier(), monitorData.getFormat());
123             logger.debug("{}:    AlarmFrameCount:    {}", getLogIdentifier(), monitorData.getAlarmFrameCount());
124             logger.debug("{}:    AlarmMaxFPS:        {}", getLogIdentifier(), monitorData.getAlarmMaxFPS());
125             logger.debug("{}:    AnalysisFPS:        {}", getLogIdentifier(), monitorData.getAnalysisFPS());
126             logger.debug("{}:    Height x Width:     {} x {}", getLogIdentifier(), monitorData.getHeight(),
127                     monitorData.getWidth());
128
129             updateMonitorProperties(session);
130         } catch (Exception ex) {
131             logger.error("{}: Exception occurred when calling 'onBridgeConencted()'. Exception='{}'",
132                     getLogIdentifier(), ex.getMessage());
133         } finally {
134             releaseSession();
135         }
136     }
137
138     @Override
139     public void onBridgeDisconnected(ZoneMinderServerBridgeHandler bridge) {
140         try {
141             logger.info("{}: Bridge '{}' disconnected", getLogIdentifier(), bridge.getThing().getUID().getAsString());
142
143             logger.info("{}: Unsubscribing from Monitor Events: {}", getLogIdentifier(),
144                     bridge.getThing().getUID().getAsString());
145             ZoneMinderFactory.UnsubscribeMonitorEvents(config.getZoneMinderId(), this);
146
147             logger.debug("{}: Calling parent onBridgeConnected()", getLogIdentifier());
148             super.onBridgeDisconnected(bridge);
149         } catch (Exception ex) {
150             logger.error("{}: Exception occurred when calling 'onBridgeDisonencted()'. Exception='{}'",
151                     getLogIdentifier(), ex.getMessage());
152         }
153     }
154
155     @Override
156     public void handleCommand(ChannelUID channelUID, Command command) {
157         try {
158             logger.debug("{}: Channel '{}' in monitor '{}' received command='{}'", getLogIdentifier(), channelUID,
159                     getZoneMinderId(), command);
160
161             // Allow refresh of channels
162             if (command == RefreshType.REFRESH) {
163                 updateChannel(channelUID);
164                 return;
165             }
166
167             // Communication TO Monitor
168             switch (channelUID.getId()) {
169                 // Done via Telnet connection
170                 case ZoneMinderConstants.CHANNEL_MONITOR_FORCE_ALARM:
171                     logger.debug(
172                             "{}: 'handleCommand' => CHANNEL_MONITOR_FORCE_ALARM: Command '{}' received for monitor '{}'",
173                             getLogIdentifier(), command, channelUID.getId());
174
175                     if ((command == OnOffType.OFF) || (command == OnOffType.ON)) {
176                         String eventText = getConfigValueAsString(ZoneMinderConstants.PARAMETER_MONITOR_EVENTTEXT);
177
178                         BigDecimal eventTimeout = getConfigValueAsBigDecimal(
179                                 ZoneMinderConstants.PARAMETER_MONITOR_TRIGGER_TIMEOUT);
180
181                         ZoneMinderServerBridgeHandler bridge = getZoneMinderBridgeHandler();
182                         if (bridge == null) {
183                             logger.warn("'handleCommand()': Bridge is 'null'!");
184                         }
185
186                         IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(),
187                                 getZoneMinderId());
188                         try {
189                             if (command == OnOffType.ON) {
190                                 forceAlarmManualState = 1;
191                                 logger.info("{}: Activate 'ForceAlarm' to '{}' (Reason='{}', Timeout='{}')",
192                                         getLogIdentifier(), command, eventText, eventTimeout.intValue());
193
194                                 monitorProxy.activateForceAlarm(255, ZoneMinderConstants.MONITOR_EVENT_OPENHAB,
195                                         eventText, "", eventTimeout.intValue());
196
197                             }
198
199                             else if (command == OnOffType.OFF) {
200                                 forceAlarmManualState = 0;
201                                 logger.info("{}: Cancel 'ForceAlarm'", getLogIdentifier());
202                                 monitorProxy.deactivateForceAlarm();
203
204                             }
205
206                         } finally {
207                             releaseSession();
208                         }
209
210                         recalculateChannelStates();
211
212                         handleCommand(channelUID, RefreshType.REFRESH);
213                         handleCommand(getChannelUIDFromChannelId(ZoneMinderConstants.CHANNEL_MONITOR_EVENT_STATE),
214                                 RefreshType.REFRESH);
215                         handleCommand(getChannelUIDFromChannelId(ZoneMinderConstants.CHANNEL_MONITOR_RECORD_STATE),
216                                 RefreshType.REFRESH);
217
218                         // Force a refresh
219                         startPriorityRefresh();
220                     }
221                     break;
222
223                 case ZoneMinderConstants.CHANNEL_MONITOR_ENABLED:
224                     logger.debug(
225                             "{}: 'handleCommand' => CHANNEL_MONITOR_ENABLED: Command '{}' received for monitor '{}'",
226                             getLogIdentifier(), command, channelUID.getId());
227
228                     if ((command == OnOffType.OFF) || (command == OnOffType.ON)) {
229                         boolean newState = ((command == OnOffType.ON) ? true : false);
230
231                         IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(),
232                                 getZoneMinderId());
233                         try {
234                             monitorProxy.SetEnabled(newState);
235                         } finally {
236                             releaseSession();
237                         }
238
239                         channelEnabled = newState;
240
241                         logger.info("{}: Setting enabled to '{}'", getLogIdentifier(), command);
242                     }
243
244                     handleCommand(channelUID, RefreshType.REFRESH);
245                     break;
246
247                 case ZoneMinderConstants.CHANNEL_MONITOR_FUNCTION:
248                     String commandString = "";
249                     if (ZoneMinderMonitorFunctionEnum.isValid(command.toString())) {
250                         commandString = ZoneMinderMonitorFunctionEnum.getEnum(command.toString()).toString();
251
252                         IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(),
253                                 getZoneMinderId());
254                         try {
255                             monitorProxy.SetFunction(commandString);
256                         } finally {
257                             releaseSession();
258                         }
259
260                         // Make sure local copy is set to new value
261                         channelFunction = ZoneMinderMonitorFunctionEnum.getEnum(command.toString());
262
263                         logger.info("{}: Setting function to '{}'", getLogIdentifier(), commandString);
264
265                     } else {
266                         logger.error(
267                                 "{}: Value '{}' for monitor channel is not valid. Accepted values is: 'None', 'Monitor', 'Modect', Record', 'Mocord', 'Nodect'",
268                                 getLogIdentifier(), commandString);
269                     }
270                     handleCommand(channelUID, RefreshType.REFRESH);
271                     break;
272
273                 // They are all readonly in the channel config.
274                 case ZoneMinderConstants.CHANNEL_MONITOR_EVENT_STATE:
275                 case ZoneMinderConstants.CHANNEL_MONITOR_DETAILED_STATUS:
276                 case ZoneMinderConstants.CHANNEL_MONITOR_RECORD_STATE:
277                 case ZoneMinderConstants.CHANNEL_ONLINE:
278                 case ZoneMinderConstants.CHANNEL_MONITOR_EVENT_CAUSE:
279                 case ZoneMinderConstants.CHANNEL_MONITOR_CAPTURE_DAEMON_STATE:
280                 case ZoneMinderConstants.CHANNEL_MONITOR_ANALYSIS_DAEMON_STATE:
281                 case ZoneMinderConstants.CHANNEL_MONITOR_FRAME_DAEMON_STATE:
282                     // Do nothing, they are all read only
283                     break;
284                 default:
285                     logger.warn("{}: Command received for an unknown channel: {}", getLogIdentifier(),
286                             channelUID.getId());
287                     break;
288             }
289         } catch (Exception ex) {
290             logger.error("{}: handleCommand: Command='{}' failed for channel='{}' Exception='{}'", getLogIdentifier(),
291                     command, channelUID.getId(), ex.getMessage());
292         }
293     }
294
295     @Override
296     public void initialize() {
297         try {
298             super.initialize();
299             this.config = getMonitorConfig();
300             logger.info("{}: ZoneMinder Monitor Handler Initialized", getLogIdentifier());
301             logger.debug("{}:    Monitor Id:         {}", getLogIdentifier(), config.getZoneMinderId());
302         } catch (Exception ex) {
303             logger.error("{}: Exception occurred when calling 'initialize()'. Exception='{}'", getLogIdentifier(),
304                     ex.getMessage());
305         }
306     }
307
308     @Override
309     public void onTrippedForceAlarm(ZoneMinderTriggerEvent event) {
310         try {
311             logger.info("{}: Received forceAlarm for monitor {}", getLogIdentifier(), event.getMonitorId());
312
313             // Set Current Event to actual event
314             if (event.getState()) {
315                 startPriorityRefresh();
316
317             } else {
318                 curEvent = null;
319             }
320         } catch (Exception ex) {
321             logger.error("{}: Exception occurred inTrippedForceAlarm() Exception='{}'", getLogIdentifier(),
322                     ex.getMessage());
323         }
324     }
325
326     protected ZoneMinderThingMonitorConfig getMonitorConfig() {
327         return this.getConfigAs(ZoneMinderThingMonitorConfig.class);
328     }
329
330     @Override
331     protected String getZoneMinderThingType() {
332         return ZoneMinderConstants.THING_ZONEMINDER_MONITOR;
333     }
334
335     @Override
336     public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) {
337         // Assume success
338         ThingStatus newThingStatus = ThingStatus.ONLINE;
339         ThingStatusDetail thingStatusDetailed = ThingStatusDetail.NONE;
340         String thingStatusDescription = "";
341
342         ThingStatus curThingStatus = this.getThing().getStatus();
343
344         // Is connected to ZoneMinder and thing is ONLINE
345         if (isConnected() && curThingStatus == ThingStatus.ONLINE) {
346             updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
347             return;
348         }
349
350         try {
351             ZoneMinderFactory.validateConnection(connection);
352         } catch (IllegalArgumentException e) {
353             logger.error("{}: validateConnection failed with exception='{}'", getLogIdentifier(), e.getMessage());
354             newThingStatus = ThingStatus.OFFLINE;
355             thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR;
356             thingStatusDescription = "Could not connect to thing";
357             updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
358             return;
359         }
360
361         try {
362             String msg;
363
364             final Bridge bridge = getBridge();
365
366             // 1. Is there a Bridge assigned?
367             if (bridge == null) {
368                 msg = String.format("No Bridge assigned to monitor '%s'", thing.getUID());
369                 logger.error("{}: {}", getLogIdentifier(), msg);
370                 newThingStatus = ThingStatus.OFFLINE;
371                 thingStatusDetailed = ThingStatusDetail.BRIDGE_OFFLINE;
372                 thingStatusDescription = "No Bridge assigned to monitor";
373                 updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
374                 return;
375             } else {
376                 logger.debug("{}: ThingAvailability: Thing '{}' has Bridge '{}' defined (Check PASSED)",
377                         getLogIdentifier(), thing.getUID(), bridge.getBridgeUID());
378             }
379
380             // 2. Is Bridge Online?
381             if (bridge.getStatus() != ThingStatus.ONLINE) {
382                 msg = String.format("Bridge '%s' is OFFLINE", bridge.getBridgeUID());
383                 newThingStatus = ThingStatus.OFFLINE;
384                 thingStatusDetailed = ThingStatusDetail.BRIDGE_OFFLINE;
385                 thingStatusDescription = msg;
386                 updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
387                 logger.error("{}: {}", getLogIdentifier(), msg);
388                 return;
389             } else {
390                 logger.debug("{}: ThingAvailability: Bridge '{}' is ONLINE (Check PASSED)", getLogIdentifier(),
391                         bridge.getBridgeUID());
392             }
393
394             // 3. Is Configuration OK?
395             if (getMonitorConfig() == null) {
396                 msg = String.format("No valid configuration found for '%s'", thing.getUID());
397                 newThingStatus = ThingStatus.OFFLINE;
398                 thingStatusDetailed = ThingStatusDetail.CONFIGURATION_ERROR;
399                 thingStatusDescription = msg;
400                 updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
401
402                 logger.error("{}: {}", getLogIdentifier(), msg);
403                 return;
404             } else {
405                 logger.debug("{}: ThingAvailability: Thing '{}' has valid configuration (Check PASSED)",
406                         getLogIdentifier(), thing.getUID());
407             }
408
409             // ZoneMinder Id for Monitor not set, we are pretty much lost then
410             if (getMonitorConfig().getZoneMinderId().isEmpty()) {
411                 msg = String.format("No Id is specified for monitor '%s'", thing.getUID());
412                 newThingStatus = ThingStatus.OFFLINE;
413                 thingStatusDetailed = ThingStatusDetail.CONFIGURATION_ERROR;
414                 thingStatusDescription = msg;
415                 updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
416
417                 logger.error("{}: {}", getLogIdentifier(), msg);
418                 return;
419             } else {
420                 logger.debug("{}: ThingAvailability: ZoneMinder Id for Thing '{}' defined (Check PASSED)",
421                         getLogIdentifier(), thing.getUID());
422             }
423
424             IZoneMinderMonitor monitorProxy = null;
425             IZoneMinderDaemonStatus captureDaemon = null;
426             // TODO:: Also look at Analysis and Frame Daemons (only if they are supposed to be running)
427             // IZoneMinderSession session = aquireSession();
428
429             IZoneMinderSession curSession = null;
430             try {
431                 curSession = ZoneMinderFactory.CreateSession(connection);
432             } catch (FailedLoginException | IllegalArgumentException | IOException
433                     | ZoneMinderUrlNotFoundException ex) {
434                 logger.error("{}: Create Session failed with exception {}", getLogIdentifier(), ex.getMessage());
435
436                 newThingStatus = ThingStatus.OFFLINE;
437                 thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR;
438                 thingStatusDescription = "Failed to connect. (Check Log)";
439
440                 updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
441                 return;
442             }
443
444             if (curSession != null) {
445                 monitorProxy = ZoneMinderFactory.getMonitorProxy(curSession, getZoneMinderId());
446
447                 captureDaemon = monitorProxy.getCaptureDaemonStatus();
448             }
449
450             if (captureDaemon == null) {
451                 msg = String.format("Capture Daemon not accssible");
452                 newThingStatus = ThingStatus.OFFLINE;
453                 thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR;
454                 thingStatusDescription = msg;
455                 updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
456                 logger.error("{}: {}", getLogIdentifier(), msg);
457                 return;
458             } else if (!captureDaemon.getStatus()) {
459                 msg = String.format("Capture Daemon is not running");
460                 newThingStatus = ThingStatus.OFFLINE;
461                 thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR;
462                 thingStatusDescription = msg;
463                 updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
464                 logger.error("{}: {}", getLogIdentifier(), msg);
465                 return;
466             }
467             newThingStatus = ThingStatus.ONLINE;
468
469         } catch (Exception exception) {
470             newThingStatus = ThingStatus.OFFLINE;
471             thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR;
472             thingStatusDescription = "Error occurred (Check log)";
473             updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
474
475             logger.error("{}: 'ThingMonitorHandler.updateAvailabilityStatus()': Exception occurred '{}'",
476                     getLogIdentifier(), exception.getMessage());
477
478             return;
479         }
480
481         updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
482     }
483
484     /*
485      * From here we update states in openHAB
486      *
487      * @see
488      * org.openhab.binding.zoneminder.handler.ZoneMinderBaseThingHandler#updateChannel(org.openhab.core.thing.
489      * ChannelUID)
490      */
491     @Override
492     public void updateChannel(ChannelUID channel) {
493         State state = null;
494
495         try {
496             switch (channel.getId()) {
497                 case ZoneMinderConstants.CHANNEL_MONITOR_ENABLED:
498                     state = getChannelBoolAsOnOffState(channelEnabled);
499                     break;
500
501                 case ZoneMinderConstants.CHANNEL_ONLINE:
502                     // Ask super class to handle, because this channel is shared for all things
503                     super.updateChannel(channel);
504                     break;
505                 case ZoneMinderConstants.CHANNEL_MONITOR_FORCE_ALARM:
506                     state = getChannelBoolAsOnOffState(channelForceAlarm);
507                     break;
508                 case ZoneMinderConstants.CHANNEL_MONITOR_EVENT_STATE:
509                     state = getChannelBoolAsOnOffState(channelAlarmedState);
510                     break;
511
512                 case ZoneMinderConstants.CHANNEL_MONITOR_RECORD_STATE:
513                     state = getChannelBoolAsOnOffState(channelRecordingState);
514                     break;
515
516                 case ZoneMinderConstants.CHANNEL_MONITOR_DETAILED_STATUS:
517                     state = getDetailedStatus();
518                     break;
519
520                 case ZoneMinderConstants.CHANNEL_MONITOR_EVENT_CAUSE:
521                     state = getChannelStringAsStringState(channelEventCause);
522                     break;
523
524                 case ZoneMinderConstants.CHANNEL_MONITOR_FUNCTION:
525                     state = getChannelStringAsStringState(channelFunction.toString());
526                     break;
527
528                 case ZoneMinderConstants.CHANNEL_MONITOR_CAPTURE_DAEMON_STATE:
529                     state = getChannelBoolAsOnOffState(channelDaemonCapture);
530                     break;
531
532                 case ZoneMinderConstants.CHANNEL_MONITOR_ANALYSIS_DAEMON_STATE:
533                     state = getChannelBoolAsOnOffState(channelDaemonAnalysis);
534                     break;
535
536                 case ZoneMinderConstants.CHANNEL_MONITOR_FRAME_DAEMON_STATE:
537                     state = getChannelBoolAsOnOffState(channelDaemonFrame);
538                     break;
539
540                 default:
541                     logger.warn("{}: updateChannel(): Monitor '{}': No handler defined for channel='{}'",
542                             getLogIdentifier(), thing.getLabel(), channel.getAsString());
543
544                     // Ask super class to handle
545                     super.updateChannel(channel);
546             }
547
548             if (state != null) {
549                 logger.debug("{}: Setting channel '{}' to '{}'", getLogIdentifier(), channel.toString(),
550                         state.toString());
551                 updateState(channel.getId(), state);
552             }
553         } catch (Exception ex) {
554             logger.error("{}: Error when 'updateChannel' was called (channelId='{}'state='{}', exception'{}')",
555                     getLogIdentifier(), channel, state, ex.getMessage());
556         }
557     }
558
559     @Override
560     public void updateStatus(ThingStatus status) {
561         super.updateStatus(status);
562         updateState(ZoneMinderConstants.CHANNEL_ONLINE,
563                 ((status == ThingStatus.ONLINE) ? OnOffType.ON : OnOffType.OFF));
564     }
565
566     protected void recalculateChannelStates() {
567         boolean recordingFunction = false;
568         boolean recordingDetailedState = false;
569         boolean alarmedFunction = false;
570         boolean alarmedDetailedState = false;
571
572         // Calculate based on state of Function
573         switch (channelFunction) {
574             case NONE:
575             case MONITOR:
576                 alarmedFunction = false;
577                 recordingFunction = false;
578                 break;
579
580             case MODECT:
581                 alarmedFunction = true;
582                 recordingFunction = true;
583                 break;
584             case RECORD:
585                 alarmedFunction = false;
586                 recordingFunction = true;
587                 break;
588             case MOCORD:
589                 alarmedFunction = true;
590                 recordingFunction = true;
591                 break;
592             case NODECT:
593                 alarmedFunction = false;
594                 recordingFunction = true;
595                 break;
596             default:
597                 recordingFunction = (curEvent != null) ? true : false;
598         }
599         logger.debug(
600                 "{}: Recalculate channel states based on Function: Function='{}' -> alarmState='{}', recordingState='{}'",
601                 getLogIdentifier(), channelFunction.name(), alarmedFunction, recordingFunction);
602
603         // Calculated based on detailed Monitor Status
604         switch (channelMonitorStatus) {
605             case IDLE:
606                 alarmedDetailedState = false;
607                 recordingDetailedState = false;
608                 channelForceAlarm = false;
609                 channelEventCause = "";
610                 break;
611             case PRE_ALARM:
612                 alarmedDetailedState = true;
613                 recordingDetailedState = true;
614                 channelForceAlarm = false;
615                 break;
616             case ALARM:
617                 alarmedDetailedState = true;
618                 recordingDetailedState = true;
619                 channelForceAlarm = true;
620                 break;
621             case ALERT:
622                 alarmedDetailedState = true;
623                 recordingDetailedState = true;
624                 channelForceAlarm = false;
625                 break;
626             case RECORDING:
627                 alarmedDetailedState = false;
628                 recordingDetailedState = true;
629                 channelForceAlarm = false;
630                 break;
631             case UNKNOWN:
632
633         }
634         logger.debug(
635                 "{}: Recalculate channel states based on Detailed State: DetailedState='{}' -> alarmState='{}', recordingState='{}'",
636                 getLogIdentifier(), channelMonitorStatus.name(), alarmedDetailedState, recordingDetailedState);
637
638         // Check if Force alarm was initialed from openHAB
639         if (forceAlarmManualState == 0) {
640             if (channelForceAlarm) {
641                 channelForceAlarm = false;
642             } else {
643                 forceAlarmManualState = -1;
644             }
645         } else if (forceAlarmManualState == 1) {
646             if (!channelForceAlarm) {
647                 channelForceAlarm = true;
648             } else {
649                 forceAlarmManualState = -1;
650             }
651
652         }
653
654         // Now we can conclude on the Alarmed and Recording channel state
655         channelRecordingState = (recordingFunction && recordingDetailedState && channelEnabled);
656         channelAlarmedState = (alarmedFunction && alarmedDetailedState && channelEnabled);
657     }
658
659     @Override
660     protected void onFetchData() {
661         IZoneMinderSession session = null;
662
663         session = aquireSession();
664         try {
665             IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(session, getZoneMinderId());
666
667             IZoneMinderMonitorData data = null;
668             IZoneMinderDaemonStatus captureDaemon = null;
669             IZoneMinderDaemonStatus analysisDaemon = null;
670             IZoneMinderDaemonStatus frameDaemon = null;
671
672             data = monitorProxy.getMonitorData();
673             logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
674                     monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(),
675                     monitorProxy.getHttpResponseMessage());
676
677             captureDaemon = monitorProxy.getCaptureDaemonStatus();
678             logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
679                     monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(),
680                     monitorProxy.getHttpResponseMessage());
681
682             analysisDaemon = monitorProxy.getAnalysisDaemonStatus();
683             logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
684                     monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(),
685                     monitorProxy.getHttpResponseMessage());
686
687             frameDaemon = monitorProxy.getFrameDaemonStatus();
688             logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
689                     monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(),
690                     monitorProxy.getHttpResponseMessage());
691
692             if ((data.getHttpResponseCode() != 200) || (captureDaemon.getHttpResponseCode() != 200)
693                     || (analysisDaemon.getHttpResponseCode() != 200) || (frameDaemon.getHttpResponseCode() != 200)) {
694                 if (data.getHttpResponseCode() != 200) {
695                     logger.warn("{}: HTTP Response MonitorData: Code='{}', Message'{}'", getLogIdentifier(),
696                             data.getHttpResponseCode(), data.getHttpResponseMessage());
697
698                     channelMonitorStatus = ZoneMinderMonitorStatusEnum.UNKNOWN;
699                     channelFunction = ZoneMinderMonitorFunctionEnum.NONE;
700                     channelEnabled = false;
701                     channelEventCause = "";
702                 }
703                 if (captureDaemon.getHttpResponseCode() != 200) {
704                     channelDaemonCapture = false;
705                     logger.warn("{}: HTTP Response CaptureDaemon: Code='{}', Message'{}'", getLogIdentifier(),
706                             captureDaemon.getHttpResponseCode(), captureDaemon.getHttpResponseMessage());
707
708                 }
709                 if (analysisDaemon.getHttpResponseCode() != 200) {
710                     channelDaemonAnalysis = false;
711
712                     logger.warn("{}: HTTP Response AnalysisDaemon: Code='{}', Message='{}'", getLogIdentifier(),
713                             analysisDaemon.getHttpResponseCode(), analysisDaemon.getHttpResponseMessage());
714                 }
715                 if (frameDaemon.getHttpResponseCode() != 200) {
716                     channelDaemonFrame = false;
717                     logger.warn("{}: HTTP Response MonitorData: Code='{}', Message'{}'", getLogIdentifier(),
718                             frameDaemon.getHttpResponseCode(), frameDaemon.getHttpResponseMessage());
719                 }
720
721             } else {
722                 if (isConnected()) {
723                     channelMonitorStatus = monitorProxy.getMonitorDetailedStatus();
724                     logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
725                             monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(),
726                             monitorProxy.getHttpResponseMessage());
727
728                     channelFunction = data.getFunction();
729                     channelEnabled = data.getEnabled();
730                     IZoneMinderEventData event = monitorProxy.getLastEvent();
731                     if (event != null) {
732                         channelEventCause = event.getCause();
733                     } else {
734                         channelEventCause = "";
735                     }
736
737                     channelDaemonCapture = captureDaemon.getStatus();
738                     channelDaemonAnalysis = analysisDaemon.getStatus();
739                     channelDaemonFrame = frameDaemon.getStatus();
740                 } else {
741                     channelMonitorStatus = ZoneMinderMonitorStatusEnum.UNKNOWN;
742                     channelFunction = ZoneMinderMonitorFunctionEnum.NONE;
743                     channelEnabled = false;
744                     channelEventCause = "";
745                     channelDaemonCapture = false;
746                     channelDaemonAnalysis = false;
747                     channelDaemonFrame = false;
748                 }
749             }
750         } finally {
751             releaseSession();
752         }
753
754         recalculateChannelStates();
755
756         if (!channelForceAlarm && !channelAlarmedState
757                 && (DataRefreshPriorityEnum.HIGH_PRIORITY == getRefreshPriority())) {
758             stopPriorityRefresh();
759         }
760     }
761
762     protected State getDetailedStatus() {
763         State state = UnDefType.UNDEF;
764
765         try {
766             if (channelMonitorStatus == ZoneMinderMonitorStatusEnum.UNKNOWN) {
767                 state = getChannelStringAsStringState("");
768             } else {
769                 state = getChannelStringAsStringState(channelMonitorStatus.toString());
770             }
771
772         } catch (Exception ex) {
773             logger.debug("{}", ex.getMessage());
774         }
775
776         return state;
777     }
778
779     /*
780      * This is experimental
781      * Try to add different properties
782      */
783     private void updateMonitorProperties(IZoneMinderSession session) {
784         logger.debug("{}: Update Monitor Properties", getLogIdentifier());
785         // Update property information about this device
786         Map<String, String> properties = editProperties();
787         IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(session, getZoneMinderId());
788         IZoneMinderMonitorData monitorData = monitorProxy.getMonitorData();
789         logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
790                 monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(), monitorProxy.getHttpResponseMessage());
791
792         properties.put(ZoneMinderProperties.PROPERTY_ID, getLogIdentifier());
793         properties.put(ZoneMinderProperties.PROPERTY_MONITOR_NAME, monitorData.getName());
794
795         properties.put(ZoneMinderProperties.PROPERTY_MONITOR_SOURCETYPE, monitorData.getSourceType().name());
796
797         properties.put(ZoneMinderProperties.PROPERTY_MONITOR_ANALYSIS_FPS, monitorData.getAnalysisFPS());
798         properties.put(ZoneMinderProperties.PROPERTY_MONITOR_MAXIMUM_FPS, monitorData.getMaxFPS());
799         properties.put(ZoneMinderProperties.PROPERTY_MONITOR_ALARM_MAXIMUM, monitorData.getAlarmMaxFPS());
800
801         properties.put(ZoneMinderProperties.PROPERTY_MONITOR_IMAGE_WIDTH, monitorData.getWidth());
802         properties.put(ZoneMinderProperties.PROPERTY_MONITOR_IMAGE_HEIGHT, monitorData.getHeight());
803
804         // Must loop over the new properties since we might have added data
805         boolean update = false;
806         Map<String, String> originalProperties = editProperties();
807         for (String property : properties.keySet()) {
808             if ((originalProperties.get(property) == null
809                     || !originalProperties.get(property).equals(properties.get(property)))) {
810                 update = true;
811                 break;
812             }
813         }
814
815         if (update) {
816             logger.debug("{}: Properties synchronised", getLogIdentifier());
817             updateProperties(properties);
818         }
819     }
820
821     @Override
822     public String getLogIdentifier() {
823         String result = "[MONITOR]";
824
825         try {
826             if (config != null) {
827                 result = String.format("[MONITOR-%s]", config.getZoneMinderId().toString());
828             }
829
830         } catch (Exception ex) {
831             result = "[MONITOR]";
832         }
833
834         return result;
835     }
836 }