";
Log3 $name, 2, $msg;
return $msg;
}
$hash->{NAME} = $name;
readingsSingleUpdate($hash, "state", "idle", 1);
AssignIoPort($hash);
my $ioDevName = $hash->{IODev}{NAME};
my $logDevAddress = $ioDevName.'_'.$zmMonitorId;
# Adresse rückwärts dem Hash zuordnen (für ParseFn)
# Log3 $name, 3, "ZM_Monitor ($name) - Logical device address: $logDevAddress";
$modules{ZM_Monitor}{defptr}{$logDevAddress} = $hash;
# Log3 $name, 3, "ZM_Monitor ($name) - Define done ... module=$module, zmHost=$zmHost, zmMonitorId=$zmMonitorId";
$hash->{helper}{ZM_MONITOR_ID} = $zmMonitorId;
ZM_Monitor_UpdateStreamUrls($hash);
return undef;
}
sub ZM_Monitor_UpdateStreamUrls {
my ( $hash ) = @_;
my $ioDevName = $hash->{IODev}{NAME};
my $zmPathZms = $hash->{IODev}{helper}{ZM_PATH_ZMS};
if (not $zmPathZms) {
return undef;
}
my $zmHost = $hash->{IODev}{helper}{ZM_HOST};
my $streamUrl = "http://$zmHost";
my $zmUsername = urlEncode($hash->{IODev}{helper}{ZM_USERNAME});
my $zmPassword = urlEncode($hash->{IODev}{helper}{ZM_PASSWORD});
my $authPart = "&user=$zmUsername&pass=$zmPassword";
readingsBeginUpdate($hash);
ZM_Monitor_WriteStreamUrlToReading($hash, $streamUrl, 'streamUrl', $authPart);
my $pubStreamUrl = $attr{$ioDevName}{publicAddress};
if ($pubStreamUrl) {
my $authHash = ReadingsVal($ioDevName, 'authHash', '');
if ($authHash) { #if ZM_AUTH_KEY is defined, use the auth-hash. otherwise, use the previously defined username/pwd
$authPart = "&auth=$authHash";
}
ZM_Monitor_WriteStreamUrlToReading($hash, $pubStreamUrl, 'pubStreamUrl', $authPart);
}
readingsEndUpdate($hash, 1);
return undef;
}
# is build by using hosname, NPH_ZMS, monitorId, streamBufferSize, and auth
sub ZM_Monitor_getZmStreamUrl {
my ($hash) = @_;
#use private or public LAN for streaming access?
return undef;
}
sub ZM_Monitor_WriteStreamUrlToReading {
my ( $hash, $streamUrl, $readingName, $authPart ) = @_;
my $name = $hash->{NAME};
my $zmPathZms = $hash->{IODev}{helper}{ZM_PATH_ZMS};
my $zmMonitorId = $hash->{helper}{ZM_MONITOR_ID};
my $buffer = ReadingsVal($name, 'streamReplayBuffer', '1000');
my $imageUrl = $streamUrl."$zmPathZms?mode=single&scale=100&monitor=$zmMonitorId".$authPart;
my $imageReadingName = $readingName;
$imageReadingName =~ s/Stream/Image/g;
readingsBulkUpdate($hash, $imageReadingName, $imageUrl, 1);
$streamUrl = $streamUrl."$zmPathZms?mode=jpeg&scale=100&maxfps=30&buffer=$buffer&monitor=$zmMonitorId".$authPart;
readingsBulkUpdate($hash, $readingName, "$streamUrl", 1);
}
sub ZM_Monitor_DetailFn {
my ( $FW_wname, $deviceName, $FW_room ) = @_;
my $hash = $defs{$deviceName};
my $name = $hash->{NAME};
my $showLiveStream = $attr{$name}{showLiveStreamInDetail};
return "To view a live stream here, execute: attr $name showLiveStreamInDetail 1
" if (not $showLiveStream);
my $streamDisabled = (ReadingsVal($deviceName, 'monitorFunction', 'None') eq 'None');
if ($streamDisabled) {
return 'Streaming disabled
';
}
my $streamUrl = ReadingsVal($deviceName, 'pubStreamUrl', undef);
if (not $streamUrl) {
$streamUrl = ReadingsVal($deviceName, 'streamUrl', undef);
}
if ($streamUrl) {
return "
";
} else {
return undef;
}
}
sub ZM_Monitor_Undef {
my ($hash, $arg) = @_;
my $name = $hash->{NAME};
return undef;
}
sub ZM_Monitor_Get {
my ( $hash, $name, $opt, @args ) = @_;
# return "Unknown argument $opt, choose one of config";
return undef;
}
sub ZM_Monitor_Set {
my ( $hash, $name, $cmd, @args ) = @_;
if ( "monitorFunction" eq $cmd ) {
my $arg = $args[0];
if (grep { $_ eq $arg } @ZM_Functions) {
my $arguments = {
method => 'changeMonitorFunction',
zmMonitorId => $hash->{helper}{ZM_MONITOR_ID},
zmFunction => $arg
};
my $result = IOWrite($hash, $arguments);
return $result;
}
return "Unknown value $arg for $cmd, choose one of ".join(' ', @ZM_Functions);
} elsif ("motionDetectionEnabled" eq $cmd ) {
my $arg = $args[0];
if ($arg eq '1' || $arg eq '0') {
my $arguments = {
method => 'changeMonitorEnabled',
zmMonitorId => $hash->{helper}{ZM_MONITOR_ID},
zmEnabled => $arg
};
my $result = IOWrite($hash, $arguments);
return $result;
}
return "Unknown value $arg for $cmd, choose one of 0 1";
} elsif ("alarmState" eq $cmd) {
my $arg = $args[0];
if (grep { $_ eq $arg } @ZM_Alarms) {
$arg .= ' '.$args[1] if ( 'on-for-timer' eq $arg );
my ( $unnamedParams, $namedParams ) = parseParams(join(' ', @args));
my $cause = 'fhem';
my $notes = '';
if( defined($namedParams->{'cause'}) ) {
$cause = $namedParams->{'cause'};
}
if( defined($namedParams->{'notes'}) ) {
$notes = $namedParams->{'notes'};
}
my $arguments = {
method => 'changeMonitorAlarm',
zmMonitorId => $hash->{helper}{ZM_MONITOR_ID},
zmAlarm => $arg,
zmCause => $cause,
zmNotes => $notes
};
my $result = IOWrite($hash, $arguments);
return $result;
}
return "Unknown value $arg for $cmd, chose one of ".join(' '. @ZM_Alarms);
} elsif ("text" eq $cmd) {
my $arg = join ' ', @args;
if (not $arg) {
$arg = '';
}
my $arguments = {
method => 'changeMonitorText',
zmMonitorId => $hash->{helper}{ZM_MONITOR_ID},
text => $arg
};
my $result = IOWrite($hash, $arguments);
return $result;
}
return 'monitorFunction:'.join(',', @ZM_Functions).' motionDetectionEnabled:0,1 alarmState:on,off,on-for-timer text';
}
# incoming messages from physical device module (70_ZoneMinder in this case).
sub ZM_Monitor_Parse {
my ( $io_hash, $message) = @_;
my @msg = split(/\:/, $message, 2);
my $msgType = $msg[0];
if ($msgType eq 'event') {
return ZM_Monitor_handleEvent($io_hash, $msg[1]);
} elsif ($msgType eq 'eventDetails') {
return ZM_Monitor_handleZoneUpdate($io_hash, $msg[1]);
} elsif ($msgType eq 'createMonitor') {
return ZM_Monitor_handleMonitorCreation($io_hash, $msg[1]);
} elsif ($msgType eq 'monitor') {
return ZM_Monitor_handleMonitorUpdate($io_hash, $msg[1]);
} else {
Log3 $io_hash, 0, "Unknown message type: $msgType";
}
return undef;
}
sub ZM_Monitor_handleEvent {
my ( $io_hash, $message ) = @_;
my $ioName = $io_hash->{NAME};
my @msgTokens = split(/\|/, $message);
my $zmMonitorId = $msgTokens[0];
my $alertState = $msgTokens[1];
my $eventTs = $msgTokens[2];
my $eventId = $msgTokens[3];
my $logDevAddress = $ioName.'_'.$zmMonitorId;
Log3 $io_hash, 5, "ZM_Monitor Handling event for logical device $logDevAddress";
# wenn bereits eine Gerätedefinition existiert (via Definition Pointer aus Define-Funktion)
if(my $hash = $modules{ZM_Monitor}{defptr}{$logDevAddress}) {
my $name = $hash->{NAME};
Log3 $hash, 5, "ZM_Monitor ($name) Logical device $logDevAddress found. Writing readings";
ZM_Monitor_queryEventDetails($hash, $eventId);
readingsBeginUpdate($hash);
ZM_Monitor_createEventStreamUrl($hash, $eventId);
my $state;
if ($alertState eq "on") {
$state = "alert";
} elsif ($alertState eq "off") {
$state = "idle";
}
readingsBulkUpdate($hash, "state", $state, 1);
readingsBulkUpdate($hash, "alert", $alertState, 1);
readingsBulkUpdate($hash, "lastEventTimestamp", $eventTs);
readingsBulkUpdate($hash, "lastEventId", $eventId);
readingsBulkUpdate($hash, "lastEventNotes", '');
readingsEndUpdate($hash, 1);
Log3 $hash, 5, "ZM_Monitor ($name) Writing readings done. Now returning log dev name: $hash->{NAME}";
# Rückgabe des Gerätenamens, für welches die Nachricht bestimmt ist.
return $hash->{NAME};
} else {
# Keine Gerätedefinition verfügbar. Daher Vorschlag define-Befehl:
my $autocreate = "UNDEFINED ZM_Monitor_$logDevAddress ZM_Monitor $zmMonitorId";
Log3 $io_hash, 5, "ZM_Monitor logical device with address $logDevAddress not found. returning autocreate: $autocreate";
return $autocreate;
}
}
sub ZM_Monitor_queryEventDetails {
my ( $hash, $eventId ) = @_;
my $arguments = {
method => 'queryEventDetails',
zmMonitorId => $hash->{helper}{ZM_MONITOR_ID},
zmEventId => $eventId
};
my $result = IOWrite($hash, $arguments);
}
sub ZM_Monitor_handleZoneUpdate {
my ( $io_hash, $message ) = @_;
my $ioName = $io_hash->{NAME};
my @msgTokens = split(/\|/, $message);
my $zmMonitorId = $msgTokens[0];
my $zmEventId = $msgTokens[1];
my $zmNotes = $msgTokens[2];
my $logDevAddress = $ioName.'_'.$zmMonitorId;
if(my $hash = $modules{ZM_Monitor}{defptr}{$logDevAddress}) {
readingsSingleUpdate($hash, 'lastEventNotes', $zmNotes, 1);
return $hash->{NAME}
} else {
# Keine Gerätedefinition verfügbar. Daher Vorschlag define-Befehl:
my $autocreate = "UNDEFINED ZM_Monitor_$logDevAddress ZM_Monitor $zmMonitorId";
Log3 $io_hash, 5, "ZM_Monitor logical device with address $logDevAddress not found. returning autocreate: $autocreate";
return $autocreate;
}
}
#for now, this is nearly a duplicate of writing the streamUrl reading.
#will need some love to make better use of existing code.
sub ZM_Monitor_createEventStreamUrl {
my ( $hash, $eventId ) = @_;
my $ioDevName = $hash->{IODev}{NAME};
my $zmPathZms = $hash->{IODev}{helper}{ZM_PATH_ZMS};
if (not $zmPathZms) {
return undef;
}
my $zmHost = $hash->{IODev}{helper}{ZM_HOST};
my $streamUrl = "http://$zmHost";
my $zmUsername = urlEncode($hash->{IODev}{helper}{ZM_USERNAME});
my $zmPassword = urlEncode($hash->{IODev}{helper}{ZM_PASSWORD});
my $authPart = "&user=$zmUsername&pass=$zmPassword";
ZM_Monitor_WriteEventStreamUrlToReading($hash, $streamUrl, 'eventStreamUrl', $authPart, $eventId);
my $pubStreamUrl = $attr{$ioDevName}{publicAddress};
if ($pubStreamUrl) {
my $authHash = ReadingsVal($ioDevName, 'authHash', '');
if ($authHash) { #if ZM_AUTH_KEY is defined, use the auth-hash. otherwise, use the previously defined username/pwd
$authPart = "&auth=$authHash";
}
ZM_Monitor_WriteEventStreamUrlToReading($hash, $pubStreamUrl, 'pubEventStreamUrl', $authPart, $eventId);
}
}
sub ZM_Monitor_handleMonitorUpdate {
my ( $io_hash, $message ) = @_;
my $ioName = $io_hash->{NAME};
my @msgTokens = split(/\|/, $message); #$message = "$monitorId|$function|$enabled|$streamReplayBuffer|$monitorType";
my $zmMonitorId = $msgTokens[0];
my $function = $msgTokens[1];
my $enabled = $msgTokens[2];
my $streamReplayBuffer = $msgTokens[3];
my $monitorType = $msgTokens[4];
my $logDevAddress = $ioName.'_'.$zmMonitorId;
if ( my $hash = $modules{ZM_Monitor}{defptr}{$logDevAddress} ) {
readingsBeginUpdate($hash);
readingsBulkUpdateIfChanged($hash, 'monitorFunction', $function);
readingsBulkUpdateIfChanged($hash, 'motionDetectionEnabled', $enabled);
my $bufferChanged = readingsBulkUpdateIfChanged($hash, 'streamReplayBuffer', $streamReplayBuffer);
readingsEndUpdate($hash, 1);
$hash->{model} = $monitorType;
ZM_Monitor_UpdateStreamUrls($hash);
return $hash->{NAME};
# } else {
# my $autocreate = "UNDEFINED ZM_Monitor_$logDevAddress ZM_Monitor $zmMonitorId";
# Log3 $io_hash, 5, "logical device with address $logDevAddress not found. returning autocreate: $autocreate";
# return $autocreate;
}
return undef;
}
sub ZM_Monitor_handleMonitorCreation {
my ( $io_hash, $message ) = @_;
my $ioName = $io_hash->{NAME};
my @msgTokens = split(/\|/, $message); #$message = "$monitorId|$monitorType";
my $zmMonitorId = $msgTokens[0];
my $logDevAddress = $ioName.'_'.$zmMonitorId;
if ( my $hash = $modules{ZM_Monitor}{defptr}{$logDevAddress} ) {
$hash->{model} = $msgTokens[1];
return $hash->{NAME};
} else {
my $autocreate = "UNDEFINED ZM_Monitor_$logDevAddress ZM_Monitor $zmMonitorId";
Log3 $io_hash, 5, "logical device with address $logDevAddress not found. returning autocreate: $autocreate";
return $autocreate;
}
return undef;
}
sub ZM_Monitor_WriteEventStreamUrlToReading {
my ( $hash, $streamUrl, $readingName, $authPart, $eventId ) = @_;
my $zmPathZms = $hash->{IODev}{helper}{ZM_PATH_ZMS};
$streamUrl = $streamUrl."/" if (not $streamUrl =~ m/\/$/);
my $zmMonitorId = $hash->{helper}{ZM_MONITOR_ID};
my $imageUrl = $streamUrl."$zmPathZms?mode=single&scale=100&monitor=$zmMonitorId".$authPart;
my $imageReadingName = $readingName;
$imageReadingName =~ s/Stream/Image/g;
readingsBulkUpdate($hash, $imageReadingName, $imageUrl, 1);
$streamUrl = $streamUrl."$zmPathZms?source=event&mode=jpeg&event=$eventId&frame=1&scale=100&rate=100&maxfps=30".$authPart;
readingsBulkUpdate($hash, $readingName, $streamUrl, 1);
}
sub ZM_Monitor_Notify {
my ($own_hash, $dev_hash) = @_;
my $name = $own_hash->{NAME}; # own name / hash
return "" if(IsDisabled($name)); # Return without any further action if the module is disabled
my $devName = $dev_hash->{NAME}; # Device that created the events
my $events = deviceEvents($dev_hash,1);
return if( !$events );
foreach my $event (@{$events}) {
$event = "" if(!defined($event));
Log3 $name, 4, "ZM_Monitor ($name) - Incoming event: $event";
my @msg = split(/\:/, $event, 2);
if ($msg[0] eq 'authHash') {
ZM_Monitor_UpdateStreamUrls($own_hash);
} else {
Log3 $name, 4, "ZM_Monitor ($name) - ignoring";
}
# Examples:
# $event = "readingname: value"
# or
# $event = "INITIALIZED" (for $devName equal "global")
#
# processing $event with further code
}
}
# Eval-Rückgabewert für erfolgreiches
# Laden des Moduls
1;
# Beginn der Commandref
=pod
=item device
=item summary Logical device to change Monitor operation modes in ZoneMinder
=item summary_DE Logisches Modul zum Verändern der Kameraeinstellungen in ZoneMinder
=begin html
ZM_Monitor
Define
define <name> ZM_Monitor <ZM-Monitor ID>
This is usually called by autocreate and triggered by the ZoneMinder IODevice.
Set
alarmState
Puts a monitor into alarm state or out of alarm state via the ZoneMinder trigger port. Can also take one or both of cause="xxx" notes="xxx"
parameters
monitorFunction
Sets the operating mode of a Monitor in ZoneMinder via the ZoneMinder API.
motionDetectionEnabled
Enables or disables monitor detection of a monitor via ZoneMinder API.
text
Allows you to set a text for a Timestamp's %Q
portion in ZoneMinder via the ZoneMinder trigger port.
Attributes
showLiveStreamInDetail
If set to 1
, a live-stream of the current monitor will be shown on top of the FHEMWEB detail page.
Readings
alert
The alert state.
eventImageUrl
Link to the first image of the latest event recording, based on the ZM-Host parameter used in the device definition.
eventStreamUrl
Link to the latest event recording, based on the ZM-Host parameter used in the device definition.
lastEventId
ID of the latest event in ZoneMinder.
lastEventTimestamp
Timestamp of the latest event from ZoneMinder.
monitorFunction
Current operation mode of the monitor.
motionDetectionEnabled
Equals the 'enabled' setting in ZoneMinder. Allows you to put the monitor into a more passive state (according to ZoneMinder documentation).
pubEventImageUrl
Link to the first image of the latest event recording, based on the publicAddress
attribute used in the ZoneMinder device.
pubEventStreamUrl
Link to the latest event recording, based on the publicAddress
attribute used in the ZoneMinder device.
pubImageUrl
Link to the current live image, based on the publicAddress
attribute used in the ZoneMinder device.
pubStreamUrl
Link to the live-stream, based on the publicAddress
attribute used in the ZoneMinder device.
streamReplayBuffer
Taken from the ZoneMinder configuration. Used for the buffer
parameter of stream URLs.
streamUrl
Link to the live-stream, based on the ZM-Host parameter used in the device definition.
=end html
# Ende der Commandref
=cut