2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.zoneminder.internal.handler;
15 import java.io.IOException;
16 import java.math.BigDecimal;
17 import java.security.GeneralSecurityException;
18 import java.util.Collections;
22 import javax.security.auth.login.FailedLoginException;
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;
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;
57 * The {@link ZoneMinderThingMonitorHandler} is responsible for handling commands, which are
58 * sent to one of the channels.
60 * @author Martin S. Eskildsen - Initial contribution
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);
66 /** Make sure we can log errors, warnings or what ever somewhere */
67 private final Logger logger = LoggerFactory.getLogger(ZoneMinderThingMonitorHandler.class);
69 private ZoneMinderThingMonitorConfig config;
71 private ZoneMinderEvent curEvent;
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;
87 private int forceAlarmManualState = -1;
89 public ZoneMinderThingMonitorHandler(Thing thing) {
92 logger.info("{}: Starting ZoneMinder Server Thing Handler (Thing='{}')", getLogIdentifier(), thing.getUID());
96 public void dispose() {
100 public String getZoneMinderId() {
101 if (config == null) {
102 logger.error("{}: Configuration for Thing '{}' is not loaded correctly.", getLogIdentifier(),
103 getThing().getUID());
106 return config.getZoneMinderId().toString();
110 public void onBridgeConnected(ZoneMinderServerBridgeHandler bridge, IZoneMinderConnectionInfo connection)
111 throws IllegalArgumentException, GeneralSecurityException, IOException, ZoneMinderUrlNotFoundException {
113 logger.info("{}: Bridge '{}' connected", getLogIdentifier(), bridge.getThing().getUID().getAsString());
114 super.onBridgeConnected(bridge, connection);
116 ZoneMinderFactory.SubscribeMonitorEvents(connection, config.getZoneMinderId(), this);
117 IZoneMinderSession session = aquireSession();
118 IZoneMinderMonitor monitor = ZoneMinderFactory.getMonitorProxy(session, config.getZoneMinderId());
119 IZoneMinderMonitorData monitorData = monitor.getMonitorData();
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());
129 updateMonitorProperties(session);
130 } catch (Exception ex) {
131 logger.error("{}: Exception occurred when calling 'onBridgeConencted()'. Exception='{}'",
132 getLogIdentifier(), ex.getMessage());
139 public void onBridgeDisconnected(ZoneMinderServerBridgeHandler bridge) {
141 logger.info("{}: Bridge '{}' disconnected", getLogIdentifier(), bridge.getThing().getUID().getAsString());
143 logger.info("{}: Unsubscribing from Monitor Events: {}", getLogIdentifier(),
144 bridge.getThing().getUID().getAsString());
145 ZoneMinderFactory.UnsubscribeMonitorEvents(config.getZoneMinderId(), this);
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());
156 public void handleCommand(ChannelUID channelUID, Command command) {
158 logger.debug("{}: Channel '{}' in monitor '{}' received command='{}'", getLogIdentifier(), channelUID,
159 getZoneMinderId(), command);
161 // Allow refresh of channels
162 if (command == RefreshType.REFRESH) {
163 updateChannel(channelUID);
167 // Communication TO Monitor
168 switch (channelUID.getId()) {
169 // Done via Telnet connection
170 case ZoneMinderConstants.CHANNEL_MONITOR_FORCE_ALARM:
172 "{}: 'handleCommand' => CHANNEL_MONITOR_FORCE_ALARM: Command '{}' received for monitor '{}'",
173 getLogIdentifier(), command, channelUID.getId());
175 if ((command == OnOffType.OFF) || (command == OnOffType.ON)) {
176 String eventText = getConfigValueAsString(ZoneMinderConstants.PARAMETER_MONITOR_EVENTTEXT);
178 BigDecimal eventTimeout = getConfigValueAsBigDecimal(
179 ZoneMinderConstants.PARAMETER_MONITOR_TRIGGER_TIMEOUT);
181 ZoneMinderServerBridgeHandler bridge = getZoneMinderBridgeHandler();
182 if (bridge == null) {
183 logger.warn("'handleCommand()': Bridge is 'null'!");
186 IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(),
189 if (command == OnOffType.ON) {
190 forceAlarmManualState = 1;
191 logger.info("{}: Activate 'ForceAlarm' to '{}' (Reason='{}', Timeout='{}')",
192 getLogIdentifier(), command, eventText, eventTimeout.intValue());
194 monitorProxy.activateForceAlarm(255, ZoneMinderConstants.MONITOR_EVENT_OPENHAB,
195 eventText, "", eventTimeout.intValue());
199 else if (command == OnOffType.OFF) {
200 forceAlarmManualState = 0;
201 logger.info("{}: Cancel 'ForceAlarm'", getLogIdentifier());
202 monitorProxy.deactivateForceAlarm();
210 recalculateChannelStates();
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);
219 startPriorityRefresh();
223 case ZoneMinderConstants.CHANNEL_MONITOR_ENABLED:
225 "{}: 'handleCommand' => CHANNEL_MONITOR_ENABLED: Command '{}' received for monitor '{}'",
226 getLogIdentifier(), command, channelUID.getId());
228 if ((command == OnOffType.OFF) || (command == OnOffType.ON)) {
229 boolean newState = ((command == OnOffType.ON) ? true : false);
231 IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(),
234 monitorProxy.SetEnabled(newState);
239 channelEnabled = newState;
241 logger.info("{}: Setting enabled to '{}'", getLogIdentifier(), command);
244 handleCommand(channelUID, RefreshType.REFRESH);
247 case ZoneMinderConstants.CHANNEL_MONITOR_FUNCTION:
248 String commandString = "";
249 if (ZoneMinderMonitorFunctionEnum.isValid(command.toString())) {
250 commandString = ZoneMinderMonitorFunctionEnum.getEnum(command.toString()).toString();
252 IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(),
255 monitorProxy.SetFunction(commandString);
260 // Make sure local copy is set to new value
261 channelFunction = ZoneMinderMonitorFunctionEnum.getEnum(command.toString());
263 logger.info("{}: Setting function to '{}'", getLogIdentifier(), commandString);
267 "{}: Value '{}' for monitor channel is not valid. Accepted values is: 'None', 'Monitor', 'Modect', Record', 'Mocord', 'Nodect'",
268 getLogIdentifier(), commandString);
270 handleCommand(channelUID, RefreshType.REFRESH);
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
285 logger.warn("{}: Command received for an unknown channel: {}", getLogIdentifier(),
289 } catch (Exception ex) {
290 logger.error("{}: handleCommand: Command='{}' failed for channel='{}' Exception='{}'", getLogIdentifier(),
291 command, channelUID.getId(), ex.getMessage());
296 public void 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(),
309 public void onTrippedForceAlarm(ZoneMinderTriggerEvent event) {
311 logger.info("{}: Received forceAlarm for monitor {}", getLogIdentifier(), event.getMonitorId());
313 // Set Current Event to actual event
314 if (event.getState()) {
315 startPriorityRefresh();
320 } catch (Exception ex) {
321 logger.error("{}: Exception occurred inTrippedForceAlarm() Exception='{}'", getLogIdentifier(),
326 protected ZoneMinderThingMonitorConfig getMonitorConfig() {
327 return this.getConfigAs(ZoneMinderThingMonitorConfig.class);
331 protected String getZoneMinderThingType() {
332 return ZoneMinderConstants.THING_ZONEMINDER_MONITOR;
336 public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) {
338 ThingStatus newThingStatus = ThingStatus.ONLINE;
339 ThingStatusDetail thingStatusDetailed = ThingStatusDetail.NONE;
340 String thingStatusDescription = "";
342 ThingStatus curThingStatus = this.getThing().getStatus();
344 // Is connected to ZoneMinder and thing is ONLINE
345 if (isConnected() && curThingStatus == ThingStatus.ONLINE) {
346 updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
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);
364 final Bridge bridge = getBridge();
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);
376 logger.debug("{}: ThingAvailability: Thing '{}' has Bridge '{}' defined (Check PASSED)",
377 getLogIdentifier(), thing.getUID(), bridge.getBridgeUID());
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);
390 logger.debug("{}: ThingAvailability: Bridge '{}' is ONLINE (Check PASSED)", getLogIdentifier(),
391 bridge.getBridgeUID());
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);
402 logger.error("{}: {}", getLogIdentifier(), msg);
405 logger.debug("{}: ThingAvailability: Thing '{}' has valid configuration (Check PASSED)",
406 getLogIdentifier(), thing.getUID());
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);
417 logger.error("{}: {}", getLogIdentifier(), msg);
420 logger.debug("{}: ThingAvailability: ZoneMinder Id for Thing '{}' defined (Check PASSED)",
421 getLogIdentifier(), thing.getUID());
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();
429 IZoneMinderSession curSession = null;
431 curSession = ZoneMinderFactory.CreateSession(connection);
432 } catch (FailedLoginException | IllegalArgumentException | IOException
433 | ZoneMinderUrlNotFoundException ex) {
434 logger.error("{}: Create Session failed with exception {}", getLogIdentifier(), ex.getMessage());
436 newThingStatus = ThingStatus.OFFLINE;
437 thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR;
438 thingStatusDescription = "Failed to connect. (Check Log)";
440 updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
444 if (curSession != null) {
445 monitorProxy = ZoneMinderFactory.getMonitorProxy(curSession, getZoneMinderId());
447 captureDaemon = monitorProxy.getCaptureDaemonStatus();
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);
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);
467 newThingStatus = ThingStatus.ONLINE;
469 } catch (Exception exception) {
470 newThingStatus = ThingStatus.OFFLINE;
471 thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR;
472 thingStatusDescription = "Error occurred (Check log)";
473 updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
475 logger.error("{}: 'ThingMonitorHandler.updateAvailabilityStatus()': Exception occurred '{}'",
476 getLogIdentifier(), exception.getMessage());
481 updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
485 * From here we update states in openHAB
488 * org.openhab.binding.zoneminder.handler.ZoneMinderBaseThingHandler#updateChannel(org.openhab.core.thing.
492 public void updateChannel(ChannelUID channel) {
496 switch (channel.getId()) {
497 case ZoneMinderConstants.CHANNEL_MONITOR_ENABLED:
498 state = getChannelBoolAsOnOffState(channelEnabled);
501 case ZoneMinderConstants.CHANNEL_ONLINE:
502 // Ask super class to handle, because this channel is shared for all things
503 super.updateChannel(channel);
505 case ZoneMinderConstants.CHANNEL_MONITOR_FORCE_ALARM:
506 state = getChannelBoolAsOnOffState(channelForceAlarm);
508 case ZoneMinderConstants.CHANNEL_MONITOR_EVENT_STATE:
509 state = getChannelBoolAsOnOffState(channelAlarmedState);
512 case ZoneMinderConstants.CHANNEL_MONITOR_RECORD_STATE:
513 state = getChannelBoolAsOnOffState(channelRecordingState);
516 case ZoneMinderConstants.CHANNEL_MONITOR_DETAILED_STATUS:
517 state = getDetailedStatus();
520 case ZoneMinderConstants.CHANNEL_MONITOR_EVENT_CAUSE:
521 state = getChannelStringAsStringState(channelEventCause);
524 case ZoneMinderConstants.CHANNEL_MONITOR_FUNCTION:
525 state = getChannelStringAsStringState(channelFunction.toString());
528 case ZoneMinderConstants.CHANNEL_MONITOR_CAPTURE_DAEMON_STATE:
529 state = getChannelBoolAsOnOffState(channelDaemonCapture);
532 case ZoneMinderConstants.CHANNEL_MONITOR_ANALYSIS_DAEMON_STATE:
533 state = getChannelBoolAsOnOffState(channelDaemonAnalysis);
536 case ZoneMinderConstants.CHANNEL_MONITOR_FRAME_DAEMON_STATE:
537 state = getChannelBoolAsOnOffState(channelDaemonFrame);
541 logger.warn("{}: updateChannel(): Monitor '{}': No handler defined for channel='{}'",
542 getLogIdentifier(), thing.getLabel(), channel.getAsString());
544 // Ask super class to handle
545 super.updateChannel(channel);
549 logger.debug("{}: Setting channel '{}' to '{}'", getLogIdentifier(), channel.toString(),
551 updateState(channel.getId(), state);
553 } catch (Exception ex) {
554 logger.error("{}: Error when 'updateChannel' was called (channelId='{}'state='{}', exception'{}')",
555 getLogIdentifier(), channel, state, ex.getMessage());
560 public void updateStatus(ThingStatus status) {
561 super.updateStatus(status);
562 updateState(ZoneMinderConstants.CHANNEL_ONLINE,
563 ((status == ThingStatus.ONLINE) ? OnOffType.ON : OnOffType.OFF));
566 protected void recalculateChannelStates() {
567 boolean recordingFunction = false;
568 boolean recordingDetailedState = false;
569 boolean alarmedFunction = false;
570 boolean alarmedDetailedState = false;
572 // Calculate based on state of Function
573 switch (channelFunction) {
576 alarmedFunction = false;
577 recordingFunction = false;
581 alarmedFunction = true;
582 recordingFunction = true;
585 alarmedFunction = false;
586 recordingFunction = true;
589 alarmedFunction = true;
590 recordingFunction = true;
593 alarmedFunction = false;
594 recordingFunction = true;
597 recordingFunction = (curEvent != null) ? true : false;
600 "{}: Recalculate channel states based on Function: Function='{}' -> alarmState='{}', recordingState='{}'",
601 getLogIdentifier(), channelFunction.name(), alarmedFunction, recordingFunction);
603 // Calculated based on detailed Monitor Status
604 switch (channelMonitorStatus) {
606 alarmedDetailedState = false;
607 recordingDetailedState = false;
608 channelForceAlarm = false;
609 channelEventCause = "";
612 alarmedDetailedState = true;
613 recordingDetailedState = true;
614 channelForceAlarm = false;
617 alarmedDetailedState = true;
618 recordingDetailedState = true;
619 channelForceAlarm = true;
622 alarmedDetailedState = true;
623 recordingDetailedState = true;
624 channelForceAlarm = false;
627 alarmedDetailedState = false;
628 recordingDetailedState = true;
629 channelForceAlarm = false;
635 "{}: Recalculate channel states based on Detailed State: DetailedState='{}' -> alarmState='{}', recordingState='{}'",
636 getLogIdentifier(), channelMonitorStatus.name(), alarmedDetailedState, recordingDetailedState);
638 // Check if Force alarm was initialed from openHAB
639 if (forceAlarmManualState == 0) {
640 if (channelForceAlarm) {
641 channelForceAlarm = false;
643 forceAlarmManualState = -1;
645 } else if (forceAlarmManualState == 1) {
646 if (!channelForceAlarm) {
647 channelForceAlarm = true;
649 forceAlarmManualState = -1;
654 // Now we can conclude on the Alarmed and Recording channel state
655 channelRecordingState = (recordingFunction && recordingDetailedState && channelEnabled);
656 channelAlarmedState = (alarmedFunction && alarmedDetailedState && channelEnabled);
660 protected void onFetchData() {
661 IZoneMinderSession session = null;
663 session = aquireSession();
665 IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(session, getZoneMinderId());
667 IZoneMinderMonitorData data = null;
668 IZoneMinderDaemonStatus captureDaemon = null;
669 IZoneMinderDaemonStatus analysisDaemon = null;
670 IZoneMinderDaemonStatus frameDaemon = null;
672 data = monitorProxy.getMonitorData();
673 logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
674 monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(),
675 monitorProxy.getHttpResponseMessage());
677 captureDaemon = monitorProxy.getCaptureDaemonStatus();
678 logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
679 monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(),
680 monitorProxy.getHttpResponseMessage());
682 analysisDaemon = monitorProxy.getAnalysisDaemonStatus();
683 logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
684 monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(),
685 monitorProxy.getHttpResponseMessage());
687 frameDaemon = monitorProxy.getFrameDaemonStatus();
688 logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
689 monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(),
690 monitorProxy.getHttpResponseMessage());
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());
698 channelMonitorStatus = ZoneMinderMonitorStatusEnum.UNKNOWN;
699 channelFunction = ZoneMinderMonitorFunctionEnum.NONE;
700 channelEnabled = false;
701 channelEventCause = "";
703 if (captureDaemon.getHttpResponseCode() != 200) {
704 channelDaemonCapture = false;
705 logger.warn("{}: HTTP Response CaptureDaemon: Code='{}', Message'{}'", getLogIdentifier(),
706 captureDaemon.getHttpResponseCode(), captureDaemon.getHttpResponseMessage());
709 if (analysisDaemon.getHttpResponseCode() != 200) {
710 channelDaemonAnalysis = false;
712 logger.warn("{}: HTTP Response AnalysisDaemon: Code='{}', Message='{}'", getLogIdentifier(),
713 analysisDaemon.getHttpResponseCode(), analysisDaemon.getHttpResponseMessage());
715 if (frameDaemon.getHttpResponseCode() != 200) {
716 channelDaemonFrame = false;
717 logger.warn("{}: HTTP Response MonitorData: Code='{}', Message'{}'", getLogIdentifier(),
718 frameDaemon.getHttpResponseCode(), frameDaemon.getHttpResponseMessage());
723 channelMonitorStatus = monitorProxy.getMonitorDetailedStatus();
724 logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
725 monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(),
726 monitorProxy.getHttpResponseMessage());
728 channelFunction = data.getFunction();
729 channelEnabled = data.getEnabled();
730 IZoneMinderEventData event = monitorProxy.getLastEvent();
732 channelEventCause = event.getCause();
734 channelEventCause = "";
737 channelDaemonCapture = captureDaemon.getStatus();
738 channelDaemonAnalysis = analysisDaemon.getStatus();
739 channelDaemonFrame = frameDaemon.getStatus();
741 channelMonitorStatus = ZoneMinderMonitorStatusEnum.UNKNOWN;
742 channelFunction = ZoneMinderMonitorFunctionEnum.NONE;
743 channelEnabled = false;
744 channelEventCause = "";
745 channelDaemonCapture = false;
746 channelDaemonAnalysis = false;
747 channelDaemonFrame = false;
754 recalculateChannelStates();
756 if (!channelForceAlarm && !channelAlarmedState
757 && (DataRefreshPriorityEnum.HIGH_PRIORITY == getRefreshPriority())) {
758 stopPriorityRefresh();
762 protected State getDetailedStatus() {
763 State state = UnDefType.UNDEF;
766 if (channelMonitorStatus == ZoneMinderMonitorStatusEnum.UNKNOWN) {
767 state = getChannelStringAsStringState("");
769 state = getChannelStringAsStringState(channelMonitorStatus.toString());
772 } catch (Exception ex) {
773 logger.debug("{}", ex.getMessage());
780 * This is experimental
781 * Try to add different properties
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());
792 properties.put(ZoneMinderProperties.PROPERTY_ID, getLogIdentifier());
793 properties.put(ZoneMinderProperties.PROPERTY_MONITOR_NAME, monitorData.getName());
795 properties.put(ZoneMinderProperties.PROPERTY_MONITOR_SOURCETYPE, monitorData.getSourceType().name());
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());
801 properties.put(ZoneMinderProperties.PROPERTY_MONITOR_IMAGE_WIDTH, monitorData.getWidth());
802 properties.put(ZoneMinderProperties.PROPERTY_MONITOR_IMAGE_HEIGHT, monitorData.getHeight());
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)))) {
816 logger.debug("{}: Properties synchronised", getLogIdentifier());
817 updateProperties(properties);
822 public String getLogIdentifier() {
823 String result = "[MONITOR]";
826 if (config != null) {
827 result = String.format("[MONITOR-%s]", config.getZoneMinderId().toString());
830 } catch (Exception ex) {
831 result = "[MONITOR]";