added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.zoneminder-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-zoneminder" description="ZoneMinder Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.zoneminder/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zoneminder.internal;
/**
*
* @author Martin S. Eskildsen - Initial contribution
*/
public enum DataRefreshPriorityEnum {
SCHEDULED,
HIGH_PRIORITY
}

View File

@@ -0,0 +1,104 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zoneminder.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link ZoneMinderConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Martin S. Eskildsen - Initial contribution
*/
@NonNullByDefault
public class ZoneMinderConstants {
public static final String BINDING_ID = "zoneminder";
// ZoneMinder Server Bridge
public static final String BRIDGE_ZONEMINDER_SERVER = "server";
// ZoneMinder Monitor thing
public static final String THING_ZONEMINDER_MONITOR = "monitor";
// ZoneMinder Server displayable name
public static final String ZONEMINDER_SERVER_NAME = "ZoneMinder Server";
// ZoneMinder Monitor displayable name
public static final String ZONEMINDER_MONITOR_NAME = "ZoneMinder Monitor";
/*
* ZoneMinder Server Constants
*/
// Thing Type UID for Server
public static final ThingTypeUID THING_TYPE_BRIDGE_ZONEMINDER_SERVER = new ThingTypeUID(BINDING_ID,
BRIDGE_ZONEMINDER_SERVER);
// Shared channel for all bridges / things
public static final String CHANNEL_ONLINE = "online";
// Channel Id's for the ZoneMinder Server
public static final String CHANNEL_SERVER_DISKUSAGE = "disk-usage";
public static final String CHANNEL_SERVER_CPULOAD = "cpu-load";
// Parameters for the ZoneMinder Server
public static final String PARAM_HOSTNAME = "hostname";
public static final String PARAM_PORT = "port";
public static final String PARAM_REFRESH_INTERVAL = "refresh_interval";
public static final String PARAM_REFRESH_INTERVAL_DISKUSAGE = "refresh_interval_disk_usage";
// Default values for Monitor parameters
public static final Integer DEFAULT_HTTP_PORT = 80;
public static final Integer DEFAULT_TELNET_PORT = 6802;
/*
* ZoneMinder Monitor Constants
*/
// Thing Type UID for Monitor
public static final ThingTypeUID THING_TYPE_THING_ZONEMINDER_MONITOR = new ThingTypeUID(BINDING_ID,
THING_ZONEMINDER_MONITOR);
/*
* Channel Id's for the ZoneMinder Monitor
*/
public static final String CHANNEL_MONITOR_ENABLED = "enabled";
public static final String CHANNEL_MONITOR_FORCE_ALARM = "force-alarm";
public static final String CHANNEL_MONITOR_EVENT_STATE = "alarm";
public static final String CHANNEL_MONITOR_EVENT_CAUSE = "event-cause";
public static final String CHANNEL_MONITOR_RECORD_STATE = "recording";
public static final String CHANNEL_MONITOR_DETAILED_STATUS = "detailed-status";
public static final String CHANNEL_MONITOR_FUNCTION = "function";
public static final String CHANNEL_MONITOR_CAPTURE_DAEMON_STATE = "capture-daemon";
public static final String CHANNEL_MONITOR_ANALYSIS_DAEMON_STATE = "analysis-daemon";
public static final String CHANNEL_MONITOR_FRAME_DAEMON_STATE = "frame-daemon";
// Parameters for the ZoneMinder Monitor
public static final String PARAMETER_MONITOR_ID = "monitorId";
public static final String PARAMETER_MONITOR_TRIGGER_TIMEOUT = "monitorTriggerTimeout";
public static final String PARAMETER_MONITOR_EVENTTEXT = "monitorEventText";
// Default values for Monitor parameters
public static final Integer PARAMETER_MONITOR_TRIGGER_TIMEOUT_DEFAULTVALUE = 60;
public static final String PARAMETER_MONITOR_EVENTNOTE_DEFAULTVALUE = "openHAB triggered event";
// ZoneMinder Event types
public static final String MONITOR_EVENT_NONE = "";
public static final String MONITOR_EVENT_SIGNAL = "Signal";
public static final String MONITOR_EVENT_MOTION = "Motion";
public static final String MONITOR_EVENT_FORCED_WEB = "Forced Web";
public static final String MONITOR_EVENT_OPENHAB = "openHAB";
}

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zoneminder.internal;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.openhab.binding.zoneminder.internal.handler.ZoneMinderServerBridgeHandler;
import org.openhab.binding.zoneminder.internal.handler.ZoneMinderThingMonitorHandler;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link ZoneMinderHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Martin S. Eskildsen - Initial contribution
*
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.zoneminder")
public class ZoneMinderHandlerFactory extends BaseThingHandlerFactory {
private final Logger logger = LoggerFactory.getLogger(ZoneMinderHandlerFactory.class);
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections
.unmodifiableSet(Stream.concat(ZoneMinderServerBridgeHandler.SUPPORTED_THING_TYPES.stream(),
ZoneMinderThingMonitorHandler.SUPPORTED_THING_TYPES.stream()).collect(Collectors.toSet()));
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES.contains(thingTypeUID);
}
@Override
protected ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(ZoneMinderConstants.THING_TYPE_BRIDGE_ZONEMINDER_SERVER)) {
logger.debug("[FACTORY]: creating handler for bridge thing '{}'", thing);
return new ZoneMinderServerBridgeHandler((Bridge) thing);
} else if (thingTypeUID.equals(ZoneMinderConstants.THING_TYPE_THING_ZONEMINDER_MONITOR)) {
return new ZoneMinderThingMonitorHandler(thing);
}
return null;
}
}

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zoneminder.internal;
/**
*
* @author Martin S. Eskildsen - Initial contribution
*/
public class ZoneMinderProperties {
public static final String PROPERTY_ID = "Id";
public static final String PROPERTY_SERVER_VERSION = "Version";
public static final String PROPERTY_SERVER_API_VERSION = "API Version";
public static final String PROPERTY_SERVER_USE_API = "API Enabled";
public static final String PROPERTY_SERVER_USE_AUTHENTIFICATION = "Use Authentification";
public static final String PROPERTY_SERVER_TRIGGERS_ENABLED = "Triggers enabled";
public static final String PROPERTY_MONITOR_NAME = "Name";
public static final String PROPERTY_MONITOR_SOURCETYPE = "Sourcetype";
public static final String PROPERTY_MONITOR_ANALYSIS_FPS = "Analysis FPS";
public static final String PROPERTY_MONITOR_MAXIMUM_FPS = "Maximum FPS";
public static final String PROPERTY_MONITOR_ALARM_MAXIMUM = "Alarm Maximum FPS";
public static final String PROPERTY_MONITOR_IMAGE_WIDTH = "Width";
public static final String PROPERTY_MONITOR_IMAGE_HEIGHT = "Height";
}

View File

@@ -0,0 +1,129 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zoneminder.internal.config;
import org.openhab.binding.zoneminder.internal.ZoneMinderConstants;
/**
* Configuration data according to zoneminderserver.xml
*
* @author Martin S. Eskildsen - Initial contribution
*/
public class ZoneMinderBridgeServerConfig extends ZoneMinderConfig {
private String hostname;
private Integer http_port;
private Integer telnet_port;
private String protocol;
private String urlpath;
private String user;
private String password;
private Integer refresh_interval;
private Integer refresh_interval_disk_usage;
private Boolean autodiscover_things;
@Override
public String getConfigId() {
return ZoneMinderConstants.BRIDGE_ZONEMINDER_SERVER;
}
public String getHostName() {
return hostname;
}
public void setHostName(String hostName) {
this.hostname = hostName;
}
public Integer getHttpPort() {
if ((http_port == null) || (http_port == 0)) {
if (getProtocol().equalsIgnoreCase("http")) {
http_port = 80;
} else {
http_port = 443;
}
}
return http_port;
}
public void setHttpPort(Integer port) {
this.http_port = port;
}
public Integer getTelnetPort() {
return telnet_port;
}
public void setTelnetPort(Integer telnetPort) {
this.telnet_port = telnetPort;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getServerBasePath() {
return urlpath;
}
public void setServerBasePath(String urlpath) {
this.urlpath = urlpath;
}
public String getUserName() {
return user;
}
public void setUserName(String userName) {
this.user = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getRefreshInterval() {
return refresh_interval;
}
public void setRefreshInterval(Integer refreshInterval) {
this.refresh_interval = refreshInterval;
}
public Integer getRefreshIntervalLowPriorityTask() {
return refresh_interval_disk_usage;
}
public void setRefreshIntervalDiskUsage(Integer refreshIntervalDiskUsage) {
this.refresh_interval_disk_usage = refreshIntervalDiskUsage;
}
public Boolean getAutodiscoverThings() {
return autodiscover_things;
}
public void setAutodiscoverThings(Boolean autodiscoverThings) {
this.autodiscover_things = autodiscoverThings;
}
}

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zoneminder.internal.config;
/**
* base class containing Configuration in openHAB
*
* @author Martin S. Eskildsen - Initial contribution
*/
public abstract class ZoneMinderConfig {
public abstract String getConfigId();
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zoneminder.internal.config;
import org.openhab.binding.zoneminder.internal.ZoneMinderConstants;
/**
* Monitor configuration from openHAB.
*
* @author Martin S. Eskildsen - Initial contribution
*/
public abstract class ZoneMinderThingConfig extends ZoneMinderConfig {
public abstract String getZoneMinderId();
@Override
public String getConfigId() {
return ZoneMinderConstants.THING_ZONEMINDER_MONITOR;
}
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zoneminder.internal.config;
import org.openhab.binding.zoneminder.internal.ZoneMinderConstants;
/**
* Specific configuration class for Monitor COnfig.
*
* @author Martin S. Eskildsen - Initial contribution
*/
public class ZoneMinderThingMonitorConfig extends ZoneMinderThingConfig {
// Parameters
private Integer monitorId;
@Override
public String getConfigId() {
return ZoneMinderConstants.THING_ZONEMINDER_MONITOR;
}
public String getId() {
return monitorId.toString();
}
@Override
public String getZoneMinderId() {
return monitorId.toString();
}
}

View File

@@ -0,0 +1,152 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zoneminder.internal.discovery;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.zoneminder.internal.ZoneMinderConstants;
import org.openhab.binding.zoneminder.internal.handler.ZoneMinderServerBridgeHandler;
import org.openhab.binding.zoneminder.internal.handler.ZoneMinderThingMonitorHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import name.eskildsen.zoneminder.IZoneMinderMonitorData;
/**
*
* @author Martin S. Eskildsen - Initial contribution
*/
public class ZoneMinderDiscoveryService extends AbstractDiscoveryService
implements DiscoveryService, ThingHandlerService {
private final Logger logger = LoggerFactory.getLogger(ZoneMinderDiscoveryService.class);
private @NonNullByDefault({}) ZoneMinderServerBridgeHandler serverHandler;
public ZoneMinderDiscoveryService() {
super(30);
}
@Override
public void setThingHandler(@Nullable ThingHandler handler) {
if (handler instanceof ZoneMinderServerBridgeHandler) {
this.serverHandler = (ZoneMinderServerBridgeHandler) handler;
this.serverHandler.setDiscoveryService(this);
}
}
@Override
public @Nullable ThingHandler getThingHandler() {
return serverHandler;
}
@Override
public void activate() {
logger.debug("[DISCOVERY]: Activating ZoneMinder discovery service for {}", serverHandler.getThing().getUID());
}
@Override
public void deactivate() {
logger.debug("[DISCOVERY]: Deactivating ZoneMinder discovery service for {}",
serverHandler.getThing().getUID());
}
@Override
public Set<ThingTypeUID> getSupportedThingTypes() {
return ZoneMinderThingMonitorHandler.SUPPORTED_THING_TYPES;
}
@Override
public void startBackgroundDiscovery() {
logger.debug("[DISCOVERY]: Performing background discovery scan for {}", serverHandler.getThing().getUID());
discoverMonitors();
}
@Override
public void startScan() {
logger.debug("[DISCOVERY]: Starting discovery scan for {}", serverHandler.getThing().getUID());
discoverMonitors();
}
@Override
public synchronized void abortScan() {
super.abortScan();
}
@Override
protected synchronized void stopScan() {
super.stopScan();
}
protected String buildMonitorLabel(String id, String name) {
return String.format("%s [%s]", ZoneMinderConstants.ZONEMINDER_MONITOR_NAME, name);
}
protected synchronized void discoverMonitors() {
// Add all existing devices
for (IZoneMinderMonitorData monitor : serverHandler.getMonitors()) {
deviceAdded(monitor);
}
}
private boolean monitorThingExists(ThingUID newThingUID) {
return serverHandler.getThing().getThing(newThingUID) != null ? true : false;
}
/**
* This is called once the node is fully discovered. At this point we know most of the information about
* the device including manufacturer information.
*
* @param node the node to be added
*/
public void deviceAdded(IZoneMinderMonitorData monitor) {
try {
ThingUID bridgeUID = serverHandler.getThing().getUID();
String monitorUID = String.format("%s-%s", ZoneMinderConstants.THING_ZONEMINDER_MONITOR, monitor.getId());
ThingUID thingUID = new ThingUID(ZoneMinderConstants.THING_TYPE_THING_ZONEMINDER_MONITOR, bridgeUID,
monitorUID);
// Does Monitor exist?
if (!monitorThingExists(thingUID)) {
logger.info("[DISCOVERY]: Monitor with Id='{}' and Name='{}' added", monitor.getId(),
monitor.getName());
Map<String, Object> properties = new HashMap<>(0);
properties.put(ZoneMinderConstants.PARAMETER_MONITOR_ID, Integer.valueOf(monitor.getId()));
properties.put(ZoneMinderConstants.PARAMETER_MONITOR_TRIGGER_TIMEOUT,
ZoneMinderConstants.PARAMETER_MONITOR_TRIGGER_TIMEOUT_DEFAULTVALUE);
properties.put(ZoneMinderConstants.PARAMETER_MONITOR_EVENTTEXT,
ZoneMinderConstants.MONITOR_EVENT_OPENHAB);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withBridge(bridgeUID).withLabel(buildMonitorLabel(monitor.getId(), monitor.getName())).build();
thingDiscovered(discoveryResult);
}
} catch (Exception ex) {
logger.error("[DISCOVERY]: Error occurred when calling 'monitorAdded' from Discovery. Exception={}",
ex.getMessage());
}
}
}

View File

@@ -0,0 +1,394 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zoneminder.internal.handler;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.GeneralSecurityException;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.openhab.binding.zoneminder.internal.DataRefreshPriorityEnum;
import org.openhab.binding.zoneminder.internal.ZoneMinderConstants;
import org.openhab.binding.zoneminder.internal.config.ZoneMinderThingConfig;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import name.eskildsen.zoneminder.IZoneMinderConnectionInfo;
import name.eskildsen.zoneminder.IZoneMinderSession;
import name.eskildsen.zoneminder.ZoneMinderFactory;
import name.eskildsen.zoneminder.exception.ZoneMinderUrlNotFoundException;
/**
* The {@link ZoneMinderBaseThingHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Martin S. Eskildsen - Initial contribution
*/
public abstract class ZoneMinderBaseThingHandler extends BaseThingHandler implements ZoneMinderHandler {
/** Logger for the Thing. */
private final Logger logger = LoggerFactory.getLogger(ZoneMinderBaseThingHandler.class);
/** Bridge Handler for the Thing. */
public ZoneMinderServerBridgeHandler zoneMinderBridgeHandler;
/** This refresh status. */
private boolean thingRefreshed;
private Lock lockSession = new ReentrantLock();
private IZoneMinderSession zoneMinderSession;
/** Configuration from openHAB */
protected ZoneMinderThingConfig configuration;
private DataRefreshPriorityEnum refreshPriority = DataRefreshPriorityEnum.SCHEDULED;
protected boolean isOnline() {
if (zoneMinderSession == null) {
return false;
}
if (!zoneMinderSession.isConnected()) {
return false;
}
return true;
}
public DataRefreshPriorityEnum getRefreshPriority() {
return refreshPriority;
}
public ZoneMinderBaseThingHandler(Thing thing) {
super(thing);
}
/**
* Initializes the monitor.
*
* @author Martin S. Eskildsen
*
*/
@Override
public void initialize() {
updateStatus(ThingStatus.ONLINE);
}
protected boolean isConnected() {
if (zoneMinderSession == null) {
return false;
}
return zoneMinderSession.isConnected();
}
protected IZoneMinderSession aquireSession() {
lockSession.lock();
return zoneMinderSession;
}
protected void releaseSession() {
lockSession.unlock();
}
/**
* Method to start a priority data refresh task.
*/
protected boolean startPriorityRefresh() {
logger.info("[MONITOR-{}]: Starting High Priority Refresh", getZoneMinderId());
refreshPriority = DataRefreshPriorityEnum.HIGH_PRIORITY;
return true;
}
/**
* Method to stop the data Refresh task.
*/
protected void stopPriorityRefresh() {
logger.info("{}: Stopping Priority Refresh for Monitor", getLogIdentifier());
refreshPriority = DataRefreshPriorityEnum.SCHEDULED;
}
@Override
public void dispose() {
}
/**
* Helper method for getting ChannelUID from ChannelId.
*
*/
public ChannelUID getChannelUIDFromChannelId(String id) {
Channel ch = thing.getChannel(id);
if (ch == null) {
return null;
} else {
return ch.getUID();
}
}
protected abstract void onFetchData();
/**
* Method to Refresh Thing Handler.
*/
public final synchronized void refreshThing(IZoneMinderSession session, DataRefreshPriorityEnum refreshPriority) {
if ((refreshPriority != getRefreshPriority()) && (!isConnected())) {
return;
}
if (refreshPriority == DataRefreshPriorityEnum.HIGH_PRIORITY) {
logger.debug("{}: Performing HIGH PRIORITY refresh", getLogIdentifier());
} else {
logger.debug("{}: Performing refresh", getLogIdentifier());
}
if (getZoneMinderBridgeHandler() != null) {
if (isConnected()) {
logger.debug("{}: refreshThing(): Bridge '{}' Found for Thing '{}'!", getLogIdentifier(),
getThing().getUID(), this.getThing().getUID());
onFetchData();
}
}
Thing thing = getThing();
List<Channel> channels = thing.getChannels();
logger.debug("{}: refreshThing(): Refreshing Thing - {}", getLogIdentifier(), thing.getUID());
for (Channel channel : channels) {
updateChannel(channel.getUID());
}
this.setThingRefreshed(true);
logger.debug("[{}: refreshThing(): Thing Refreshed - {}", getLogIdentifier(), thing.getUID());
}
/**
* Get the Bridge Handler for ZoneMinder.
*
* @return zoneMinderBridgeHandler
*/
public synchronized ZoneMinderServerBridgeHandler getZoneMinderBridgeHandler() {
if (this.zoneMinderBridgeHandler == null) {
Bridge bridge = getBridge();
if (bridge == null) {
logger.debug("{}: getZoneMinderBridgeHandler(): Unable to get bridge!", getLogIdentifier());
return null;
}
logger.debug("{}: getZoneMinderBridgeHandler(): Bridge for '{}' - '{}'", getLogIdentifier(),
getThing().getUID(), bridge.getUID());
ThingHandler handler = null;
try {
handler = bridge.getHandler();
} catch (Exception ex) {
logger.debug("{}: Exception in 'getZoneMinderBridgeHandler()': {}", getLogIdentifier(),
ex.getMessage());
}
if (handler instanceof ZoneMinderServerBridgeHandler) {
this.zoneMinderBridgeHandler = (ZoneMinderServerBridgeHandler) handler;
} else {
logger.debug("{}: getZoneMinderBridgeHandler(): Unable to get bridge handler!", getLogIdentifier());
}
}
return this.zoneMinderBridgeHandler;
}
/**
* Method to Update a Channel
*
* @param channel
*/
@Override
public void updateChannel(ChannelUID channel) {
switch (channel.getId()) {
case ZoneMinderConstants.CHANNEL_ONLINE:
updateState(channel, getChannelBoolAsOnOffState(isOnline()));
break;
default:
logger.error(
"{}: updateChannel() in base class, called for an unknown channel '{}', this channel must be handled in super class.",
getLogIdentifier(), channel.getId());
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
@Override
public void onBridgeConnected(ZoneMinderServerBridgeHandler bridge, IZoneMinderConnectionInfo connection)
throws IllegalArgumentException, GeneralSecurityException, IOException, ZoneMinderUrlNotFoundException {
lockSession.lock();
try {
zoneMinderSession = ZoneMinderFactory.CreateSession(connection);
} finally {
lockSession.unlock();
}
}
@Override
public void onBridgeDisconnected(ZoneMinderServerBridgeHandler bridge) {
if (bridge.getThing().getUID().equals(getThing().getBridgeUID())) {
this.setThingRefreshed(false);
}
lockSession.lock();
try {
zoneMinderSession = null;
} finally {
lockSession.unlock();
}
}
/**
* Get Channel by ChannelUID.
*
* @param {ChannelUID} channelUID Identifier of Channel
*/
public Channel getChannel(ChannelUID channelUID) {
Channel channel = null;
List<Channel> channels = getThing().getChannels();
for (Channel ch : channels) {
if (channelUID == ch.getUID()) {
channel = ch;
break;
}
}
return channel;
}
/**
* Get Thing Handler refresh status.
*
* @return thingRefresh
*/
public boolean isThingRefreshed() {
return thingRefreshed;
}
/**
* Set Thing Handler refresh status.
*
* @param {boolean} refreshed Sets status refreshed of thing
*/
public void setThingRefreshed(boolean refreshed) {
this.thingRefreshed = refreshed;
}
protected abstract String getZoneMinderThingType();
private Object getConfigValue(String configKey) {
return getThing().getConfiguration().getProperties().get(configKey);
}
/*
* Helper to get a value from configuration as a String
*
* @author Martin S. Eskildsen
*
*/
protected String getConfigValueAsString(String configKey) {
return (String) getConfigValue(configKey);
}
/*
* Helper to get a value from configuration as a Integer
*
* @author Martin S. Eskildsen
*
*/
protected Integer getConfigValueAsInteger(String configKey) {
return (Integer) getConfigValue(configKey);
}
protected BigDecimal getConfigValueAsBigDecimal(String configKey) {
return (BigDecimal) getConfigValue(configKey);
}
protected State getChannelStringAsStringState(String channelValue) {
State state = UnDefType.UNDEF;
try {
if (isConnected()) {
state = new StringType(channelValue);
}
} catch (Exception ex) {
logger.error("{}", ex.getMessage());
}
return state;
}
protected State getChannelBoolAsOnOffState(boolean value) {
State state = UnDefType.UNDEF;
try {
if (isConnected()) {
state = value ? OnOffType.ON : OnOffType.OFF;
}
} catch (Exception ex) {
logger.error("{}: Exception occurred in 'getChannelBoolAsOnOffState()' (Exception='{}')",
getLogIdentifier(), ex.getMessage());
}
return state;
}
@Override
public abstract String getLogIdentifier();
protected void updateThingStatus(ThingStatus thingStatus, ThingStatusDetail statusDetail,
String statusDescription) {
ThingStatusInfo curStatusInfo = thing.getStatusInfo();
String curDescription = ((curStatusInfo.getDescription() == null) ? "" : curStatusInfo.getDescription());
// Status changed
if ((curStatusInfo.getStatus() != thingStatus) || (curStatusInfo.getStatusDetail() != statusDetail)
|| (curDescription != statusDescription)) {
// Update Status correspondingly
if ((thingStatus == ThingStatus.OFFLINE) && (statusDetail != ThingStatusDetail.NONE)) {
logger.info("{}: Thing status changed from '{}' to '{}' (DetailedStatus='{}', Description='{}')",
getLogIdentifier(), thing.getStatus(), thingStatus, statusDetail, statusDescription);
updateStatus(thingStatus, statusDetail, statusDescription);
} else {
logger.info("{}: Thing status changed from '{}' to '{}'", getLogIdentifier(), thing.getStatus(),
thingStatus);
updateStatus(thingStatus);
}
}
}
}

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zoneminder.internal.handler;
import java.io.IOException;
import java.security.GeneralSecurityException;
import org.openhab.core.thing.ChannelUID;
import name.eskildsen.zoneminder.IZoneMinderConnectionInfo;
import name.eskildsen.zoneminder.exception.ZoneMinderUrlNotFoundException;
/**
* Interface for ZoneMinder handlers.
*
* @author Martin S. Eskildsen - Initial contribution
*/
public interface ZoneMinderHandler {
String getZoneMinderId();
/**
* Method used to relate a log entry to a thing
*/
String getLogIdentifier();
void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection);
void updateChannel(ChannelUID channel);
void onBridgeConnected(ZoneMinderServerBridgeHandler bridge, IZoneMinderConnectionInfo connection)
throws IllegalArgumentException, GeneralSecurityException, IOException, ZoneMinderUrlNotFoundException;
void onBridgeDisconnected(ZoneMinderServerBridgeHandler bridge);
}

View File

@@ -0,0 +1,836 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zoneminder.internal.handler;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import javax.security.auth.login.FailedLoginException;
import org.openhab.binding.zoneminder.internal.DataRefreshPriorityEnum;
import org.openhab.binding.zoneminder.internal.ZoneMinderConstants;
import org.openhab.binding.zoneminder.internal.ZoneMinderProperties;
import org.openhab.binding.zoneminder.internal.config.ZoneMinderThingMonitorConfig;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import name.eskildsen.zoneminder.IZoneMinderConnectionInfo;
import name.eskildsen.zoneminder.IZoneMinderDaemonStatus;
import name.eskildsen.zoneminder.IZoneMinderEventData;
import name.eskildsen.zoneminder.IZoneMinderEventSubscriber;
import name.eskildsen.zoneminder.IZoneMinderMonitor;
import name.eskildsen.zoneminder.IZoneMinderMonitorData;
import name.eskildsen.zoneminder.IZoneMinderSession;
import name.eskildsen.zoneminder.ZoneMinderFactory;
import name.eskildsen.zoneminder.api.event.ZoneMinderEvent;
import name.eskildsen.zoneminder.api.telnet.ZoneMinderTriggerEvent;
import name.eskildsen.zoneminder.common.ZoneMinderMonitorFunctionEnum;
import name.eskildsen.zoneminder.common.ZoneMinderMonitorStatusEnum;
import name.eskildsen.zoneminder.exception.ZoneMinderUrlNotFoundException;
/**
* The {@link ZoneMinderThingMonitorHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Martin S. Eskildsen - Initial contribution
*/
public class ZoneMinderThingMonitorHandler extends ZoneMinderBaseThingHandler implements IZoneMinderEventSubscriber {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections
.singleton(ZoneMinderConstants.THING_TYPE_THING_ZONEMINDER_MONITOR);
/** Make sure we can log errors, warnings or what ever somewhere */
private final Logger logger = LoggerFactory.getLogger(ZoneMinderThingMonitorHandler.class);
private ZoneMinderThingMonitorConfig config;
private ZoneMinderEvent curEvent;
/**
* Channels
*/
private ZoneMinderMonitorFunctionEnum channelFunction = ZoneMinderMonitorFunctionEnum.NONE;
private Boolean channelEnabled = false;
private boolean channelRecordingState;
private boolean channelAlarmedState;
private String channelEventCause = "";
private ZoneMinderMonitorStatusEnum channelMonitorStatus = ZoneMinderMonitorStatusEnum.UNKNOWN;
private boolean channelDaemonCapture;
private boolean channelDaemonAnalysis;
private boolean channelDaemonFrame;
private boolean channelForceAlarm;
private int forceAlarmManualState = -1;
public ZoneMinderThingMonitorHandler(Thing thing) {
super(thing);
logger.info("{}: Starting ZoneMinder Server Thing Handler (Thing='{}')", getLogIdentifier(), thing.getUID());
}
@Override
public void dispose() {
}
@Override
public String getZoneMinderId() {
if (config == null) {
logger.error("{}: Configuration for Thing '{}' is not loaded correctly.", getLogIdentifier(),
getThing().getUID());
return "";
}
return config.getZoneMinderId().toString();
}
@Override
public void onBridgeConnected(ZoneMinderServerBridgeHandler bridge, IZoneMinderConnectionInfo connection)
throws IllegalArgumentException, GeneralSecurityException, IOException, ZoneMinderUrlNotFoundException {
try {
logger.info("{}: Bridge '{}' connected", getLogIdentifier(), bridge.getThing().getUID().getAsString());
super.onBridgeConnected(bridge, connection);
ZoneMinderFactory.SubscribeMonitorEvents(connection, config.getZoneMinderId(), this);
IZoneMinderSession session = aquireSession();
IZoneMinderMonitor monitor = ZoneMinderFactory.getMonitorProxy(session, config.getZoneMinderId());
IZoneMinderMonitorData monitorData = monitor.getMonitorData();
logger.debug("{}: SourceType: {}", getLogIdentifier(), monitorData.getSourceType().name());
logger.debug("{}: Format: {}", getLogIdentifier(), monitorData.getFormat());
logger.debug("{}: AlarmFrameCount: {}", getLogIdentifier(), monitorData.getAlarmFrameCount());
logger.debug("{}: AlarmMaxFPS: {}", getLogIdentifier(), monitorData.getAlarmMaxFPS());
logger.debug("{}: AnalysisFPS: {}", getLogIdentifier(), monitorData.getAnalysisFPS());
logger.debug("{}: Height x Width: {} x {}", getLogIdentifier(), monitorData.getHeight(),
monitorData.getWidth());
updateMonitorProperties(session);
} catch (Exception ex) {
logger.error("{}: Exception occurred when calling 'onBridgeConencted()'. Exception='{}'",
getLogIdentifier(), ex.getMessage());
} finally {
releaseSession();
}
}
@Override
public void onBridgeDisconnected(ZoneMinderServerBridgeHandler bridge) {
try {
logger.info("{}: Bridge '{}' disconnected", getLogIdentifier(), bridge.getThing().getUID().getAsString());
logger.info("{}: Unsubscribing from Monitor Events: {}", getLogIdentifier(),
bridge.getThing().getUID().getAsString());
ZoneMinderFactory.UnsubscribeMonitorEvents(config.getZoneMinderId(), this);
logger.debug("{}: Calling parent onBridgeConnected()", getLogIdentifier());
super.onBridgeDisconnected(bridge);
} catch (Exception ex) {
logger.error("{}: Exception occurred when calling 'onBridgeDisonencted()'. Exception='{}'",
getLogIdentifier(), ex.getMessage());
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
try {
logger.debug("{}: Channel '{}' in monitor '{}' received command='{}'", getLogIdentifier(), channelUID,
getZoneMinderId(), command);
// Allow refresh of channels
if (command == RefreshType.REFRESH) {
updateChannel(channelUID);
return;
}
// Communication TO Monitor
switch (channelUID.getId()) {
// Done via Telnet connection
case ZoneMinderConstants.CHANNEL_MONITOR_FORCE_ALARM:
logger.debug(
"{}: 'handleCommand' => CHANNEL_MONITOR_FORCE_ALARM: Command '{}' received for monitor '{}'",
getLogIdentifier(), command, channelUID.getId());
if ((command == OnOffType.OFF) || (command == OnOffType.ON)) {
String eventText = getConfigValueAsString(ZoneMinderConstants.PARAMETER_MONITOR_EVENTTEXT);
BigDecimal eventTimeout = getConfigValueAsBigDecimal(
ZoneMinderConstants.PARAMETER_MONITOR_TRIGGER_TIMEOUT);
ZoneMinderServerBridgeHandler bridge = getZoneMinderBridgeHandler();
if (bridge == null) {
logger.warn("'handleCommand()': Bridge is 'null'!");
}
IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(),
getZoneMinderId());
try {
if (command == OnOffType.ON) {
forceAlarmManualState = 1;
logger.info("{}: Activate 'ForceAlarm' to '{}' (Reason='{}', Timeout='{}')",
getLogIdentifier(), command, eventText, eventTimeout.intValue());
monitorProxy.activateForceAlarm(255, ZoneMinderConstants.MONITOR_EVENT_OPENHAB,
eventText, "", eventTimeout.intValue());
}
else if (command == OnOffType.OFF) {
forceAlarmManualState = 0;
logger.info("{}: Cancel 'ForceAlarm'", getLogIdentifier());
monitorProxy.deactivateForceAlarm();
}
} finally {
releaseSession();
}
recalculateChannelStates();
handleCommand(channelUID, RefreshType.REFRESH);
handleCommand(getChannelUIDFromChannelId(ZoneMinderConstants.CHANNEL_MONITOR_EVENT_STATE),
RefreshType.REFRESH);
handleCommand(getChannelUIDFromChannelId(ZoneMinderConstants.CHANNEL_MONITOR_RECORD_STATE),
RefreshType.REFRESH);
// Force a refresh
startPriorityRefresh();
}
break;
case ZoneMinderConstants.CHANNEL_MONITOR_ENABLED:
logger.debug(
"{}: 'handleCommand' => CHANNEL_MONITOR_ENABLED: Command '{}' received for monitor '{}'",
getLogIdentifier(), command, channelUID.getId());
if ((command == OnOffType.OFF) || (command == OnOffType.ON)) {
boolean newState = ((command == OnOffType.ON) ? true : false);
IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(),
getZoneMinderId());
try {
monitorProxy.SetEnabled(newState);
} finally {
releaseSession();
}
channelEnabled = newState;
logger.info("{}: Setting enabled to '{}'", getLogIdentifier(), command);
}
handleCommand(channelUID, RefreshType.REFRESH);
break;
case ZoneMinderConstants.CHANNEL_MONITOR_FUNCTION:
String commandString = "";
if (ZoneMinderMonitorFunctionEnum.isValid(command.toString())) {
commandString = ZoneMinderMonitorFunctionEnum.getEnum(command.toString()).toString();
IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(),
getZoneMinderId());
try {
monitorProxy.SetFunction(commandString);
} finally {
releaseSession();
}
// Make sure local copy is set to new value
channelFunction = ZoneMinderMonitorFunctionEnum.getEnum(command.toString());
logger.info("{}: Setting function to '{}'", getLogIdentifier(), commandString);
} else {
logger.error(
"{}: Value '{}' for monitor channel is not valid. Accepted values is: 'None', 'Monitor', 'Modect', Record', 'Mocord', 'Nodect'",
getLogIdentifier(), commandString);
}
handleCommand(channelUID, RefreshType.REFRESH);
break;
// They are all readonly in the channel config.
case ZoneMinderConstants.CHANNEL_MONITOR_EVENT_STATE:
case ZoneMinderConstants.CHANNEL_MONITOR_DETAILED_STATUS:
case ZoneMinderConstants.CHANNEL_MONITOR_RECORD_STATE:
case ZoneMinderConstants.CHANNEL_ONLINE:
case ZoneMinderConstants.CHANNEL_MONITOR_EVENT_CAUSE:
case ZoneMinderConstants.CHANNEL_MONITOR_CAPTURE_DAEMON_STATE:
case ZoneMinderConstants.CHANNEL_MONITOR_ANALYSIS_DAEMON_STATE:
case ZoneMinderConstants.CHANNEL_MONITOR_FRAME_DAEMON_STATE:
// Do nothing, they are all read only
break;
default:
logger.warn("{}: Command received for an unknown channel: {}", getLogIdentifier(),
channelUID.getId());
break;
}
} catch (Exception ex) {
logger.error("{}: handleCommand: Command='{}' failed for channel='{}' Exception='{}'", getLogIdentifier(),
command, channelUID.getId(), ex.getMessage());
}
}
@Override
public void initialize() {
try {
super.initialize();
this.config = getMonitorConfig();
logger.info("{}: ZoneMinder Monitor Handler Initialized", getLogIdentifier());
logger.debug("{}: Monitor Id: {}", getLogIdentifier(), config.getZoneMinderId());
} catch (Exception ex) {
logger.error("{}: Exception occurred when calling 'initialize()'. Exception='{}'", getLogIdentifier(),
ex.getMessage());
}
}
@Override
public void onTrippedForceAlarm(ZoneMinderTriggerEvent event) {
try {
logger.info("{}: Received forceAlarm for monitor {}", getLogIdentifier(), event.getMonitorId());
// Set Current Event to actual event
if (event.getState()) {
startPriorityRefresh();
} else {
curEvent = null;
}
} catch (Exception ex) {
logger.error("{}: Exception occurred inTrippedForceAlarm() Exception='{}'", getLogIdentifier(),
ex.getMessage());
}
}
protected ZoneMinderThingMonitorConfig getMonitorConfig() {
return this.getConfigAs(ZoneMinderThingMonitorConfig.class);
}
@Override
protected String getZoneMinderThingType() {
return ZoneMinderConstants.THING_ZONEMINDER_MONITOR;
}
@Override
public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) {
// Assume success
ThingStatus newThingStatus = ThingStatus.ONLINE;
ThingStatusDetail thingStatusDetailed = ThingStatusDetail.NONE;
String thingStatusDescription = "";
ThingStatus curThingStatus = this.getThing().getStatus();
// Is connected to ZoneMinder and thing is ONLINE
if (isConnected() && curThingStatus == ThingStatus.ONLINE) {
updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
return;
}
try {
ZoneMinderFactory.validateConnection(connection);
} catch (IllegalArgumentException e) {
logger.error("{}: validateConnection failed with exception='{}'", getLogIdentifier(), e.getMessage());
newThingStatus = ThingStatus.OFFLINE;
thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR;
thingStatusDescription = "Could not connect to thing";
updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
return;
}
try {
String msg;
final Bridge bridge = getBridge();
// 1. Is there a Bridge assigned?
if (bridge == null) {
msg = String.format("No Bridge assigned to monitor '%s'", thing.getUID());
logger.error("{}: {}", getLogIdentifier(), msg);
newThingStatus = ThingStatus.OFFLINE;
thingStatusDetailed = ThingStatusDetail.BRIDGE_OFFLINE;
thingStatusDescription = "No Bridge assigned to monitor";
updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
return;
} else {
logger.debug("{}: ThingAvailability: Thing '{}' has Bridge '{}' defined (Check PASSED)",
getLogIdentifier(), thing.getUID(), bridge.getBridgeUID());
}
// 2. Is Bridge Online?
if (bridge.getStatus() != ThingStatus.ONLINE) {
msg = String.format("Bridge '%s' is OFFLINE", bridge.getBridgeUID());
newThingStatus = ThingStatus.OFFLINE;
thingStatusDetailed = ThingStatusDetail.BRIDGE_OFFLINE;
thingStatusDescription = msg;
updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
logger.error("{}: {}", getLogIdentifier(), msg);
return;
} else {
logger.debug("{}: ThingAvailability: Bridge '{}' is ONLINE (Check PASSED)", getLogIdentifier(),
bridge.getBridgeUID());
}
// 3. Is Configuration OK?
if (getMonitorConfig() == null) {
msg = String.format("No valid configuration found for '%s'", thing.getUID());
newThingStatus = ThingStatus.OFFLINE;
thingStatusDetailed = ThingStatusDetail.CONFIGURATION_ERROR;
thingStatusDescription = msg;
updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
logger.error("{}: {}", getLogIdentifier(), msg);
return;
} else {
logger.debug("{}: ThingAvailability: Thing '{}' has valid configuration (Check PASSED)",
getLogIdentifier(), thing.getUID());
}
// ZoneMinder Id for Monitor not set, we are pretty much lost then
if (getMonitorConfig().getZoneMinderId().isEmpty()) {
msg = String.format("No Id is specified for monitor '%s'", thing.getUID());
newThingStatus = ThingStatus.OFFLINE;
thingStatusDetailed = ThingStatusDetail.CONFIGURATION_ERROR;
thingStatusDescription = msg;
updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
logger.error("{}: {}", getLogIdentifier(), msg);
return;
} else {
logger.debug("{}: ThingAvailability: ZoneMinder Id for Thing '{}' defined (Check PASSED)",
getLogIdentifier(), thing.getUID());
}
IZoneMinderMonitor monitorProxy = null;
IZoneMinderDaemonStatus captureDaemon = null;
// TODO:: Also look at Analysis and Frame Daemons (only if they are supposed to be running)
// IZoneMinderSession session = aquireSession();
IZoneMinderSession curSession = null;
try {
curSession = ZoneMinderFactory.CreateSession(connection);
} catch (FailedLoginException | IllegalArgumentException | IOException
| ZoneMinderUrlNotFoundException ex) {
logger.error("{}: Create Session failed with exception {}", getLogIdentifier(), ex.getMessage());
newThingStatus = ThingStatus.OFFLINE;
thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR;
thingStatusDescription = "Failed to connect. (Check Log)";
updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
return;
}
if (curSession != null) {
monitorProxy = ZoneMinderFactory.getMonitorProxy(curSession, getZoneMinderId());
captureDaemon = monitorProxy.getCaptureDaemonStatus();
}
if (captureDaemon == null) {
msg = String.format("Capture Daemon not accssible");
newThingStatus = ThingStatus.OFFLINE;
thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR;
thingStatusDescription = msg;
updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
logger.error("{}: {}", getLogIdentifier(), msg);
return;
} else if (!captureDaemon.getStatus()) {
msg = String.format("Capture Daemon is not running");
newThingStatus = ThingStatus.OFFLINE;
thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR;
thingStatusDescription = msg;
updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
logger.error("{}: {}", getLogIdentifier(), msg);
return;
}
newThingStatus = ThingStatus.ONLINE;
} catch (Exception exception) {
newThingStatus = ThingStatus.OFFLINE;
thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR;
thingStatusDescription = "Error occurred (Check log)";
updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
logger.error("{}: 'ThingMonitorHandler.updateAvailabilityStatus()': Exception occurred '{}'",
getLogIdentifier(), exception.getMessage());
return;
}
updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription);
}
/*
* From here we update states in openHAB
*
* @see
* org.openhab.binding.zoneminder.handler.ZoneMinderBaseThingHandler#updateChannel(org.openhab.core.thing.
* ChannelUID)
*/
@Override
public void updateChannel(ChannelUID channel) {
State state = null;
try {
switch (channel.getId()) {
case ZoneMinderConstants.CHANNEL_MONITOR_ENABLED:
state = getChannelBoolAsOnOffState(channelEnabled);
break;
case ZoneMinderConstants.CHANNEL_ONLINE:
// Ask super class to handle, because this channel is shared for all things
super.updateChannel(channel);
break;
case ZoneMinderConstants.CHANNEL_MONITOR_FORCE_ALARM:
state = getChannelBoolAsOnOffState(channelForceAlarm);
break;
case ZoneMinderConstants.CHANNEL_MONITOR_EVENT_STATE:
state = getChannelBoolAsOnOffState(channelAlarmedState);
break;
case ZoneMinderConstants.CHANNEL_MONITOR_RECORD_STATE:
state = getChannelBoolAsOnOffState(channelRecordingState);
break;
case ZoneMinderConstants.CHANNEL_MONITOR_DETAILED_STATUS:
state = getDetailedStatus();
break;
case ZoneMinderConstants.CHANNEL_MONITOR_EVENT_CAUSE:
state = getChannelStringAsStringState(channelEventCause);
break;
case ZoneMinderConstants.CHANNEL_MONITOR_FUNCTION:
state = getChannelStringAsStringState(channelFunction.toString());
break;
case ZoneMinderConstants.CHANNEL_MONITOR_CAPTURE_DAEMON_STATE:
state = getChannelBoolAsOnOffState(channelDaemonCapture);
break;
case ZoneMinderConstants.CHANNEL_MONITOR_ANALYSIS_DAEMON_STATE:
state = getChannelBoolAsOnOffState(channelDaemonAnalysis);
break;
case ZoneMinderConstants.CHANNEL_MONITOR_FRAME_DAEMON_STATE:
state = getChannelBoolAsOnOffState(channelDaemonFrame);
break;
default:
logger.warn("{}: updateChannel(): Monitor '{}': No handler defined for channel='{}'",
getLogIdentifier(), thing.getLabel(), channel.getAsString());
// Ask super class to handle
super.updateChannel(channel);
}
if (state != null) {
logger.debug("{}: Setting channel '{}' to '{}'", getLogIdentifier(), channel.toString(),
state.toString());
updateState(channel.getId(), state);
}
} catch (Exception ex) {
logger.error("{}: Error when 'updateChannel' was called (channelId='{}'state='{}', exception'{}')",
getLogIdentifier(), channel, state, ex.getMessage());
}
}
@Override
public void updateStatus(ThingStatus status) {
super.updateStatus(status);
updateState(ZoneMinderConstants.CHANNEL_ONLINE,
((status == ThingStatus.ONLINE) ? OnOffType.ON : OnOffType.OFF));
}
protected void recalculateChannelStates() {
boolean recordingFunction = false;
boolean recordingDetailedState = false;
boolean alarmedFunction = false;
boolean alarmedDetailedState = false;
// Calculate based on state of Function
switch (channelFunction) {
case NONE:
case MONITOR:
alarmedFunction = false;
recordingFunction = false;
break;
case MODECT:
alarmedFunction = true;
recordingFunction = true;
break;
case RECORD:
alarmedFunction = false;
recordingFunction = true;
break;
case MOCORD:
alarmedFunction = true;
recordingFunction = true;
break;
case NODECT:
alarmedFunction = false;
recordingFunction = true;
break;
default:
recordingFunction = (curEvent != null) ? true : false;
}
logger.debug(
"{}: Recalculate channel states based on Function: Function='{}' -> alarmState='{}', recordingState='{}'",
getLogIdentifier(), channelFunction.name(), alarmedFunction, recordingFunction);
// Calculated based on detailed Monitor Status
switch (channelMonitorStatus) {
case IDLE:
alarmedDetailedState = false;
recordingDetailedState = false;
channelForceAlarm = false;
channelEventCause = "";
break;
case PRE_ALARM:
alarmedDetailedState = true;
recordingDetailedState = true;
channelForceAlarm = false;
break;
case ALARM:
alarmedDetailedState = true;
recordingDetailedState = true;
channelForceAlarm = true;
break;
case ALERT:
alarmedDetailedState = true;
recordingDetailedState = true;
channelForceAlarm = false;
break;
case RECORDING:
alarmedDetailedState = false;
recordingDetailedState = true;
channelForceAlarm = false;
break;
case UNKNOWN:
}
logger.debug(
"{}: Recalculate channel states based on Detailed State: DetailedState='{}' -> alarmState='{}', recordingState='{}'",
getLogIdentifier(), channelMonitorStatus.name(), alarmedDetailedState, recordingDetailedState);
// Check if Force alarm was initialed from openHAB
if (forceAlarmManualState == 0) {
if (channelForceAlarm) {
channelForceAlarm = false;
} else {
forceAlarmManualState = -1;
}
} else if (forceAlarmManualState == 1) {
if (!channelForceAlarm) {
channelForceAlarm = true;
} else {
forceAlarmManualState = -1;
}
}
// Now we can conclude on the Alarmed and Recording channel state
channelRecordingState = (recordingFunction && recordingDetailedState && channelEnabled);
channelAlarmedState = (alarmedFunction && alarmedDetailedState && channelEnabled);
}
@Override
protected void onFetchData() {
IZoneMinderSession session = null;
session = aquireSession();
try {
IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(session, getZoneMinderId());
IZoneMinderMonitorData data = null;
IZoneMinderDaemonStatus captureDaemon = null;
IZoneMinderDaemonStatus analysisDaemon = null;
IZoneMinderDaemonStatus frameDaemon = null;
data = monitorProxy.getMonitorData();
logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(),
monitorProxy.getHttpResponseMessage());
captureDaemon = monitorProxy.getCaptureDaemonStatus();
logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(),
monitorProxy.getHttpResponseMessage());
analysisDaemon = monitorProxy.getAnalysisDaemonStatus();
logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(),
monitorProxy.getHttpResponseMessage());
frameDaemon = monitorProxy.getFrameDaemonStatus();
logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(),
monitorProxy.getHttpResponseMessage());
if ((data.getHttpResponseCode() != 200) || (captureDaemon.getHttpResponseCode() != 200)
|| (analysisDaemon.getHttpResponseCode() != 200) || (frameDaemon.getHttpResponseCode() != 200)) {
if (data.getHttpResponseCode() != 200) {
logger.warn("{}: HTTP Response MonitorData: Code='{}', Message'{}'", getLogIdentifier(),
data.getHttpResponseCode(), data.getHttpResponseMessage());
channelMonitorStatus = ZoneMinderMonitorStatusEnum.UNKNOWN;
channelFunction = ZoneMinderMonitorFunctionEnum.NONE;
channelEnabled = false;
channelEventCause = "";
}
if (captureDaemon.getHttpResponseCode() != 200) {
channelDaemonCapture = false;
logger.warn("{}: HTTP Response CaptureDaemon: Code='{}', Message'{}'", getLogIdentifier(),
captureDaemon.getHttpResponseCode(), captureDaemon.getHttpResponseMessage());
}
if (analysisDaemon.getHttpResponseCode() != 200) {
channelDaemonAnalysis = false;
logger.warn("{}: HTTP Response AnalysisDaemon: Code='{}', Message='{}'", getLogIdentifier(),
analysisDaemon.getHttpResponseCode(), analysisDaemon.getHttpResponseMessage());
}
if (frameDaemon.getHttpResponseCode() != 200) {
channelDaemonFrame = false;
logger.warn("{}: HTTP Response MonitorData: Code='{}', Message'{}'", getLogIdentifier(),
frameDaemon.getHttpResponseCode(), frameDaemon.getHttpResponseMessage());
}
} else {
if (isConnected()) {
channelMonitorStatus = monitorProxy.getMonitorDetailedStatus();
logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(),
monitorProxy.getHttpResponseMessage());
channelFunction = data.getFunction();
channelEnabled = data.getEnabled();
IZoneMinderEventData event = monitorProxy.getLastEvent();
if (event != null) {
channelEventCause = event.getCause();
} else {
channelEventCause = "";
}
channelDaemonCapture = captureDaemon.getStatus();
channelDaemonAnalysis = analysisDaemon.getStatus();
channelDaemonFrame = frameDaemon.getStatus();
} else {
channelMonitorStatus = ZoneMinderMonitorStatusEnum.UNKNOWN;
channelFunction = ZoneMinderMonitorFunctionEnum.NONE;
channelEnabled = false;
channelEventCause = "";
channelDaemonCapture = false;
channelDaemonAnalysis = false;
channelDaemonFrame = false;
}
}
} finally {
releaseSession();
}
recalculateChannelStates();
if (!channelForceAlarm && !channelAlarmedState
&& (DataRefreshPriorityEnum.HIGH_PRIORITY == getRefreshPriority())) {
stopPriorityRefresh();
}
}
protected State getDetailedStatus() {
State state = UnDefType.UNDEF;
try {
if (channelMonitorStatus == ZoneMinderMonitorStatusEnum.UNKNOWN) {
state = getChannelStringAsStringState("");
} else {
state = getChannelStringAsStringState(channelMonitorStatus.toString());
}
} catch (Exception ex) {
logger.debug("{}", ex.getMessage());
}
return state;
}
/*
* This is experimental
* Try to add different properties
*/
private void updateMonitorProperties(IZoneMinderSession session) {
logger.debug("{}: Update Monitor Properties", getLogIdentifier());
// Update property information about this device
Map<String, String> properties = editProperties();
IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(session, getZoneMinderId());
IZoneMinderMonitorData monitorData = monitorProxy.getMonitorData();
logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(),
monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(), monitorProxy.getHttpResponseMessage());
properties.put(ZoneMinderProperties.PROPERTY_ID, getLogIdentifier());
properties.put(ZoneMinderProperties.PROPERTY_MONITOR_NAME, monitorData.getName());
properties.put(ZoneMinderProperties.PROPERTY_MONITOR_SOURCETYPE, monitorData.getSourceType().name());
properties.put(ZoneMinderProperties.PROPERTY_MONITOR_ANALYSIS_FPS, monitorData.getAnalysisFPS());
properties.put(ZoneMinderProperties.PROPERTY_MONITOR_MAXIMUM_FPS, monitorData.getMaxFPS());
properties.put(ZoneMinderProperties.PROPERTY_MONITOR_ALARM_MAXIMUM, monitorData.getAlarmMaxFPS());
properties.put(ZoneMinderProperties.PROPERTY_MONITOR_IMAGE_WIDTH, monitorData.getWidth());
properties.put(ZoneMinderProperties.PROPERTY_MONITOR_IMAGE_HEIGHT, monitorData.getHeight());
// Must loop over the new properties since we might have added data
boolean update = false;
Map<String, String> originalProperties = editProperties();
for (String property : properties.keySet()) {
if ((originalProperties.get(property) == null
|| !originalProperties.get(property).equals(properties.get(property)))) {
update = true;
break;
}
}
if (update) {
logger.debug("{}: Properties synchronised", getLogIdentifier());
updateProperties(properties);
}
}
@Override
public String getLogIdentifier() {
String result = "[MONITOR]";
try {
if (config != null) {
result = String.format("[MONITOR-%s]", config.getZoneMinderId().toString());
}
} catch (Exception ex) {
result = "[MONITOR]";
}
return result;
}
}

View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zoneminder.internal.handler;
/**
* Enumerator for each Bridge and Thing
*
* @author Martin S. Eskildsen - Initial contribution
*/
public enum ZoneMinderThingType {
ZoneMinderServerBridge,
ZoneMinderMonitorThing
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="zoneminder" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>ZoneMinder Binding</name>
<description>This binding interfaces a ZoneMinder Server</description>
<author>Martin S. Eskildsen</author>
</binding:binding>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="thing-type:monitor-channels:config">
<parameter name="monitorId" type="integer" required="true">
<label>Monitor ID</label>
<description>The ID of the monitor in ZoneMinder</description>
</parameter>
<parameter name="monitorTriggerTimeout" type="integer" required="false" min="0" max="65535">
<label>ForceAlarm Timeout</label>
<description>Timeout in seconds when activating alarm. Default is 60 seconds</description>
<default>60</default>
</parameter>
<parameter name="monitorEventText" type="text" required="false">
<label>Event Text</label>
<description>Event text in ZoneMinder</description>
<default>Triggered from openHAB</default>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="thing-type:zoneminderserver:config">
<parameter-group name="basic">
<context>basic</context>
<label>Basic</label>
</parameter-group>
<parameter-group name="credentials">
<context>credentials</context>
<label>Credentials</label>
</parameter-group>
<parameter-group name="network">
<context>network</context>
<label>Port Configuration</label>
</parameter-group>
<parameter-group name="refreshConfig">
<context>refreshConfig</context>
<label>Refresh Settings</label>
</parameter-group>
<parameter-group name="advancedSettings">
<context>advancedSettings</context>
<label>Advanced</label>
</parameter-group>
<parameter name="hostname" type="text" required="true" groupName="basic">
<context>network-address</context>
<label>Host</label>
<description>The IP address or hostname of the ZoneMinder Server</description>
</parameter>
<parameter name="protocol" type="text" required="false" groupName="basic">
<label>Protocol</label>
<description>Protocol to connect to the ZoneMinder Server API (http or https)</description>
<default>http</default>
<options>
<option value="http">HTTP</option>
<option value="https">HTTPS</option>
</options>
</parameter>
<parameter name="urlpath" type="text" required="false" groupName="basic">
<label>Additional Path On ZoneMinder Server to Access API</label>
<description>Additional path on ZoneMinder Server to access API. In a standard installation this is' /zm'</description>
<default>/zm</default>
</parameter>
<parameter name="user" type="text" required="false" groupName="credentials">
<label>Username</label>
<description>User to access the ZoneMinder Server API</description>
</parameter>
<parameter name="password" type="text" required="false" groupName="credentials">
<context>password</context>
<label>Password</label>
<description>Password to access the ZoneMinder Server API</description>
</parameter>
<parameter name="http_port" type="integer" required="false" min="0" max="65535" groupName="network">
<label>Port</label>
<description>Port of the ZoneMinder Server API. If '0', then the port will be determined from the protocol</description>
<default>0</default>
<advanced>true</advanced>
</parameter>
<parameter name="telnet_port" type="integer" required="false" min="1" max="65535" groupName="network">
<label>Telnet Port</label>
<description>Port to listen for events in (Telnet)</description>
<default>6802</default>
<advanced>true</advanced>
</parameter>
<parameter name="refresh_interval" type="integer" required="false" min="1" max="65535"
groupName="refreshConfig">
<label>API Polling Interval</label>
<description>Seconds between each call to ZoneMinder Server API to refresh values in openHAB</description>
<default>10</default>
<advanced>true</advanced>
</parameter>
<parameter name="refresh_interval_disk_usage" type="integer" required="false" min="0" max="65335"
groupName="refreshConfig">
<label>Refresh Interval for Disk Usage</label>
<description>Minutes between each call to ZoneMinder Server to refresh Server DiskUsage in ZoneMinder. Default value
is '0' (Disabled)</description>
<default>0</default>
<advanced>true</advanced>
</parameter>
<parameter name="autodiscover_things" type="boolean" required="false" groupName="advanced">
<label>Background Discovery</label>
<description>If enabled new monitors on the ZoneMinder Server will automatically be added to the Inbox in openHAB</description>
<default>true</default>
<advanced>true</advanced>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="zoneminder"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Supported ZoneMinder devices and features -->
<thing-type id="monitor">
<supported-bridge-type-refs>
<bridge-type-ref id="server"/>
</supported-bridge-type-refs>
<label>ZoneMinder Monitor</label>
<description>Camera in ZoneMinder</description>
<channels>
<channel id="online" typeId="monitor_online"/>
<channel id="enabled" typeId="monitor_enabled"/>
<channel id="force-alarm" typeId="monitor_force_alarm"/>
<channel id="alarm" typeId="monitor_alarm"/>
<channel id="recording" typeId="monitor_recording"/>
<channel id="detailed-status" typeId="monitor_detailed_status"/>
<channel id="function" typeId="monitor_function"/>
<channel id="event-cause" typeId="monitor_event_cause"/>
<channel id="capture-daemon" typeId="monitor_zmc_daemon"/>
<channel id="analysis-daemon" typeId="monitor_zma_daemon"/>
<channel id="frame-daemon" typeId="monitor_zmf_daemon"/>
</channels>
<config-description-ref uri="thing-type:monitor-channels:config"/>
</thing-type>
<!-- Channel definitions of ZoneMinder Server -->
<channel-type id="monitor_online">
<item-type>Switch</item-type>
<label>Online</label>
<description>Switch telling if the monitor is online</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="monitor_enabled">
<item-type>Switch</item-type>
<label>Enabled</label>
<description>Showing the value of the checkbox 'enabled' in ZoneMinder for the monitor</description>
<state readOnly="false"/>
</channel-type>
<channel-type id="monitor_force_alarm">
<item-type>Switch</item-type>
<label>Force Alarm</label>
<description>Will force an alarm from openHAB in ZoneMinder</description>
<state readOnly="false"/>
</channel-type>
<channel-type id="monitor_alarm">
<item-type>Switch</item-type>
<label>Alarm Status</label>
<description>set to 'ON' when one of the following is true: Motion detected, Signal lost, Force Alarm pressed,
External Alarm. Else set to 'OFF'</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="monitor_recording">
<item-type>Switch</item-type>
<label>Recording Status</label>
<description>set to 'ON' when either channel monitor-alarm set to 'ON', or montior function is 'Mocord' or 'Record'.
Else set to 'OFF'</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="monitor_detailed_status" advanced="true">
<item-type>String</item-type>
<label>Detailed Status</label>
<description>Current Monitor Status: 0=Idle, 1=Pre-alarm, 2=Alarm, 3=Alert, 4=Recording</description>
<state pattern="%s" readOnly="true">
<options>
<option value="Idle">Idle</option>
<option value="Pre-alarm">Pre-alarm</option>
<option value="Alarm">Alarm</option>
<option value="Alert">Alert</option>
<option value="Recording">Recording</option>
</options>
</state>
</channel-type>
<channel-type id="monitor_function" advanced="true">
<item-type>String</item-type>
<label>Operating Mode</label>
<description>Current Monitor Function: None, Monitor, Modect, Record, Mocord, Nodect</description>
<state pattern="%s" readOnly="false">
<options>
<option value="None">None</option>
<option value="Monitor">Monitor</option>
<option value="Modect">Modect</option>
<option value="Record">Record</option>
<option value="Mocord">Mocord</option>
<option value="Nodect">Nodect</option>
</options>
</state>
</channel-type>
<channel-type id="monitor_event_cause" advanced="true">
<item-type>String</item-type>
<label>Event Cause</label>
<description>Cause of event: None, Signal, Motion, Forced Web, openHAB, Other</description>
<state pattern="%s" readOnly="true">
<options>
<option value="none">None</option>
<option value="signal">Signal</option>
<option value="motion">Motion</option>
<option value="forced_web">Forced Web</option>
<option value="openhab">openHAB</option>
<option value="other">Other</option>
</options>
</state>
</channel-type>
<channel-type id="monitor_zmc_daemon" advanced="true">
<item-type>Switch</item-type>
<label>Capture Daemon Status</label>
<description>State of ZoneMinder Capture daemon for this monitor</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="monitor_zma_daemon" advanced="true">
<item-type>Switch</item-type>
<label>Analysis Daemon Status</label>
<description>State of ZoneMinder Analysis daemon for this monitor</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="monitor_zmf_daemon" advanced="true">
<item-type>Switch</item-type>
<label>Frame Daemon Status</label>
<description>State of ZoneMinder Frame daemon for this monitor</description>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="zoneminder"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<bridge-type id="server">
<label>ZoneMinder Server</label>
<description>ZoneMinder Server</description>
<channels>
<channel id="online" typeId="server_online"/>
<channel id="cpu-load" typeId="server_cpu_load"/>
<channel id="disk-usage" typeId="server_disk_usage"/>
</channels>
<config-description-ref uri="thing-type:zoneminderserver:config"/>
</bridge-type>
<channel-type id="server_online">
<item-type>Switch</item-type>
<label>Online</label>
<description>ZoneMinder Server Online Status</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="server_cpu_load">
<item-type>Number</item-type>
<label>CPU Load</label>
<description>ZoneMinder Server CPU Load</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="server_disk_usage">
<item-type>Number</item-type>
<label>Diskusage</label>
<description>ZoneMinder Server Disk Usage</description>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>