Skip to content

feat(onvif): add manual camera URL entry to ONVIF probe#4728

Merged
connortechnology merged 4 commits intoZoneMinder:masterfrom
voyo:onvif-manual-camera-url
Apr 18, 2026
Merged

feat(onvif): add manual camera URL entry to ONVIF probe#4728
connortechnology merged 4 commits intoZoneMinder:masterfrom
voyo:onvif-manual-camera-url

Conversation

@voyo
Copy link
Copy Markdown
Contributor

@voyo voyo commented Apr 11, 2026

WS-Discovery uses UDP multicast and cannot find cameras on remote subnets. Users with ZoneMinder on a different network from their cameras (e.g. VPN, routed networks) have no way to add ONVIF cameras through the probe UI.

This change adds a free-text "Camera IP / URL" input to both probe pages so users can connect to a known camera address directly, bypassing WS-Discovery entirely.

onvifprobe.php / onvifprobe.js:

  • Add manual_url text input with placeholder showing accepted formats
  • Accept bare IP (e.g. 192.168.1.100) or full URL; normalize bare IPs to http:///onvif/device_service before passing to probeProfiles()
  • Pre-enable Next button when URL arrives as a GET parameter
  • Use inline oninput/onchange handlers for immediate button activation
  • Make credentials optional: many cameras require no authentication

monitorprobe.php / monitorprobe.js:

  • Add the same "Camera IP / URL" input and a "Connect ONVIF" button
  • Clicking Connect ONVIF redirects to onvifprobe with the address pre-filled so the standard two-step profile selection flow takes over
  • Add get_networks() compatibility shim for installations where the function is not yet present in functions.php

en_gb.php:

  • Add OnvifManualOr, OnvifManualLabel, OnvifManualPlaceholder, OnvifManualConnect translation strings
  • Update OnvifCredentialsIntro to indicate credentials are optional

WS-Discovery uses UDP multicast and cannot find cameras on remote
subnets. Users with ZoneMinder on a different network from their
cameras (e.g. VPN, routed networks) have no way to add ONVIF cameras
through the probe UI.

This change adds a free-text "Camera IP / URL" input to both probe
pages so users can connect to a known camera address directly,
bypassing WS-Discovery entirely.

onvifprobe.php / onvifprobe.js:
- Add manual_url text input with placeholder showing accepted formats
- Accept bare IP (e.g. 192.168.1.100) or full URL; normalize bare IPs
  to http://<ip>/onvif/device_service before passing to probeProfiles()
- Pre-enable Next button when URL arrives as a GET parameter
- Use inline oninput/onchange handlers for immediate button activation
- Make credentials optional: many cameras require no authentication

monitorprobe.php / monitorprobe.js:
- Add the same "Camera IP / URL" input and a "Connect ONVIF" button
- Clicking Connect ONVIF redirects to onvifprobe with the address
  pre-filled so the standard two-step profile selection flow takes over
- Add get_networks() compatibility shim for installations where the
  function is not yet present in functions.php

en_gb.php:
- Add OnvifManualOr, OnvifManualLabel, OnvifManualPlaceholder,
  OnvifManualConnect translation strings
- Update OnvifCredentialsIntro to indicate credentials are optional
Comment thread web/skins/classic/views/onvifprobe.php Outdated
data-on-input-this="configureButtons"
oninput="(function(el){var nb=el.form.elements.namedItem('nextBtn');if(nb)nb.disabled=el.value.trim().length===0;})(this)"
onchange="(function(el){var nb=el.form.elements.namedItem('nextBtn');if(nb)nb.disabled=el.value.trim().length===0;})(this)"
size="40"/>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WHy 40? If just for sizing, better to do with with css.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guilty as charged. I learned to code before CSS existed and size was the only way. Fixing it now.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a manual “Camera IP / URL” entry path to the classic-skin ONVIF probe flows so users can connect to a known ONVIF endpoint directly (useful when WS-Discovery can’t reach remote subnets).

Changes:

  • Added manual ONVIF URL input to onvifprobe step 1 and to monitorprobe, plus a “Connect ONVIF” redirect flow from monitorprobeonvifprobe.
  • Updated probe-page button enable/disable logic and made ONVIF credentials optional in the UI.
  • Added English (en_gb) translation strings for the new UI copy.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
web/skins/classic/views/onvifprobe.php Adds manual URL input and adjusts initial “Next” button disabled state; includes a get_networks() shim.
web/skins/classic/views/monitorprobe.php Adds manual URL input + “Connect ONVIF” button; includes a get_networks() shim.
web/skins/classic/views/js/onvifprobe.js Normalizes/serializes manual URL into the existing probe payload; relaxes button gating to allow blank credentials.
web/skins/classic/views/js/monitorprobe.js Enables “Connect ONVIF” when a manual URL is present and redirects into onvifprobe with it prefilled.
web/lang/en_gb.php Adds new translation strings and updates the credentials intro to be optional.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +18 to +25
var manualUrlEl = form.elements.namedItem('manual_url');
var manualUrl = manualUrlEl ? manualUrlEl.value.trim() : '';

if ( manualUrl.length > 0 ) {
// Normalize: bare IP or IP:port -> prepend scheme and standard ONVIF device path
if ( !/^https?:\/\//i.test(manualUrl) ) {
manualUrl = 'http://' + manualUrl + '/onvif/device_service';
}
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gotoStep2 always uses manual_url when it has any content, even if the user also selected a detected camera in the probe dropdown. This makes the manual entry silently override the selected camera, which is likely unintended for an "Or" alternative. Consider only applying manual_url when probe.selectedIndex == 0, or clearing manual_url when a camera is selected.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Comment thread web/skins/classic/views/js/onvifprobe.js Outdated
Comment thread web/skins/classic/views/js/onvifprobe.js Outdated
<button type="button" data-on-click="backWindow"><?php echo translate('Cancel') ?></button>
<button type="button" name="nextBtn" data-on-click-this="gotoStep2" disabled="disabled"><?php echo translate('Next') ?></button>
<button type="button" name="nextBtn" data-on-click-this="gotoStep2"
<?php echo (empty($_REQUEST['manual_url']) ? 'disabled="disabled"' : '') ?>><?php echo translate('Next') ?></button>
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Next button is enabled server-side whenever $_REQUEST['manual_url'] is non-empty, but this does not trim whitespace. A value like ' ' enables Next, while gotoStep2() will treat it as empty (after .trim()), leading to a step 2 submit with no probe and a fatal error. Consider enabling based on trim($_REQUEST['manual_url'] ?? '') instead.

Suggested change
<?php echo (empty($_REQUEST['manual_url']) ? 'disabled="disabled"' : '') ?>><?php echo translate('Next') ?></button>
<?php echo (trim($_REQUEST['manual_url'] ?? '') === '' ? 'disabled="disabled"' : '') ?>><?php echo translate('Next') ?></button>

Copilot uses AI. Check for mistakes.
Comment on lines +219 to +220
oninput="(function(el){var nb=el.form.elements.namedItem('nextBtn');if(nb)nb.disabled=el.value.trim().length===0;})(this)"
onchange="(function(el){var nb=el.form.elements.namedItem('nextBtn');if(nb)nb.disabled=el.value.trim().length===0;})(this)"
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This input defines inline oninput/onchange handlers, but web/skins/classic/js/skin.js binds data-on-input-this/data-on-change-this by assigning to el.oninput/el.onchange, which overrides inline handlers. These inline handlers are therefore dead code and can be removed to avoid confusion.

Suggested change
oninput="(function(el){var nb=el.form.elements.namedItem('nextBtn');if(nb)nb.disabled=el.value.trim().length===0;})(this)"
onchange="(function(el){var nb=el.form.elements.namedItem('nextBtn');if(nb)nb.disabled=el.value.trim().length===0;})(this)"

Copilot uses AI. Check for mistakes.
Comment on lines 488 to 492
<button type="button" name="saveBtn" value="Save" data-on-click-this="submitCamera" disabled="disabled">
<?php echo translate('Save') ?></button>
<button type="button" name="onvifBtn" data-on-click-this="connectOnvif" disabled="disabled">
<?php echo translate('OnvifManualConnect') ?></button>
<button type="button" data-on-click="backWindow"><?php echo translate('Cancel') ?></button>
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

manual_url is prefilled from $_REQUEST, but the "Connect ONVIF" button is always rendered disabled and configureButtons() is only called on change/input events. If the page is loaded with manual_url already set (e.g., via query string), the button stays disabled until the user triggers an event. Consider conditionally omitting disabled when trim($_REQUEST['manual_url'] ?? '') is non-empty, or invoking configureButtons() on page load.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

connortechnology and others added 2 commits April 18, 2026 09:48
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@connortechnology connortechnology merged commit 1deb1cb into ZoneMinder:master Apr 18, 2026
1 of 4 checks passed
connortechnology added a commit that referenced this pull request Apr 18, 2026
feat(onvif): add manual camera URL entry to ONVIF probe
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants