feat(onvif): add manual camera URL entry to ONVIF probe#4728
feat(onvif): add manual camera URL entry to ONVIF probe#4728connortechnology merged 4 commits intoZoneMinder:masterfrom
Conversation
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
| 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"/> |
There was a problem hiding this comment.
WHy 40? If just for sizing, better to do with with css.
There was a problem hiding this comment.
Guilty as charged. I learned to code before CSS existed and size was the only way. Fixing it now.
There was a problem hiding this comment.
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
onvifprobestep 1 and tomonitorprobe, plus a “Connect ONVIF” redirect flow frommonitorprobe→onvifprobe. - 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.
| 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'; | ||
| } |
There was a problem hiding this comment.
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.
| <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> |
There was a problem hiding this comment.
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.
| <?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> |
| 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)" |
There was a problem hiding this comment.
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.
| 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)" |
| <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> |
There was a problem hiding this comment.
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.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
feat(onvif): add manual camera URL entry to ONVIF probe
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:
monitorprobe.php / monitorprobe.js:
en_gb.php: