<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Foscam API</title> <meta name="description" content="A simple HTML page to control a Foscam API compatible camera. Foscam-IPCamera-CGI-User-Guide (PDF) 1.0.16. All platform ( + except 3518A ?)."> <meta name="author" content="hyttysmyrkky"> <style> body {background-color: #1d2d47;} h1 {color: white; font-size: 10px;} h2 {color: white; font-size: 16px;} h3 {color: #e0e9ff; font-size: 14px; margin-bottom:0; padding-bottom: 0px; padding-top: 15px;} p {color: silver; font-size: 13px;} p.autogenerated {color: lime; font-size: 13px; margin:0; padding:4px;} p.viewer { color: white; font-size: 12px; padding:0px; font-weight: bold; position: relative; z-index: 1000; background-color: black; opacity : 0.7; } iframe {background-color: white;} form {color: white;} form.autogenerated {padding-top: 0px;} input.largerCheckbox { width: 60px; height: 40px; } input.autogenerated {margin:2px; padding:2px;} label {color: silver; font-size: 13px;} label.autogenerated {margin:2px; padding:2px;} button.danger { color: red; } button.sendToCamera { color: blue; } button.showhide { background-color: white; font-size: 12px; } button.viewer { background-color: white; font-size: 9px; font-weight: bold; position: relative; z-index: 1000; opacity : 0.7; } button.ptz { width: 100%; height: 100%; } button.ptz_preset { padding: 6px; } table.ptz { width: 100%; max-width: 600px; } td.ptz { text-align: center; color: silver; font-size: 12px; } input.sendToCamera { color: blue; } select.cameraselector {background-color: lime; padding:4px;} table.checkboxImageTable, table.checkboxScheduleTable, table.checkboxLinkageTable, table.help, table.ViewerSetupOptions { border: 4px solid; border-collapse: collapse; background-position: center; -moz-background-size: 100% 100%; -webkit-background-size: 100% 100%; background-size: 100% 100%; } th.checkboxImageTable, tr.checkboxImageTable, td.checkboxImageTable { color: silver; font-size: 13px; margin:0; padding-left: 12px; padding-right: 12px; padding-top: : 20px; padding-bottom: : 20px; border:solid 1px #000000; } input.checkboxImageTable { border: none; outline: none; position: relative; opacity : 0.5; -ms-transform: scale(2, 1.5); /* IE */ -moz-transform: scale(2, 1.5); /* FF */ -webkit-transform: scale(2, 1.5); /* Safari and Chrome */ -o-transform: scale(2, 1.5); /* Opera */ transform: scale(2, 1.5); } th.checkboxScheduleTable, tr.checkboxScheduleTable, td.checkboxScheduleTable { color: silver; font-size: 10px; margin:0; border:solid 1px #000000; } th.checkboxLinkageTable, tr.checkboxLinkageTable, td.checkboxLinkageTable, tr.help, td.help, tr.ViewerSetupOptions, td.ViewerSetupOptions { color: silver; font-size: 13px; border:solid 1px #000000; padding: 5px; } pre { color: white; } a:link, a:visited { color: silver; } a:hover, a:active { color: white; } img.operate { max-width: 100vw; max-height: 60vh; } @media (min-aspect-ratio: 1/1) { img.viewer { width: 100vw; height: 100vh; padding: 0px; position: absolute; left: 0px; top: 0px; } } @media (max-aspect-ratio: 1/1) { img.viewer { max-width: 100vw; max-height: 100vh; padding: 0px; position: absolute; left: 0px; top: 0px; } } .viewerContainer { border: 0px; position: relative; padding: 0px; margin: 0; } .zeropadding { padding: 0px; margin: 0; } .somepadding { padding: 3px; margin: 3px; } </style> </head> <body onload="main();" class="zeropadding"> <div id="headerButtons" class="somepadding"> <h1>FOSCAM API</h1> <label>Show/Hide:</label> <button onclick="toggleView('Setup')" id="SetupToggle" class="showhide">Add/Remove camera</button> <button onclick="toggleView('InitialSetup')" id="InitialSetupToggle" class="showhide">Initial setup & Help</button> <button onclick="toggleView('Status')" id="StatusToggle" class="showhide">Status</button> <button onclick="toggleView('Basic')" id="BasicToggle" class="showhide">Basic</button> <button onclick="toggleView('Network')" id="NetworkToggle" class="showhide">Network</button> <button onclick="toggleView('Video')" id="VideoToggle" class="showhide">Video</button> <button onclick="toggleView('Detector')" id="DetectorToggle" class="showhide">Detector</button> <button onclick="toggleView('Record')" id="RecordToggle" class="showhide">Record</button> <button onclick="toggleView('System')" id="SystemToggle" class="showhide">System</button> <button onclick="toggleView('PTZ')" id="PTZToggle" class="showhide">PTZ</button> <button onclick="toggleView('Operate')" id="OperateToggle" class="showhide">Operate</button> <button onclick="toggleView('Custom')" id="CustomToggle" class="showhide">Custom</button> <button onclick="toggleView('ViewerSetup')" id="ViewerSetupToggle" class="showhide">Viewer Setup</button> <button onclick="toggleViewerView()" id="ViewerToggle" class="showhide">Viewer</button> <br> <label for="selectedCamera">Select the camera to control:</label> <select name="selectedCamera" id="selectedCamera" class="cameraselector" onchange="selectCamera()"></select> <label for="onlyShowCmdUrlCheckbox">Only show command URL</label> <input type="checkbox" id="onlyShowCmdUrlCheckbox"> </div> <div id="Setup" style="display: none;" class="somepadding"> <hr> <h2>Add new or edit</h2> <p>Save a camera (its address and credentials) for this user interface. Data is saved to localStorage only on the browser you are using. Requests to cameras use unencrypted HTTP and the credentials are clear-text and show up in the browser page history. Use this web page only on your personal machine, and so that the cameras are accessed via LAN (or VPN or similar). On the other hand, if you can access your cameras over the internet without VPN or similar, you should reconfigure your firewalls.</p> <form id="form" onsubmit="return false;" target="_blank"> <label for="cameraIp">IP address of the camera: (after connecting the camera, find this e.g. in router settings)</label><br> <input type="text" id="cameraIp" name="cameraIp" placeholder="192.168.12.34"><br> <label for="cameraPort">Port: (default for Opticam: 88)</label><br> <input type="text" id="cameraPort" name="cameraPort" placeholder="88"><br><br> <input type="checkbox" id="cameraIsOldSd" name="cameraIsOldSd"> <label for="cameraIsOldSd">The camera is an older SD (usually VGA resolution) camera (uses IP Camera CGI V1.27 API)</label><br><br> <label for="cameraName">Name (optional & only for this UI)</label><br> <input type="text" id="cameraName" name="cameraName" placeholder=""><br><br> <label for="cameraUser">Username: (factory default: admin)</label><br> <input type="text" id="cameraUser" name="cameraUser" placeholder="admin"><br> <label for="cameraPass">Password: (factory default is empty)</label><br> <input type="password" id="cameraPass" name="cameraPass" value=""><br><br> <input type="submit" onclick="setupSaveCamera();" value="Add/Save camera"/> </form> <br> <p>After resetting (or when setupping a new) camera, the default user is 'admin', password empty. Use this to replace that user with the username/password given above. NOTE: This only reads the IP/username/password fields above, and does NOT use the selected camera.</p> <button id="replaceFactoryUser" class="sendToCamera" onclick="replaceFactoryUser()">Change factory-default user/password with credentials above</button> <br><br> <h2>Remove</h2> <p>Choose the camera to remove from the green 'Select the camera to control' dropdown list at the top of the page. Deleting a camera only affects this user interface (and not the camera hardware) and deletes the data you have saved with the 'Add new or edit' above.</p> <button onclick="deleteCamera()">Delete selected camera</button> <br><br> <h2>Remove all</h2> <p>Reset all data saved for this UI (in localStorage). (Does not affect camera hardware in any way.)</p> <button onclick="resetGlobals()" class="danger">Reset saved data</button> <h2>Export/Import</h2> <p>Export the data saved for this UI (in localStorage). When importing the exported file, all current settings will be lost. (Does not affect camera hardware in any way.) Warning: The exported file will contain all camera credentials as clear-text.</p> <a id="downloadStart" style="display:none" download="FoscamApiExportedSettings.json">.</a> <button onclick="exportLocalStorage()">Export data (localStorage) to file and download</button><br> <button onclick="debug()">Print data to console</button><br> <h3>Import</h3> <form id="importSettingsFileForm"> <label for="selectedImportFile">Select the settings file: </label> <input type="file" id="selectedImportFile" accept=".json"> <button>Import</button> </form> <br><br> </div> <div id="InitialSetup" style="display: none;" class="somepadding"> <hr> <div> <h2>Help</h2> <p>This user interface for the Foscam API aims to be a portable, simple and easy-to-modify tool to install and manage Foscam API compatible cameras. Older SD/VGA cameras (IP Camera CGI V1.27) are not yet fully supported, and all the documentation etc. are for HD cameras (Foscam IPCamera CGI > 1.0.16).</p> <p>This html file only requires JavaScript to work, and has no external dependencies. This on the other hand means quite a raw and clumsy user experience.</p> <p>Commands are sent to the camera by opening the parsed URL as a new tab. The camera returns a response as an XML which you can see on the new tab. Therefore, some popups must be allowed, and also it makes it impossible to automatically process the response, unfortunately.</p> <p>If you want to only see the parsed URL and not send the command when clicking the buttons with blue text, select the "Only show command URL" checkbox". On Linux you can turn the URL into a curl command with this syntax:</p> <pre>curl 'PasteFullUrlHere'</pre> <p>The RTSP video stream can be accessed with this kind of URLs (main, sub, audio):</p> <pre>rtsp://user:password@192.168.1.123:88/videoMain</pre> <pre>rtsp://user:password@192.168.1.123:88/videoSub</pre> <pre>rtsp://user:password@192.168.1.123:88/audio</pre> <p>If you are looking for a setting, you can show all categories by clicking the buttons at the top of this page and then use the search functionality of your browser. This user interface cannot know the model of your camera, so for example Wifi and PTZ (pan tilt zoom) settings are always visible but only work for cameras that support them. For more information about how this html file itself was developed, see the readme in <a href="https://github.com/hyttysmyrkky/foscam_cgi_api_html">GitHub: hyttysmyrkky/foscam_cgi_api_html</a>.</p> <h3>How to interpret the response from camera</h3> <p>The camera responds with an XML, for example when querying the IR LED config (getInfraLedConfig):</p> <pre> <CGI_Result> <result>0</result> <mode>0</mode> </CGI_Result> </pre> <p><result> 0 means success. Other codes for 'result':</p> <table class="help"> <tr class="help"> <th class="help"><result></th> <th class="help">Meaning</th> </tr> <tr class="help"> <td class="help">0</td> <td class="help">Success</td> </tr> <tr class="help"> <td class="help">-1</td> <td class="help">CGI request string format error</td> </tr> <tr class="help"> <td class="help">-2</td> <td class="help">Username or password error</td> </tr> <tr class="help"> <td class="help">-3</td> <td class="help">Access denied</td> </tr> <tr class="help"> <td class="help">-4</td> <td class="help">CGI execute fail</td> </tr> <tr class="help"> <td class="help">-5</td> <td class="help">Timeout</td> </tr> <tr class="help"> <td class="help">-7</td> <td class="help">Unknown error</td> </tr> </table> <p>The rest of the returned values depend on the command. If you look at the corresponding setter commands (in this case 'setInfraLedConfig'), most of them have a dropdown list of the options for the given parameter. From that list you can figure out the meaning of 0, 1, 2 and so on.</p> <p>In some cases you have to look at the Foscam API PDF documentation.</p> <h2>Initial setup</h2> <p>In the table below there is a collection of the most commonly required commands when setupping a new camera. If you already changed the factory-default user in the 'Add/Remove camera' step, you can skip the 'changeUserNameAndPwdTogether' below. If you are setupping multiple cameras, take a look at the Custom view: There you can save the parameters (e.g. FTP server settings) and send them easily to all cameras.</p> <table class="help"> <tr class="help"> <th class="help">Description</th> <th class="help">Getter (read) command</th> <th class="help">Setter (write) command</th> </tr> <tr class="help"> <td class="help">Get users, change the factory-default</td> <td class="help">getUserList</td> <td class="help">changeUserNameAndPwdTogether</td> </tr> <tr class="help"> <td class="help">Test get a still image</td> <td class="help">snapPicture2</td> <td class="help"></td> </tr> <tr class="help"> <td class="help">Camera name</td> <td class="help">getDevName</td> <td class="help">setDevName</td> </tr> <tr class="help"> <td class="help">Wifi settings (if your camera model supports wifi)</td> <td class="help">getWifiConfig</td> <td class="help">setWifiSetting</td> </tr> <tr class="help"> <td class="help">Clock settings</td> <td class="help">getSystemTime</td> <td class="help">setSystemTime</td> </tr> <tr class="help"> <td class="help">FTP settings</td> <td class="help">getFtpConfig</td> <td class="help">setFtpConfig</td> </tr> <tr class="help"> <td class="help">P2P settings (disable if not needed)</td> <td class="help">getP2PEnable</td> <td class="help">setP2PEnable</td> </tr> <tr class="help"> <td class="help">Email alarm settings when motion is detected</td> <td class="help">getSMTPConfig</td> <td class="help">setSMTPConfig</td> </tr> <tr class="help"> <td class="help">Snapshot settings, select save location SD card or FTP</td> <td class="help">getSnapConfig</td> <td class="help">setSnapConfig</td> </tr> <tr class="help"> <td class="help">Motion detection settings</td> <td class="help">getMotionDetectConfig</td> <td class="help">setMotionDetectConfig</td> </tr> <tr class="help"> <td class="help">Recording settings, select save location SD card or FTP</td> <td class="help">getRecordPath</td> <td class="help">setRecordPath</td> </tr> <tr class="help"> <td class="help">Length of recorded video clip</td> <td class="help">getAlarmRecordConfig</td> <td class="help">setAlarmRecordConfig</td> </tr> <tr class="help"> <td class="help">Cloud settings (disable if not needed)</td> <td class="help">getCloudConfig</td> <td class="help">setCloudConfig</td> </tr> <tr class="help"> <td class="help">OSD settings (text overlay on image)</td> <td class="help">getOSDSetting</td> <td class="help">setOSDSetting</td> </tr> <tr class="help"> <td class="help">PTZ presets, if your camera supports PTZ. Add a new preset.</td> <td class="help">getPTZPresetPointList</td> <td class="help">ptzAddPresetPoint</td> </tr> <tr class="help"> <td class="help">PTZ mode when camera boots (2 = go to preset)</td> <td class="help">getPTZSelfTestMode</td> <td class="help">setPTZSelfTestMode</td> </tr> <tr class="help"> <td class="help">PTZ preset to go at boot</td> <td class="help">getPTZPrePointForSelfTest</td> <td class="help">setPTZPrePointForSelfTest</td> </tr> <tr class="help"> <td class="help">Brightness</td> <td class="help">getImageSetting</td> <td class="help">setBrightness</td> </tr> <tr class="help"> <td class="help">Resolution and bitrate: currently active preset ("streamType")</td> <td class="help">getMainVideoStreamType</td> <td class="help">setMainVideoStreamType</td> </tr> <tr class="help"> <td class="help">Resolution and bitrate settings</td> <td class="help">getVideoStreamParam</td> <td class="help">setVideoStreamParam</td> </tr> <tr class="help"> <td class="help">Reboot camera</td> <td class="help">rebootSystem</td> <td class="help"></td> </tr> </table> </div> </div> <div id="Status" style="display: none;" class="somepadding"> <hr><h2>Status</h2> </div> <div id="Basic" style="display: none;" class="somepadding"> <hr><h2>Basic</h2> </div> <div id="Network" style="display: none;" class="somepadding"> <hr><h2>Network</h2> </div> <div id="Video" style="display: none;" class="somepadding"> <hr><h2>Video</h2> </div> <div id="Detector" style="display: none;" class="somepadding"> <hr><h2>Detector</h2> <div> <h3 class="cmdTitle">Motion detection area and schedule</h3> <p class="description">Motion will be detected in the checked boxes. You can set a snapshot image from the camera to the background of the table by clicking the 'Browse'. Before that you have to manually get the image (see the 'Operate' view) and save the image as jpg. When the correct area is selected, click the 'Apply area' button which will read the values from the table to the parameter fields of 'setMotionDetectConfig' below. Then set the other parameters and click 'Send to camera'.</p> <p class="description">This UI cannot read the currently active setting from the camera automatically, but the inputted setting is saved to localStorage for the selected camera.</p> <div id="checkboxImageTable"> <input type="file" id="image_input" accept="image/jpg"> <button onclick="motionDetectAreaTable.setAll(true)">Select all</button> <button onclick="motionDetectAreaTable.setAll(false)">Deselect all</button> <button onclick="motionDetectAreaTable.applyToCmdParams()">Apply area</button> </div> <div id="checkboxScheduleTable"> <p class="description">Motion detection is enabled during every selected (starting) half an hour. Click 'Apply schedule' to read the table values to the parameter fields below.</p> <button onclick="motionDetectScheduleTable.setAll(true)">Select all</button> <button onclick="motionDetectScheduleTable.setAll(false)">Deselect all</button> <button onclick="motionDetectScheduleTable.applyToCmdParams()">Apply schedule</button> </div> <div id="checkboxLinkageTable"> <p class="description">The linkage (or action) what happens when motion is detected, is also defined the same way. Click 'Apply linkage' to read the table values to the parameter fields below.</p> <button onclick="motionDetectLinkageTable.setAll(true)">Select all</button> <button onclick="motionDetectLinkageTable.setAll(false)">Deselect all</button> <button onclick="motionDetectLinkageTable.applyToCmdParams()">Apply linkage</button> </div> </div> </div> <div id="Record" style="display: none;" class="somepadding"> <hr><h2>Record</h2> </div> <div id="System" style="display: none;" class="somepadding"> <hr><h2>System</h2> <h3>importConfig</h3> <p>(Not tested, may not work at all?)</p> <form name="form5" method="post" target="retframe" enctype="multipart/form-data" action="javascript:getUrl('importConfig');"> <input type="file" name="setting_file"> <input type="submit" value="import"> </form> <h3>fwUpgrade</h3> <p>(Not tested, so commented-out for now.)</p> <!-- TODO: NOT TESTED and potentially harmful, so commented-out now. <form name="form5" method="post" target="retframe" enctype="multipart/form-data" action="javascript:getUrl('fwUpgrade');"> <input type="file" name="fw_file"> <input type="submit" value="upgrade"> </form> --> </div> <div id="PTZ" style="display: none;" class="somepadding"> <hr><h2>PTZ</h2> </div> <div id="Operate" style="display: none;" class="somepadding"> <hr><h2>Operate</h2> <div> <button onclick="getStillImage()" id="operateViewSnapshotButton">Get/Refresh the image (also by clicking the image)</button> <br> <img onclick="getStillImage()" class="operate" id="operateViewSnapshot" src=""/> <br> <table class="ptz"> <tr> <td colspan="2"> <button onpointerdown="ptz('ptzMoveUp')" onpointerup="ptz('ptzStopRun')" class="ptz">^</button> </td> <td class="ptz">Zoom</td> <td class="ptz">Focus</td> </tr> <tr> <td> <button onpointerdown="ptz('ptzMoveLeft')" onpointerup="ptz('ptzStopRun')" class="ptz"><</button> </td> <td> <button onpointerdown="ptz('ptzMoveRight')" onpointerup="ptz('ptzStopRun')" class="ptz">></button> </td> <td> <button onpointerdown="ptz('zoomIn')" onpointerup="ptz('zoomStop')" class="ptz">+ in</button> </td> <td> <button onpointerdown="ptz('focusFar')" onpointerup="ptz('focusStop')" class="ptz">far</button> </td> </tr> <tr> <td colspan="2"> <button onpointerdown="ptz('ptzMoveDown')" onpointerup="ptz('ptzStopRun')" class="ptz">v</button> </td> <td> <button onpointerdown="ptz('zoomOut')" onpointerup="ptz('zoomStop')" class="ptz">- out</button> </td> <td> <button onpointerdown="ptz('focusNear')" onpointerup="ptz('focusStop')" class="ptz">near</button> </td> </tr> </table> <pre id="ptzShowSentCmd" class="ptz">-</pre> <button onclick="ptz('openInfraLed')">IR on</button> <button onclick="ptz('setInfraLedConfig', {'mode':'0'})">IR auto</button> <button onclick="ptz('closeInfraLed')">IR off</button> <br><br> <label for="gotoPreset1">Presets:</label> <button onclick="ptz('ptzGotoPresetPoint', {'name':'1'})" class="ptz_preset" id="gotoPreset1" >1</button> <button onclick="ptz('ptzGotoPresetPoint', {'name':'2'})" class="ptz_preset">2</button> <button onclick="ptz('ptzGotoPresetPoint', {'name':'3'})" class="ptz_preset">3</button> <button onclick="ptz('ptzGotoPresetPoint', {'name':'4'})" class="ptz_preset">4</button> <button onclick="ptz('ptzGotoPresetPoint', {'name':'5'})" class="ptz_preset">5</button> <button onclick="ptz('ptzGotoPresetPoint', {'name':'6'})" class="ptz_preset">6</button> <button onclick="ptz('ptzGotoPresetPoint', {'name':'7'})" class="ptz_preset">7</button> <button onclick="ptz('ptzGotoPresetPoint', {'name':'8'})" class="ptz_preset">8</button> <button onclick="ptz('ptzGotoPresetPoint', {'name':'9'})" class="ptz_preset">9</button> <button onclick="ptz('ptzGotoPresetPoint', {'name':'0'})" class="ptz_preset">0</button> <button onclick="ptz('ptzGotoPresetPoint', {'name':'a'})" class="ptz_preset">a</button> <button onclick="ptz('ptzGotoPresetPoint', {'name':'b'})" class="ptz_preset">b</button> <br><br> <label for="addPresetSelected">Set/Add preset (to be the current position):</label> <select name="addPresetSelected" id="addPresetSelected"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> <option value="4">4</option> <option value="5">5</option> <option value="6">6</option> <option value="7">7</option> <option value="8">8</option> <option value="9">9</option> <option value="0">0</option> <option value="a">a</option> <option value="b">b</option> </select> <button onclick="ptz('ptzAddPresetPoint')">Set</button> <br> <img height="0" width="0" id="operateViewDummyForPtz" src=""/> <br> <label for="rtspMain">Open stream (RTSP):</label> <button onclick="openRtspStream('videoMain')" id="rtspMain">Video, main</button> <button onclick="openRtspStream('videoSub')" id="rtspSub">Video, sub</button> <button onclick="openRtspStream('audio')" id="rtspAudio">Audio</button> </div> </div> <div id="Custom" style="display: none;" class="somepadding"> <hr><h2>Custom</h2> <p>Here you can essentially save a set of parameters to easily send the same command later. For example, useful for making buttons "Enable motion detection" and "Disable motion detection".</p> <div id="customCmdControls"> </div> <h3>Add new</h3> <label for="selectedCustomCommand">Select the command:</label> <select name="selectedCustomCommand" id="selectedCustomCommand" class="commandselector" onchange="selectCustomCommand()"></select> <div id="customCmdControlCreate"> </div> <br> <label for="addCustomCommandName">Name or description for the custom command:</label><br> <input type="text" id="addCustomCommandName" name="addCustomCommandName" placeholder="e.g. set my time zone"><br> <label for="addCustomCommandFixedCamera">Only for one (currently selected) camera</label> <input type="checkbox" id="addCustomCommandFixedCamera" name="addCustomCommandFixedCamera"><br> <button onclick="addCustomCommand()" id="addCustomCommandButton">Add</button> </div> <div id="ViewerSetup" style="display: none;" class="somepadding"> <hr><h2>Viewer Setup</h2> <p>The Viewer displays still images from the selected cameras in a loop. Here you can set which cameras are included.</p> <div id="ViewerSetupOptions"></div> <br> <label for="viewerRefreshInterval">Viewer refresh interval (in seconds)</label> <input type="text" oninput="this.value = this.value.replace(/[^0-9.]/g, ''); this.value = this.value.replace(/(\..*)\./g, '$1'); this.value = Math.round(this.value);" id="viewerRefreshInterval"><br> <label for="viewerConsecutiveImages">Number of consecutive images from one camera</label> <input type="text" oninput="this.value = this.value.replace(/[^0-9.]/g, ''); this.value = this.value.replace(/(\..*)\./g, '$1'); this.value = Math.round(this.value);" id="viewerConsecutiveImages"><br><br> <button onclick="saveViewerSettings()" id="SaveViewerSetupOptions">Save and apply</button> </div> <div id="Viewer" class="viewerContainer" style="display: none;"> <button onclick="toggleViewerView()" id="toggleViewerBackButton" class="viewer"><- Back</button> <button onclick="viewer.jumpToPreviousCamera()" class="viewer">Previous camera</button> <button onclick="viewer.jumpToNextCamera()" class="viewer">Next camera</button> <button onclick="viewer.pause()" class="viewer">Pause</button> <button onclick="viewer.continue()" class="viewer">Continue</button> <button onclick="viewer.manualRefreshImage()" class="viewer">Refresh (click image)</button> <p class="viewer" style="display:inline" id="ViewerStatus"> Select some cameras in the Viewer Setup to start the Viewer. </p> <br> <img class="viewer" id="viewerImage" onload="viewer.imageLoaded()" onerror="viewer_imageLoadError()" onclick="viewer.manualRefreshImage()" src=""/> </div> <script> let globals; const globalsVersion = 4; // Increment this when globals is changed so that a migration (or resetGlobals()) is needed. let logDebugLevel = false; let viewerSetupCameraTable; let viewer; class View { constructor(defaulVisible = false, apiCommands = []) { this.isVisible = defaulVisible; this.apiCommands = apiCommands; } } class Camera { constructor(ip, user, password, cameraName = "", port = 88, isOldSdCamera = false) { this.ip = ip; this.port = port; this.user = user; this.password = password; this.cameraName = cameraName; this.isOldSdCamera = isOldSdCamera; // old standard definition cameras use a different API (IP Camera CGI V1.27) this.keyName = ip+":"+port+" ("+user+") "+ cameraName; this.motionDetectAreaTableState = "1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"; this.motionDetectScheduleTableState = "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"; this.motionDetectLinkageTableState = "0111"; this.ViewerSetupOptions = "0"; } } // General functions for UI and saving/loading settings: function main(){ // Load the globals, or create, if not exist: globals = JSON.parse(localStorage.getItem('globals')); if (globals === null){ logDebug("Globals was null"); resetGlobals(); } migrateGlobals(globalsVersion); // Set the default contents of the views: setDefaultViewContents(); // Set the selectedCamera which defines the camera being controlled: var selectCameraDropdown = document.getElementById("selectedCamera"); selectCameraDropdown.length = 0; if (Object.keys(globals.setup.cameras).length <= 0){ globals.selectedCamera = null; } else { // Set the available cameras to the dropdown: for (const [key, value] of Object.entries(globals.setup.cameras)){ selectCameraDropdown.add(new Option(key, key)); } if (globals.selectedCamera === null){ globals.selectedCamera = Object.keys(globals.setup.cameras)[0]; } selectCameraDropdown.value = globals.selectedCamera; // Load the motion detection area and schedule table settings of the selected camera: motionDetectAreaTable.load(globals.setup.cameras[globals.selectedCamera].motionDetectAreaTableState); motionDetectScheduleTable.load(globals.setup.cameras[globals.selectedCamera].motionDetectScheduleTableState); motionDetectLinkageTable.load(globals.setup.cameras[globals.selectedCamera].motionDetectLinkageTableState); } // Construct views: generateViews(); // Show or hide the views: refreshViewVisibility(null); saveGlobals(); } function resetGlobals(){ logDebug("Resetting globals."); globals = { "setup" : { "cameras": {}, }, "selectedCamera" : null, "views" : { "Setup" : new View(true), "InitialSetup" : new View(false), "Status" : new View(false), "Basic" : new View(false), "Network" : new View(false), "Video" : new View(false), "Detector" : new View(false), "Record" : new View(false), "System" : new View(false), "PTZ" : new View(false), "Operate" : new View(false), "Custom" : new View(false), "ViewerSetup" : new View(false), }, "customCommands" : {}, // The saved buttons of the Custom view "viewerConsecutiveImages" : 3, "viewerRefreshInterval_ms" : 4000, "viewerIsEnabled" : false, "version" : globalsVersion, } globals.views.Setup.isVisible = true; logDebug(globals); saveGlobals(); main(); // or reload? window.location.reload(); } // Migrations for the globals in localStorage: function migrateGlobals(toVersion) { if ("version" in globals) { var currentGlobalsVersion = globals.version; } else { var currentGlobalsVersion = 0; } // migrations only up possible: while (currentGlobalsVersion < toVersion) { if (currentGlobalsVersion == 0) { console.log("Migrating globals from v. 0 to 1"); globals.views.ViewerSetup = new View(false); // add a new View } else if (currentGlobalsVersion == 1) { console.log("Migrating globals from v. 1 to 2"); globals.viewerConsecutiveImages = 3; globals.viewerRefreshInterval = 4000; // add a couple of persisted settings } else if (currentGlobalsVersion == 2) { console.log("Migrating globals from v. 2 to 3"); globals.viewerIsEnabled = false; // add more settings } else if (currentGlobalsVersion == 3) { console.log("Migrating globals from v. 3 to 4"); globals.viewerRefreshInterval_ms = globals.viewerRefreshInterval; // rename to be unique and have the unit } currentGlobalsVersion++; globals.version = currentGlobalsVersion; logDebug(globals); } saveGlobals(); } function setDefaultViewContents() { globals.views.Status.apiCommands = ["getDevName", "getDevInfo", "getDevState", "getSessionList", "getFirewallConfig", "getLog"]; globals.views.Basic.apiCommands = ["getDevName", "setDevName", "getSystemTime", "setSystemTime", "getUserList", "addAccount", "delAccount", "changeUserName", "changePassword", "changeUserNameAndPwdTogether"]; globals.views.Network.apiCommands = ["getIPInfo", "setIpInfo", "getWifiConfig", "setWifiSetting", "getSMTPConfig", "setSMTPConfig", "smtpTest", "getFtpConfig", "setFtpConfig", "testFtpServer", "getPortInfo", "setPortInfo", "getP2PEnable", "setP2PEnable", "getP2PPort", "setP2PPort", "getP2PInfo", "getDDNSConfig", "setDDNSConfig"]; globals.views.Video.apiCommands = ["getMainVideoStreamType", "setMainVideoStreamType", "getVideoStreamParam", "setVideoStreamParam", "getSubVideoStreamType", "setSubVideoStreamType", "getSubVideoStreamParam", "setSubVideoStreamParam", "getOSDSetting", "setOSDSetting", "getSnapConfig", "setSnapConfig", "getScheduleSnapConfig", "setScheduleSnapConfig", "getImageSetting", "setHue", "setBrightness", "setContrast", "setSaturation", "setSharpness", "setPwrFreq", "getMirrorAndFlipSetting", "mirrorVideo", "flipVideo"]; globals.views.Detector.apiCommands = ["setMotionDetectConfig", "getMotionDetectConfig", "getAudioAlarmConfig", "setAudioAlarmConfig"]; globals.views.Record.apiCommands = ["getRecordPath", "setRecordPath", "getAlarmRecordConfig", "setAlarmRecordConfig", "getRecordList", "getRecordList2", "getScheduleRecordConfig", "setScheduleRecordConfig", "getMultiDevList", "getMultiDevDetailInfo", "addMultiDev", "delMultiDev", "getDeFrameLevel", "setDeFrameLevel", "getCloudConfig", "setCloudConfig", "selectCloudServer", "getCloudToken", "getCloudQuota", "testCloudServer"]; globals.views.System.apiCommands = ["exportConfig", "restoreToFactorySetting", "rebootSystem"]; globals.views.PTZ.apiCommands = ["getPTZSpeed", "setPTZSpeed", "getPTZPresetPointList", "ptzAddPresetPoint", "ptzDeletePresetPoint", "ptzGotoPresetPoint", "getPTZSelfTestMode", "setPTZSelfTestMode", "getPTZPrePointForSelfTest", "setPTZPrePointForSelfTest", "getZoomSpeed", "setZoomSpeed", "ptzGetCruiseMapList", "ptzGetCruiseMapInfo", "ptzSetCruiseMap", "ptzDelCruiseMap", "ptzStartCruise", "ptzStopCruise", "getCruiseTime", "setCruiseTime", "getCruiseTimeCustomed", "setCruiseTimeCustomed", "getCruiseLoopCnt", "setCruiseLoopCnt", "getCruiseCtrlMode", "setCruiseCtrlMode", "getCruisePrePointLingerTime", "setCruisePrePointLingerTime"]; globals.views.Operate.apiCommands = ["snapPicture2", "snapPicture", "getInfraLedConfig", "setInfraLedConfig", "openInfraLed", "closeInfraLed", "zoomIn", "zoomOut", "zoomStop", "ptzMoveUp", "ptzMoveDown", "ptzMoveLeft", "ptzMoveRight", "ptzStopRun", "focusFar", "focusNear", "focusStop"]; } function saveGlobals(){ localStorage.setItem("globals", JSON.stringify(globals)); } // Functions to handle views: function toggleView(viewName){ globals.views[viewName]["isVisible"] = !globals.views[viewName]["isVisible"]; refreshViewVisibility(viewName); saveGlobals(); } function toggleViewerView() { // A special toggle which enables and disables the Viewer and its timer essentially. if (!viewer.enabled) { viewer.enable(); } else { viewer.disable(); } refreshViewVisibility(null); } function refreshViewVisibility(viewName){ if (viewName === null) { // Refresh all for (const [key, value] of Object.entries(globals.views)){ if (!viewer.enabled) { // Normal case. var display = globals.views[key]["isVisible"] ? "block" : "none"; } else { var display = "none"; // If Viewer is enabled, hide everything else. } document.getElementById(key).style.display = display; // Set the toggle button color: document.getElementById(key+"Toggle").style = globals.views[key]["isVisible"] ? "background-color:#ffd557": "background-color:white"; } // If Viewer is enabled, hide everything else. document.getElementById("headerButtons").style.display = viewer.enabled ? "none" : "block"; document.getElementById("Viewer").style.display = viewer.enabled ? "block" : "none"; } else { var display = globals.views[viewName]["isVisible"] ? "block" : "none"; document.getElementById(viewName).style.display = display; // Set the toggle button color: document.getElementById(viewName+"Toggle").style = globals.views[viewName]["isVisible"] ? "background-color:#ffd557": "background-color:white"; } } function generateViews(){ // Delete old to prevent duplicates from appearing: document.querySelectorAll('.autogenerated').forEach(e => e.remove()); for (const [key, value] of Object.entries(globals.views)){ generateView(key, value); } // Create the Custom view as a special case: // Populate the dropdown of the 'Add new': selectedCustomCommandDropdown = document.getElementById("selectedCustomCommand") selectedCustomCommandDropdown.length = 0; for (const key of Object.keys(commandJson).sort()){ selectedCustomCommandDropdown.add(new Option(key, key)); } // Also create the saved buttons: refreshCustomCmdControlsView(); // The Viewer as another special case: // Create the list of available cameras to ViewerSetup: document.getElementById("ViewerSetupOptions").innerHTML = ""; // Clear old stuff var allAvailableCameraKeys = Object.keys(globals.setup.cameras); // Create the settings table, and load the saved state: viewerSetupCameraTable = new checkboxTable(["enabled"], allAvailableCameraKeys, "ViewerSetupOptions", "viewerSetupCameraTable", "vs", "vse", true); // not completely suitable class for this... viewerSetupCameraTable.loadRowdataFromCameras(); // Load other saved settings to the text fields: document.getElementById("viewerConsecutiveImages").value = globals.viewerConsecutiveImages; document.getElementById("viewerRefreshInterval").value = globals.viewerRefreshInterval_ms / 1000; // Create the Viewer itself: viewer = new Viewer(globals.viewerRefreshInterval_ms, globals.viewerConsecutiveImages, globals.viewerIsEnabled); // The active/enabled state of the Viewer is persisted in the globals. If enabled, start timers etc: if (viewer.enabled) { viewer.enable(); } } function generateView(viewName, view){ var viewElement = document.getElementById(viewName); // Dynamically create a control for each listed API command: view.apiCommands.forEach(function (cmd, index) { // Header, e.g. "setSaturation" var header = document.createElement('h3'); header.setAttribute('class', "autogenerated"); header.append(cmd); viewElement.appendChild(header); // Description what the API command does var descriptionText = document.createElement('p'); descriptionText.append(commandJson[cmd]["Function"]); descriptionText.setAttribute('class', "autogenerated"); viewElement.appendChild(descriptionText); // Create the command parameter fields and/or the send button: createCommandControl(viewElement, cmd); }); } function createCommandControl(viewElement, cmd, addSendButton = true, idSuffix = "") { var exampleParams = commandJson[cmd]["ExampleParams"]; var paramOptions = commandJson[cmd]["paramOptions"]; if (exampleParams === null) { // Probably a get-command. A single button is enough. if (addSendButton) { const b = document.createElement("button"); b.setAttribute('onclick', "sendCommand('"+cmd+"', false);"); b.setAttribute('class', "sendToCamera autogenerated"); b.innerHTML = "Get (from camera)"; viewElement.appendChild(b); } } else { // Probably a set-command. Add a form where the parameter values can be inputted. const f = document.createElement("form"); f.setAttribute('target', "_blank"); f.setAttribute('onsubmit', "return false;"); f.setAttribute('class', "autogenerated"); // Add all required parameters to the form. Note that the Foscam API (always?) requires that all parameters are given, // and any missing parameters are reset to some default. This means you cannot set only one parameter value of a // set command. for (const [paramName, exampleValue] of Object.entries(exampleParams)){ var paramInputId = cmd+paramName+idSuffix; // Note: this format (with empty idSuffix) is assumed also elsewhere (when clicking the send button) // label of a parameter const l = document.createElement("label"); l.innerHTML = paramName; l.setAttribute('for', paramInputId); l.setAttribute('class', "autogenerated"); f.appendChild(l); if (paramOptions !== null && (paramName in paramOptions)) { // A dropdown for inputting the parameter value const i = document.createElement("select"); i.setAttribute('id', paramInputId); i.setAttribute('name', paramInputId); i.setAttribute('class', "autogenerated"); if (paramOptions[paramName]['optionsType'] == 'list') { // only a list of options is known paramOptions[paramName]['options'].forEach(function (optionValue, index) { i.add(new Option(optionValue, optionValue)); }); } else { // assume optionsType == 'dict' // a map of names and values is known for (const [optionName, optionValue] of Object.entries(paramOptions[paramName]['options'])){ i.add(new Option(optionName, optionValue)); } } i.value = exampleValue; f.appendChild(i); } else { // A text box for inputting the parameter value const i = document.createElement("input"); i.setAttribute('id', paramInputId); i.setAttribute('name', paramInputId); i.setAttribute('type', "text"); i.setAttribute('placeholder', exampleValue); i.setAttribute('class', "autogenerated"); f.appendChild(i); } f.appendChild(document.createElement("br")); } if (addSendButton) { // the send button const s = document.createElement("input"); s.setAttribute('type', "submit"); s.setAttribute('class', "sendToCamera autogenerated"); s.setAttribute('value', "Send to camera"); s.setAttribute('onclick', "sendCommand('"+cmd+"', true);"); f.appendChild(s); } viewElement.appendChild(f); } } // Setup functions: function setupSaveCamera() { var ip = document.getElementById("cameraIp").value; var port = document.getElementById("cameraPort").value; if (!port) { port = "88"; } var isOlderSdCamera = document.getElementById("cameraIsOldSd").checked; var name = document.getElementById("cameraName").value; if (!name) { name = ""; } var user = document.getElementById("cameraUser").value; var pass = document.getElementById("cameraPass").value; if (!ip || !user) { alert("Set the IP address, username and password to the fields above."); throw "IP or user name was empty."; } var newCamera = new Camera(ip, user, pass, name, port, isOlderSdCamera); globals.setup.cameras[newCamera.keyName] = newCamera; logDebug("Saved new camera. Now cameras:"); logDebug(globals.setup.cameras); if (globals.selectedCamera === null){ globals.selectedCamera = Object.keys(globals.setup.cameras)[0]; } saveGlobals(); main(); } function deleteCamera() { if (globals.selectedCamera != null){ var cameraToDelete = globals.selectedCamera; logDebug("Deleting camera "+ cameraToDelete); delete globals.setup.cameras[cameraToDelete]; globals.selectedCamera = null; logDebug("Deleted camera. Now cameras:"); logDebug(globals.setup.cameras); saveGlobals(); main(); } } // Functions for debugging and logging: function debug() { console.log("Globals:"); console.log(globals); } function logDebug(msg){ if (logDebugLevel){ console.log(msg); } } // Set the selected camera: function selectCamera(){ var dropboxvalue = document.getElementById("selectedCamera").value; globals.selectedCamera = dropboxvalue; // Load the motion detection area&schedule table settings of the selected camera: motionDetectAreaTable.load(globals.setup.cameras[globals.selectedCamera].motionDetectAreaTableState); motionDetectScheduleTable.load(globals.setup.cameras[globals.selectedCamera].motionDetectScheduleTableState); motionDetectLinkageTable.load(globals.setup.cameras[globals.selectedCamera].motionDetectLinkageTableState); saveGlobals(); } // Generic functions for sending HTTP requests: function getUrl(cmd, params={}, useCamera = null, withoutPassword = false) { if (cmd === null) { throw "No camera selected."; } var paramstring = ""; for (const [key, value] of Object.entries(params)){ paramstring += "&"+key+"="+value; } var cameraToUse = useCamera !== null ? useCamera : globals.selectedCamera; var currentCamera = globals.setup.cameras[cameraToUse]; if (!currentCamera && useCamera !== null) { alert("Could not get the requested camera. Has it been deleted?"); throw "No valid camera was selected."; } else if (!currentCamera) { alert("Select a camera from the dropdown list at the top of the page."); throw "No camera was selected."; } if (currentCamera.isOldSdCamera){ var translatedCmd = hdToSdCommandMap[cmd] ?? null; if (translatedCmd == null){ var sdNotImplementedMsg = `Command ${cmd} not implemented yet for SD cameras.`; alert(sdNotImplementedMsg); throw sdNotImplementedMsg; } else { if (withoutPassword) { return "http://"+currentCamera.ip+":"+currentCamera.port+"/"+translatedCmd+".cgi?"+paramstring+"&user="+currentCamera.user+"&pwd=*******"; } else { return "http://"+currentCamera.ip+":"+currentCamera.port+"/"+translatedCmd+".cgi?"+paramstring+"&user="+currentCamera.user+"&pwd="+currentCamera.password; } } } else { if (cmd == "videoMain" || cmd == "videoSub" || cmd == "audio"){ if (withoutPassword) { return "rtsp://"+currentCamera.user+":******@"+currentCamera.ip+":"+currentCamera.port+"/"+cmd; } else { return "rtsp://"+currentCamera.user+":"+currentCamera.password+"@"+currentCamera.ip+":"+currentCamera.port+"/"+cmd; } } else { if (withoutPassword) { return "http://"+currentCamera.ip+":"+currentCamera.port+"/cgi-bin/CGIProxy.fcgi?cmd="+cmd+paramstring+"&usr="+currentCamera.user+"&pwd=******"; } else { return "http://"+currentCamera.ip+":"+currentCamera.port+"/cgi-bin/CGIProxy.fcgi?cmd="+cmd+paramstring+"&usr="+currentCamera.user+"&pwd="+currentCamera.password; } } } } function openUrlToNewWindow(url, forceOnlyShowUrl = false){ if (logDebugLevel || forceOnlyShowUrl || document.getElementById("onlyShowCmdUrlCheckbox").checked) { alert(url); } else { window.open(url, '_blank').focus(); } } function readParameterValuesFromUi(cmd, paramsToSendJson, idSuffix = "") { // Read the params from the text fieds, and add them to paramsToSendJson (inplace). var cmdObject = commandJson[cmd]; var exampleParams = cmdObject["ExampleParams"]; for (const [paramName, exampleValue] of Object.entries(exampleParams)){ var paramInputId = cmd+paramName+idSuffix; // Note: this format is assumed also elsewhere var param = document.getElementById(paramInputId).value; if (!param) { param = exampleValue; // use the example parameter value, if the input text box is empty } paramsToSendJson[paramName] = param; } } function sendCommand(cmd, readParametersFromUi, readParametersFromCustomCommand = null, useCamera = null) { var paramsToSend = {}; if (readParametersFromUi){ readParameterValuesFromUi(cmd, paramsToSend); } else if (readParametersFromCustomCommand !== null) { paramsToSend = globals.customCommands[readParametersFromCustomCommand]["paramsToSend"]; } openUrlToNewWindow(getUrl(cmd, paramsToSend, useCamera), cmd == "restoreToFactorySetting"); // restoreToFactorySetting: Quick and dirty way to disable accidental resetting of the camera. } // Special hand-crafted functions to control the camera: function replaceFactoryUser(){ var ip = document.getElementById("cameraIp").value; var port = document.getElementById("cameraPort").value; if (!port) { port = "88"; } var user = document.getElementById("cameraUser").value; var pass = document.getElementById("cameraPass").value; if (!ip || !user || !pass) { alert("Set the IP address, username and password to the fields above. None of them can be empty."); throw "IP, user name or password was empty."; } var url = "http://"+ip+":"+port+"/cgi-bin/CGIProxy.fcgi?cmd=changeUserNameAndPwdTogether&usrName=admin&oldPwd=&usr=admin&pwd=&newUsrName="+user+"&newPwd="+pass; openUrlToNewWindow(url); } // For custom button to open the link to RTSP stream (must open in some external program). function openRtspStream(rtspType){ var url = getUrl(rtspType); openUrlToNewWindow(url); showSentCmd.innerHTML = "Opened " + getUrl(rtspType, {}, null, true); } // Get a still image from the camera and display it on the given image area. function getStillImage(targetImageAreaId = null, useCamera = null) { if (targetImageAreaId == null) { targetImageAreaId = "operateViewSnapshot"; // default to the Operate view } var imageArea = document.getElementById(targetImageAreaId); var d=new Date(); // append a dummy date to always reload the image from camera imageArea.src = getUrl("snapPicture2", {}, useCamera)+"&aa="+d.getTime(); showSentCmd.innerHTML = "Requested snapPicture2"; } // A hacky way around CORS to send commands to camera (when losing the response does not matter), by setting the dummy image source to the command URL. const dummyImg = document.getElementById("operateViewDummyForPtz"); const showSentCmd = document.getElementById("ptzShowSentCmd"); // text field to show the sent cmd (to give some response when a button is clicked and cmd sent) async function sendCmdViaImg(cmd, params={}) { // Append a dummy date to always resend the command (otherwise browser may cache the 'images' and not resend the request) var d = new Date(); // Get the URL var url = getUrl(cmd, params)+"&aa="+d.getTime(); // "Send": dummyImg.src = url; // Show sent cmd in GUI: showSentCmd.innerHTML = "Sent command "+cmd+""+JSON.stringify(params); } // For controlling PTZ and similar, that must send two or more commands in real time without opening a new tab. async function ptz(cmd, params={}) { if (cmd == "closeInfraLed" || cmd == "openInfraLed") { // First set the IR mode to manual: await sendCmdViaImg("setInfraLedConfig", {"mode":"1"}); await new Promise(r => setTimeout(r, 1000)); // sleep for a second // Then send the open/close IR command: await sendCmdViaImg(cmd); } else if (cmd == "setInfraLedConfig") { // Set IR to automatic await sendCmdViaImg(cmd, params); } else if (cmd == "ptzAddPresetPoint") { var dropdownvalue = document.getElementById("addPresetSelected").value; params = {"name": dropdownvalue}; // Try to first delete the old presetpoint with the same name: await sendCmdViaImg("ptzDeletePresetPoint", params); await new Promise(r => setTimeout(r, 1000)); // Then add: await sendCmdViaImg(cmd, params); } else { // All other PTZ/zoom etc methods: await sendCmdViaImg(cmd, params); if (cmd == "ptzStopRun" || cmd == "zoomStop" || cmd == "focusStop") { // Re-send the request, in case the first was missed due to flaky connection etc await new Promise(r => setTimeout(r, 100)); await sendCmdViaImg(cmd); } } if (cmd == "ptzStopRun" || cmd == "zoomStop" || cmd == "openInfraLed" || cmd == "closeInfraLed" || cmd == "setInfraLedConfig" || cmd == "focusStop" || cmd == "ptzGotoPresetPoint") { // also refresh the still image after a while: await new Promise(r => setTimeout(r, 1000)); getStillImage(); } } // A table of checkboxes for the commands that require some kind of bitmap input (e.g. a schedule or the motion detection area mask). class checkboxTable { constructor(columns, rows, tableClass, elementId, cmdName, rowArgumentName, showHeaders) { var tablearea = document.getElementById(tableClass); var table = document.createElement('table'); table.setAttribute("id", elementId); table.setAttribute('class', tableClass); this.showHeaders = showHeaders; this.width = showHeaders ? columns.length + 1 : columns.length; this.height = showHeaders ? rows.length + 1 : rows.length; for (var i = 0; i < this.height; i++) { // Create a row var tr = document.createElement('tr'); tr.setAttribute('class', tableClass); for (var j = 0; j < this.width; j++) { // Create a column (or cell) into the row i var td = document.createElement('td'); td.setAttribute('class', tableClass); // Add the contents to the cell: if (showHeaders && i == 0 && j > 0) { // header row cell var headerTxt = document.createTextNode(columns[j-1]); td.appendChild(headerTxt); } else if (showHeaders && i > 0 && j == 0) { // header column cell (the left edge of the table) var headerTxt = document.createTextNode(rows[i-1]); td.appendChild(headerTxt); } else if (showHeaders && i > 0 && j > 0) { // checkbox var checkbox = document.createElement('input'); checkbox.setAttribute('class', tableClass); checkbox.type = "checkbox"; checkbox.id = elementId+(i-1)+(j-1); td.appendChild(checkbox); } else if (!showHeaders) { // checkbox var checkbox = document.createElement('input'); checkbox.setAttribute('class', tableClass); checkbox.type = "checkbox"; checkbox.id = elementId+i+j; td.appendChild(checkbox); } // else: showHeaders and i==0 and j==0, which is an empty cell tr.appendChild(td); } table.appendChild(tr); } tablearea.appendChild(table); this.table = table; this.columns = columns; this.rows = rows; this.tableClass = tableClass; this.elementId = elementId; this.cmdName = cmdName; this.rowArgumentName = rowArgumentName; } // Select/Deselect all checkboxes setAll(newState) { for (var i = 0; i < this.rows.length; i++) { for (var j = 0; j < this.columns.length; j++) { document.getElementById(this.elementId+i+j).checked = newState; } } } // For saving the table contents: Returns a flat array of booleans (as a string of 1/0). Use the load() to restore it to the table. getTableState() { var boolString = ""; for (var i = 0; i < this.rows.length; i++) { for (var j = 0; j < this.columns.length; j++) { boolString += document.getElementById(this.elementId+i+j).checked ? '1' : '0'; } } return boolString; } // The same as getTableState but returns an array of strings, where array length is the number of rows in table. // In other words, data from different rows are returned separately. getTableRowsState() { var boolStrings = []; for (var i = 0; i < this.rows.length; i++) { var boolString = ""; for (var j = 0; j < this.columns.length; j++) { boolString += document.getElementById(this.elementId+i+j).checked ? '1' : '0'; } boolStrings.push(boolString); } return boolStrings; } // Load table contents load(boolString) { if (boolString != null) { var i_b = 0; for (var i = 0; i < this.rows.length; i++) { for (var j = 0; j < this.columns.length; j++) { document.getElementById(this.elementId+i+j).checked = Boolean(Number(boolString[i_b])); i_b += 1; } } } } applyToCmdParams() { for (var i = 0; i < this.rows.length; i++) { var rowBitmask = ""; for (var j = this.columns.length-1; j >= 0; j--) { // the bitmask row is essentially a horizontal mirror of the actual table/image var booleanValue = document.getElementById(this.elementId+i+j).checked ? '1' : '0'; rowBitmask += booleanValue; } // Convert the binary to decimal: var decimal = parseInt(rowBitmask, 2); var paramIdBase = this.cmdName+this.rowArgumentName; var paramId = this.rows.length < 2 ? paramIdBase : paramIdBase+i; document.getElementById(paramId).setAttribute('value', decimal); } // Also save the selection to the current Camera: if (this.rowArgumentName == "area") { globals.setup.cameras[globals.selectedCamera].motionDetectAreaTableState = this.getTableState(); saveGlobals(); } else if (this.rowArgumentName == "schedule") { globals.setup.cameras[globals.selectedCamera].motionDetectScheduleTableState = this.getTableState(); saveGlobals(); } else if (this.rowArgumentName == "linkage") { globals.setup.cameras[globals.selectedCamera].motionDetectLinkageTableState = this.getTableState(); saveGlobals(); } } // Load the table data from cameras (assuming that in this case each table row represents one camera) loadRowdataFromCameras() { var collectedDataString = ""; var tc = this.tableClass; this.rows.forEach(function (camKey, index) { // assumes that the table row headers are exactly the key names for Cameras in the globals collectedDataString += globals.setup.cameras[camKey][tc]; // assumes that the property of a Camera is named after the table area id, e.g. 'ViewerSetupOptions' }); this.load(collectedDataString); } // Save the table data to cameras (assuming that in this case each table row represents one camera) saveRowdataToCameras() { var rowDataStrings = this.getTableRowsState(); var tc = this.tableClass; this.rows.forEach(function (camKey, index) { // assumes that the table row headers are exactly the key names for Cameras in the globals globals.setup.cameras[camKey][tc] = rowDataStrings[index]; // assumes that the property of a Camera is named after the table area id, e.g. 'ViewerSetupOptions' }); saveGlobals(); } } // Create the motion detection area selection table: var motionDetectAreaTable = new checkboxTable("0123456789", "0123456789", "checkboxImageTable", "motionDetectionAreaSelectTable", "setMotionDetectConfig", "area", false); // Add the image upload functionality: const image_input = document.querySelector("#image_input"); image_input.addEventListener("change", function() { const reader = new FileReader(); reader.addEventListener("load", () => { const uploaded_image = reader.result; document.querySelector("#motionDetectionAreaSelectTable").style.backgroundImage = `url(${uploaded_image})`; }); reader.readAsDataURL(this.files[0]); }); // Create the motion detection schedule table: var motionDetectScheduleTable = new checkboxTable([ "00:00", "00:30", "01:00", "01:30", "02:00", "02:30", "03:00", "03:30", "04:00", "04:30", "05:00", "05:30", "06:00", "06:30", "07:00", "07:30", "08:00", "08:30", "09:00", "09:30", "10:00", "10:30", "11:00", "11:30", "12:00", "12:30", "13:00", "13:30", "14:00", "14:30", "15:00", "15:30", "16:00", "16:30", "17:00", "17:30", "18:00", "18:30", "19:00", "19:30", "20:00", "20:30", "21:00", "21:30", "22:00", "22:30", "23:00", "23:30" ], ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], "checkboxScheduleTable", "motionDetectionScheduleSelectTable", "setMotionDetectConfig", "schedule", true); var motionDetectLinkageTable = new checkboxTable(["Camera sound", "Send mail", "Snap picture", "Record"], ["action/linkage"], "checkboxLinkageTable", "motionDetectionLinkageSelectTable", "setMotionDetectConfig", "linkage", true); // In the "Custom" view, you can create a button/shortcut to send a command with parameters that are saved to localStorage. // When creating that button, this function displays the parameter fields for the selected cmd. function selectCustomCommand(){ // Get the selected cmd: var selectedCmd = document.getElementById("selectedCustomCommand").value; // Clear and rewrite the field where the arguments for the selected command can be inputted: var customCmdControlCreate = document.getElementById("customCmdControlCreate"); customCmdControlCreate.innerHTML = ""; createCommandControl(customCmdControlCreate, selectedCmd, false, idSuffix = "CustomAddNew"); } // Add a custom command control to the Custom view function addCustomCommand() { // Get the selected cmd: var selectedCmd = document.getElementById("selectedCustomCommand").value; // User-inputted name: var customName = document.getElementById("addCustomCommandName").value; // Fix the target camera? var isFixedCamera = document.getElementById("addCustomCommandFixedCamera").checked; var currentCamera = globals.setup.cameras[globals.selectedCamera]; if (isFixedCamera && !currentCamera) { alert("Select a camera from the dropdown list at the top of the page."); throw "No camera was selected."; } // Construct the JSON that is going to be saved to localStorage (globals): var customCommandEntry = { "cmd" : selectedCmd, "camera" : isFixedCamera ? currentCamera.keyName : null, } var exampleParams = commandJson[selectedCmd]["ExampleParams"]; var paramsToSend = {}; if (exampleParams !== null){ // read the user-inputted parameters: readParameterValuesFromUi(selectedCmd, paramsToSend, "CustomAddNew"); customCommandEntry["paramsToSend"] = paramsToSend; } else { customCommandEntry["paramsToSend"] = null; } globals.customCommands[customName] = customCommandEntry; saveGlobals(); refreshCustomCmdControlsView(); } function refreshCustomCmdControlsView() { // All custom button go here: var customCmdControls = document.getElementById("customCmdControls"); // Clear old stuff: customCmdControls.innerHTML = ""; for (const [key, value] of Object.entries(globals.customCommands)){ var descriptionText = document.createElement('p'); descriptionText.append(key); descriptionText.setAttribute('class', "autogenerated"); customCmdControls.appendChild(descriptionText); const b = document.createElement("button"); var cameraToUse = value.camera !== null ? "'"+value.camera+"'" : null; if (value["paramsToSend"] !== null) { b.setAttribute('onclick', "sendCommand('"+value.cmd+"', false, '"+key+"', "+cameraToUse+");"); } else { b.setAttribute('onclick', "sendCommand('"+value.cmd+"', false, null, "+cameraToUse+");"); } b.setAttribute('class', "sendToCamera autogenerated"); if (value.camera !== null) { b.innerHTML = "Send ("+value["cmd"] +") to "+ value.camera; } else { b.innerHTML = "Send ("+value["cmd"] +") to selected camera"; } // Create also a button for deleting the added custom command: const delb = document.createElement("button"); delb.setAttribute('onclick', "removeCustomCommandButton('"+key+"');"); delb.setAttribute('class', "danger"); delb.setAttribute('style', "margin-left: 10px"); delb.innerHTML = "x"; customCmdControls.appendChild(b); customCmdControls.appendChild(delb); customCmdControls.appendChild(document.createElement("br")); } } function removeCustomCommandButton(customName) { delete globals.customCommands[customName]; saveGlobals(); refreshCustomCmdControlsView(); } // For exporting globals as a json file: function exportLocalStorage(){ var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(globals)); var a = document.getElementById("downloadStart"); a.setAttribute("href", dataStr); a.click(); } // Add the import file upload functionality: const importForm = document.querySelector("#importSettingsFileForm"); importForm.addEventListener('submit', handleSubmit); function handleSubmit (event) { // Stop the form from reloading the page event.preventDefault(); var file = document.querySelector('#selectedImportFile'); // If there's no file, do nothing if (!file.value.length) return; // Create a new FileReader() object var reader = new FileReader(); // Setup the callback event to run when the file is read reader.addEventListener("load", () => { const uploadedFile = reader.result; globals = JSON.parse(uploadedFile); saveGlobals(); main(); // or reload? window.location.reload(); }); // Read the file reader.readAsText(file.files[0]); } // A viewer that shows snapshot images from selected cameras in a loop. Takes care of handling the timer. class Viewer { constructor(interval_ms = 4000, repeatOneCamera = 3, isEnabled = false) { this.globalStep = 0; // Every image refresh increments this. this.enabled = isEnabled; // Enabled means that the Viewer is active and visible. (All other UI is hidden then.) interval_ms = interval_ms < 100 ? 100 : interval_ms; this.interval_ms = interval_ms; // Image update interval. repeatOneCamera = repeatOneCamera < 1 ? 1 : repeatOneCamera; this.repeatOneCamera = repeatOneCamera; // How many consecutive images from one camera until change to next camera. this.timerId = null; // Id of the timer, to be able to cancel the timer. this.waitForImageToLoad = true; // Skip an image refresh (download) if the previous image hasn't fully loaded yet. this.imageLoadReady = true; // A runtime variable to indicate if the image refresh has finished. this.maxPatience = 5; // if waitForImageToLoad == true: How many ticks can be skipped until moving on. This tries to take into account if some camera somehow hangs or is super slow. this.patience = 0; // A runtime variable to indicate how many ticks have been skipped due to a slow connection/camera. this.paused = false; // A runtime variable for the state: paused or running. this.keepButtonsMoving = true; // Reduce screen burn-in by moving the buttons back and forth slowly. this.movingButtonPadding = 0; // A runtime variable for the button movement. this.movingButtonPaddingDelta = 1; // A runtime variable for how many pixels the buttons move at every tick. Start with a positive value. // Parse the list of Cameras that have been enabled for the Viewer: var allCams = Object.keys(globals.setup.cameras); this.activeCams = []; // All cameras (their keyName) that are included in the slideshow loop. for (var i = 0; i < allCams.length; i++) { if (globals.setup.cameras[allCams[i]]["ViewerSetupOptions"] == "1") { this.activeCams.push(allCams[i]); } } this.currentCamIndex = this.activeCams.length > 0 ? 0 : null; // Runtime index of the camera in turn. } // Show and enable the Viewer. enable() { this.timerId = setInterval(this.tick, this.interval_ms); this.enabled = true; globals.viewerIsEnabled = true; saveGlobals(); if (this.currentCamIndex == null) { alert("No cameras selected for Viewer. Go back and select some cameras in Viewer Setup."); } this.tick(); } // When exiting, disable the Viewer. disable() { clearInterval(this.timerId); this.enabled = false; globals.viewerIsEnabled = false; saveGlobals(); } // Advance the index of the currently active camera. nextCamera() { this.currentCamIndex++; if (this.currentCamIndex >= this.activeCams.length) { this.currentCamIndex = 0; } } // Change the currently active camera to the previous. previousCamera() { this.currentCamIndex--; if (this.currentCamIndex < 0) { this.currentCamIndex = this.activeCams.length - 1; } } // When the Viewer is active, this gets called periodically. Load an image from camera. tick(incrementGlobalStep = true) { // Must use the global viewer object here...? if (viewer.currentCamIndex !== null && (!viewer.paused || !incrementGlobalStep) && (!viewer.waitForImageToLoad || viewer.imageLoadReady || viewer.patience >= viewer.maxPatience)) { if (incrementGlobalStep) { viewer.globalStep++; } // Next camera? if (incrementGlobalStep && viewer.globalStep % viewer.repeatOneCamera == 0) { viewer.nextCamera(); } // Start the image download: getStillImage("viewerImage", viewer.activeCams[viewer.currentCamIndex]); viewer.imageLoadReady = false; document.getElementById("ViewerStatus").innerHTML = " loading... "; viewer.patience = 0; if (viewer.keepButtonsMoving) { document.getElementById("toggleViewerBackButton").style.paddingLeft = ""+viewer.getMovingButtonPadding()+"px"; } //logDebug("tick"); } else if (viewer.currentCamIndex !== null && !viewer.paused) { // The previous camera is probably slow... Wait for it, until the patience exceeds the given limit. viewer.patience++; //logDebug("skipped tick"); } } // Get the pixel placement (offset) of the buttons. getMovingButtonPadding() { this.movingButtonPadding += this.movingButtonPaddingDelta; if (this.movingButtonPadding > 100) { this.movingButtonPaddingDelta *= -1; // change direction this.movingButtonPadding = 100; } else if (this.movingButtonPadding < 0) { this.movingButtonPaddingDelta *= -1; // change direction this.movingButtonPadding = 0; } return this.movingButtonPadding; } manualRefreshImage() { this.tick(false); } pause() { document.getElementById("ViewerStatus").innerHTML = " paused "; this.paused = true; } continue() { document.getElementById("ViewerStatus").innerHTML = " continuing... "; this.paused = false; } jumpToPreviousCamera() { this.pause(); this.previousCamera(); this.manualRefreshImage(); } jumpToNextCamera() { this.pause(); this.nextCamera(); this.manualRefreshImage(); } imageLoaded() { if (!this.paused) { document.getElementById("ViewerStatus").innerHTML = ""; } else { document.getElementById("ViewerStatus").innerHTML = " paused "; } this.imageLoadReady = true; } imageLoadError() { document.getElementById("ViewerStatus").innerHTML = " error "; this.imageLoadReady = true; } } // Save all Viewer settings. Create a new Viewer with the new settings. function saveViewerSettings() { viewerSetupCameraTable.saveRowdataToCameras(); globals.viewerConsecutiveImages = parseInt(document.getElementById("viewerConsecutiveImages").value); globals.viewerRefreshInterval_ms = parseInt(document.getElementById("viewerRefreshInterval").value) * 1000; saveGlobals(); viewer = new Viewer(globals.viewerRefreshInterval_ms, globals.viewerConsecutiveImages); } // To suppress the error when loading page (when viewer is not yet defined): function viewer_imageLoadError() { if (viewer) { viewer.imageLoadError(); } } // The command map from Foscam HD camera to SD camera: Try to support SD cameras by translating the command. let hdToSdCommandMap = { "snapPicture2" : "snapshot" // TODO: Add more mappings. Any commands that do not use parameters, are probably easy to add. } let commandJson = { "getImageSetting": { "ExampleParams": null, "paramOptions": null, "Function": "Get color attribute of video", "Privilege": "admin" }, "setBrightness": { "ExampleParams": { "brightness": "50" }, "paramOptions": null, "Function": "Set brightness of video", "Privilege": "admin" }, "setContrast": { "ExampleParams": { "constrast": "50" }, "paramOptions": null, "Function": "Set contrast of video", "Privilege": "admin" }, "setHue": { "ExampleParams": { "hue": "50" }, "paramOptions": null, "Function": "Set hue of video", "Privilege": "admin" }, "setSaturation": { "ExampleParams": { "saturation": "50" }, "paramOptions": null, "Function": "Set saturation of video", "Privilege": "admin" }, "setSharpness": { "ExampleParams": { "sharpness": "50" }, "paramOptions": null, "Function": "Set sharpness of video", "Privilege": "admin" }, "resetImageSetting": { "ExampleParams": null, "paramOptions": null, "Function": "Reset color parameters to default value", "Privilege": "admin" }, "getMirrorAndFlipSetting": { "ExampleParams": null, "paramOptions": null, "Function": "Get mirror and flip attribute of video", "Privilege": "admin" }, "mirrorVideo": { "ExampleParams": { "isMirror": "1" }, "paramOptions": { "isMirror": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Mirror video", "Privilege": "admin" }, "flipVideo": { "ExampleParams": { "isFlip": "1" }, "paramOptions": { "isFlip": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Flip video", "Privilege": "admin" }, "getRatio": { "ExampleParams": null, "paramOptions": null, "Function": "Get value for image distortion correction", "Privilege": "admin" }, "setRatio": { "ExampleParams": { "ratio": "150" }, "paramOptions": null, "Function": "Set value for image distortion correction", "Privilege": "admin" }, "getH264FrmRefMode": { "ExampleParams": null, "paramOptions": null, "Function": "Get frame shipping reference mode of H264 encode stream", "Privilege": "admin" }, "setH264FrmRefMode": { "ExampleParams": { "mode": "0" }, "paramOptions": null, "Function": "Set frame shipping reference mode of H264 encode stream", "Privilege": "admin" }, "getScheduleRecordStreamChn": { "ExampleParams": null, "paramOptions": null, "Function": "Get stream channel for schedule record", "Privilege": "admin" }, "setScheduleRecordStreamChn": { "ExampleParams": { "chn": "0" }, "paramOptions": null, "Function": "Set stream channel for schedule record", "Privilege": "admin" }, "setPwrFreq": { "ExampleParams": { "freq": 2 }, "paramOptions": { "freq": { "optionsType": "dict", "options": { "60 Hz (0)": 0, "50 Hz (1)": 1, "Outdoor mode (2)": 2 } } }, "Function": "Set power freq of sensor", "Privilege": "admin" }, "getVideoStreamParam": { "ExampleParams": null, "paramOptions": null, "Function": "Get video stream param", "Privilege": "admin" }, "setVideoStreamParam": { "ExampleParams": { "streamType": "0", "resolution": "0", "bitRate": "2097152", "frameRate": "30", "GOP": "30", "isVBR": "0" }, "paramOptions": { "streamType": { "optionsType": "list", "options": [ 0, 1, 2, 3 ] }, "resolution": { "optionsType": "dict", "options": { "QHD/1536p (9)": 9, "FullHD/1080p (7)": 7, "HD/720p (0)": 0, "VGA/480p (3)": 3, "VGA 4:3 (1)": 1 } }, "isVBR": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set the video stream param of stream N. Good bitRates to try: 4194304, 2097152, 1048576, 524288, 262144, 131072 (or may have to be between 20480--2097152). Good frameRates: 20, 15, 10, 5. GOP (key frame interval): P frames between I frame, the suggested value is: X * frameRate, e.g. 40, 30, 20, 10.", "Privilege": "admin" }, "getSubVideoStreamParam": { "ExampleParams": null, "paramOptions": null, "Function": "Get sub video stream param", "Privilege": "visitor" }, "setSubVideoStreamParam": { "ExampleParams": { "streamType": "0", "resolution": "0", "bitRate": "2097152", "frameRate": "30", "GOP": "30", "isVBR": "0" }, "paramOptions": { "streamType": { "optionsType": "list", "options": [ 0, 1, 2, 3 ] }, "resolution": { "optionsType": "dict", "options": { "QHD/1536p (9)": 9, "FullHD/1080p (7)": 7, "HD/720p (0)": 0, "VGA/480p (3)": 3, "VGA 4:3 (1)": 1 } }, "isVBR": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set the sub video stream param of stream N", "Privilege": "admin" }, "getMainVideoStreamType": { "ExampleParams": null, "paramOptions": null, "Function": "Get the stream type of main stream. The streamType can be 0,1,2 or 3. It is basically a settings profile where you can save the video encoding settings, and then easily switch between profiles. If you edit streamTypes other than the currently active (0 by default), you must remember to also setMainVideoStreamType to that number to take the new settings into use.", "Privilege": "admin" }, "getSubVideoStreamType": { "ExampleParams": null, "paramOptions": null, "Function": "Get the stream type of sub stream", "Privilege": "admin" }, "setMainVideoStreamType": { "ExampleParams": { "streamType": "0" }, "paramOptions": { "streamType": { "optionsType": "list", "options": [ 0, 1, 2, 3 ] } }, "Function": "Set the stream type of main stream", "Privilege": "admin" }, "setSubStreamFormat": { "ExampleParams": { "format": "0" }, "paramOptions": null, "Function": "Set the stream format of sub stream", "Privilege": "admin" }, "GetMJStream": { "ExampleParams": {}, "paramOptions": null, "Function": "Get motion jpeg stream", "Privilege": "visitor" }, "getOSDSetting": { "ExampleParams": null, "paramOptions": null, "Function": "Get OSD config", "Privilege": "admin" }, "setOSDSetting": { "ExampleParams": { "isEnableTimeStamp": "1", "isEnableDevName": "1", "dispPos": "0", "isEnableOSDMask": "0" }, "paramOptions": { "isEnableTimeStamp": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "isEnableDevName": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "isEnableOSDMask": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set OSD config", "Privilege": "admin" }, "getOsdMaskArea": { "ExampleParams": null, "paramOptions": null, "Function": "Get OSD mask area info", "Privilege": "admin" }, "setOsdMaskArea": { "ExampleParams": { "x1_0": "100", "y1_0": "100", "x2_0": "200", "y2_0": "200", "x1_1": "0", "y1_1": "0", "x2_1": "0", "y2_1": "0", "x1_2": "0", "y1_2": "0", "x2_2": "0", "y2_2": "0", "x1_3": "0", "y1_3": "0", "x2_3": "0", "y2_3": "0" }, "paramOptions": null, "Function": "Set OSD mask area info", "Privilege": "admin" }, "getOSDMask": { "ExampleParams": null, "paramOptions": null, "Function": "Get OSD mask status", "Privilege": "admin" }, "setOSDMask": { "ExampleParams": { "isEnableOSDMask": "1" }, "paramOptions": { "isEnableOSDMask": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set OSD mask status", "Privilege": "admin" }, "getMotionDetectConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get motion detect config", "Privilege": "admin" }, "setMotionDetectConfig": { "ExampleParams": { "isEnable": "1", "linkage": "14", "snapInterval": "2", "sensitivity": "1", "triggerInterval": "5", "isMovAlarmEnable": "1", "isPirAlarmEnable": "1", "area0": "1023", "area1": "1023", "area2": "1023", "area3": "1023", "area4": "1023", "area5": "1023", "area6": "1023", "area7": "1023", "area8": "1023", "area9": "1023", "schedule0": "281474976710655", "schedule1": "281474976710655", "schedule2": "281474976710655", "schedule3": "281474976710655", "schedule4": "281474976710655", "schedule5": "281474976710655", "schedule6": "281474976710655" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "isMovAlarmEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "isPirAlarmEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "sensitivity": { "optionsType": "dict", "options": { "Low (0)": 0, "Medium (1)": 1, "High (2)": 2, "Lower (3)": 3, "Lowest (4)": 4 } } }, "Function": "Set motion detect config. NOTE: The motion detection area and schedule and others must always be included, even if you for example only want to toggle (enable) the 'isEnable'. For the area params '1023' and for schedule params '281474976710655' mean that the motion detection is always enabled for the entire image area. You can use the checkbox tables and their 'Apply' buttons above to input the parameters without manually calculating the bitmaps. 'snapInterval' means the interval time to snap picture again. 'triggerInterval' means the time after which the motion detect alarm can trigger again after a motion detection has happened (+ 5 seconds).", "Privilege": "admin" }, "setLocalAlarmRecordConfig": { "ExampleParams": { "isEnableLocalAlarmRecord": "1", "localAlarmRecordSecs": "30" }, "paramOptions": { "isEnableLocalAlarmRecord": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set local alarm-record config", "Privilege": "admin" }, "getLocalAlarmRecordConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get local alarm-record config", "Privilege": "admin" }, "getSnapConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get snap config", "Privilege": "admin" }, "setSnapConfig": { "ExampleParams": { "snapQuality": "1", "saveLocation": "2" }, "paramOptions": { "snapQuality": { "optionsType": "dict", "options": { "Low quality (0)": 0, "Normal quality (1)": 1, "High quality (2)": 2 } }, "saveLocation": { "optionsType": "dict", "options": { "Save to sd card (0)": 0, "Not in use now (1)": 1, "Upload to FTP (2)": 2 } } }, "Function": "Set snap config", "Privilege": "admin" }, "getScheduleSnapConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get schedule snap config", "Privilege": "admin" }, "setScheduleSnapConfig": { "ExampleParams": { "isEnable": "0", "snapInterval": "2", "schedule0": "1023", "schedule1": "1023", "schedule2": "1023", "schedule3": "1023", "schedule4": "1023", "schedule5": "1023", "schedule6": "1023" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set schedule snap config. To figure out the values for schedule parameters, you can possibly use the (motion detection) schedule table in the 'Detector' view.", "Privilege": "admin" }, "snapPicture": { "ExampleParams": null, "paramOptions": null, "Function": "Manual snap picture", "Privilege": "visitor" }, "snapPicture2": { "ExampleParams": null, "paramOptions": null, "Function": "Manual snap picture. Get a jpg still image from the camera.", "Privilege": "visitor" }, "getRecordList": { "ExampleParams": null, "paramOptions": null, "Function": "Get record list", "Privilege": "admin" }, "getRecordList2": { "ExampleParams": null, "paramOptions": null, "Function": "Get record list", "Privilege": "admin" }, "reloadRecordindex": { "ExampleParams": null, "paramOptions": null, "Function": "Synchronization of record index for Play", "Privilege": "admin" }, "getAlarmRecordConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get alarm record config", "Privilege": "admin" }, "setAlarmRecordConfig": { "ExampleParams": { "isEnablePreRecord": "1", "preRecordSecs": "5", "alarmRecordSecs": "30" }, "paramOptions": { "isEnablePreRecord": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set alarm record config", "Privilege": "admin" }, "getRecordPath": { "ExampleParams": null, "paramOptions": null, "Function": "Get record path for storage", "Privilege": "admin" }, "setRecordPath": { "ExampleParams": { "path": 2 }, "paramOptions": { "path": { "optionsType": "dict", "options": { "SD card (0)": 0, "FTP server (2)": 2, "SD card and cloud (3)": 3 } } }, "Function": "Set record path for storage. How to read the response: 'setResult': 0 success, -1 Sd card is not exist, -2 Share direction is not set, -3 Not enough space, -4 Param error, -5 Param recording.", "Privilege": "admin" }, "getScheduleRecordConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get config for schedule recording", "Privilege": "admin" }, "setScheduleRecordConfig": { "ExampleParams": { "isEnable": 0, "recordLevel": "0", "spaceFullMode": "0", "isEnableAudio": "0", "schedule0": 281474976710655, "schedule1": 281474976710655, "schedule2": 281474976710655, "schedule3": 281474976710655, "schedule4": 281474976710655, "schedule5": 281474976710655, "schedule6": 281474976710655 }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "isEnableAudio": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "recordLevel": { "optionsType": "dict", "options": { "Level for drop frame, 30/30 (0)": 0, "24/30 (1)": 1, "15/30 (2)": 2, "8/30 (3)": 3, "4/30 (4)": 4, "1/30 (5)": 5 } }, "spaceFullMode": { "optionsType": "dict", "options": { "Overwrite the oldest video and continue recording (0)": 0, "Stop recording (1)": 1 } } }, "Function": "Set schedule recordconfig. (This can be disabled if you only want to record a video clip when motion is detected.) The 'schedule' parameters work the same way as with setMotionDetectConfig, so you can possibly use the table under the 'Detector' category to figure out the correct values.", "Privilege": "admin" }, "setIOAlarmConfig": { "ExampleParams": { "isEnable": "1", "linkage": "16", "snapInterval": "2", "alarmLevel": "1", "triggerInterval": "5", "schedule0": "1024", "schedule1": "1024", "schedule2": "1024", "schedule3": "1024", "schedule4": "1024", "schedule5": "1024", "schedule6": "1024" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set IO alarm config", "Privilege": "admin" }, "getIOAlarmConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get IO alarm config", "Privilege": "admin" }, "clearIOAlarmOutput": { "ExampleParams": null, "paramOptions": null, "Function": "Clean IO alarm output", "Privilege": "admin" }, "setAudioAlarmConfig": { "ExampleParams": { "isEnable": "1", "linkage": 14, "snapInterval": "2", "sensitivity": "1", "triggerInterval": "5", "schedule0": 281474976710655, "schedule1": 281474976710655, "schedule2": 281474976710655, "schedule3": 281474976710655, "schedule4": 281474976710655, "schedule5": 281474976710655, "schedule6": 281474976710655 }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "sensitivity": { "optionsType": "dict", "options": { "Low (0)": 0, "Medium (1)": 1, "High (2)": 2, "Lower (3)": 3, "Lowest (4)": 4 } } }, "Function": "Set the sound detection config. For 'sensitivity' probably only 0, 1 and 2 work. 'Linkage' and 'schedule' work the same way as with setMotionDetectConfig above, so you can use the table above to figure out the correct values.", "Privilege": "admin" }, "getAudioAlarmConfig": { "ExampleParams": {}, "paramOptions": null, "Function": "Get Audio alarm config", "Privilege": "admin" }, "setPCAudioAlarmCfg": { "ExampleParams": { "isEnablePCAudioAlarm": "1" }, "paramOptions": { "isEnablePCAudioAlarm": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set audio alarm config for PC\uff08web live video\uff09", "Privilege": "admin" }, "getPCAudioAlarmCfg": { "ExampleParams": null, "paramOptions": null, "Function": "Get audio alarm config for PC\uff08web live video\uff09", "Privilege": "admin" }, "getMultiDevList": { "ExampleParams": null, "paramOptions": null, "Function": "Get multi device list", "Privilege": "admin" }, "getMultiDevDetailInfo": { "ExampleParams": null, "paramOptions": null, "Function": "Get multi device information", "Privilege": "admin" }, "addMultiDev": { "ExampleParams": { "chnnl": "2", "productType": "H264", "ip": "192.168.1.3", "port": "88", "mediaPort": "88", "userName": "admin", "passWord": "", "devName": "FI9805W" }, "paramOptions": null, "Function": "Add multiy device", "Privilege": "admin" }, "delMultiDev": { "ExampleParams": { "chnnl": "2" }, "paramOptions": null, "Function": "Delete multiy device", "Privilege": "admin" }, "setDeFrameLevel": { "ExampleParams": { "level": "0" }, "paramOptions": { "level": { "optionsType": "dict", "options": { "Disable the status of enhance (0)": 0, "Enable the status of enhance (1)": 1 } } }, "Function": "Set status to enhance night vision definition", "Privilege": "admin" }, "getDeFrameLevel": { "ExampleParams": {}, "paramOptions": null, "Function": "Get status of enhance the night vision definition", "Privilege": "admin" }, "addAccount": { "ExampleParams": { "usrName": "test", "usrPwd": "test", "privilege": "0" }, "paramOptions": { "privilege": { "optionsType": "dict", "options": { "Visitor (0)": 0, "Operator (1)": 1, "Administrator (2)": 2 } } }, "Function": "Add user account", "Privilege": "admin" }, "delAccount": { "ExampleParams": { "usrName": "test" }, "paramOptions": null, "Function": "Delete user account", "Privilege": "admin" }, "getPassword": { "ExampleParams": { "usrName": "test" }, "paramOptions": null, "Function": "Get user password", "Privilege": "admin" }, "changePassword": { "ExampleParams": { "usrName": "admin", "oldPwd": "", "newPwd": "test" }, "paramOptions": null, "Function": "Change password", "Privilege": "admin" }, "changeUserName": { "ExampleParams": { "usrName": "admin", "newUsrName": "newname" }, "paramOptions": null, "Function": "Change user name", "Privilege": "admin" }, "changeUserNameAndPwdTogether": { "ExampleParams": { "usrName": "admin", "newUsrName": "newname", "oldPwd": "", "newPwd": "newpwd" }, "paramOptions": null, "Function": "Change user name and password together", "Privilege": "admin" }, "logIn": { "ExampleParams": { "usrName": "admin", "remoteIp": "192.168.1.12", "groupId": "673982479" }, "paramOptions": null, "Function": "User log in to camera", "Privilege": "visitor" }, "logOut": { "ExampleParams": { "usrName": "admin", "ip": "192.168.1.12", "groupId": "673982479" }, "paramOptions": null, "Function": "User log out to camera", "Privilege": "visitor" }, "getSessionList": { "ExampleParams": null, "paramOptions": null, "Function": "Get current session list of the camera", "Privilege": "admin" }, "getUserList": { "ExampleParams": null, "paramOptions": null, "Function": "Get user account list of the camera", "Privilege": "admin" }, "usrBeatHeart": { "ExampleParams": { "usrName": "admin", "ip": "192.168.1.13", "groupId": "673982479" }, "paramOptions": null, "Function": "user checks connection with camera", "Privilege": "visitor" }, "ptzMoveUp": { "ExampleParams": null, "paramOptions": null, "Function": "Move up", "Privilege": "operator" }, "ptzMoveDown": { "ExampleParams": null, "paramOptions": null, "Function": "Move down", "Privilege": "operator" }, "ptzMoveLeft": { "ExampleParams": null, "paramOptions": null, "Function": "Move to left", "Privilege": "operator" }, "ptzMoveRight": { "ExampleParams": null, "paramOptions": null, "Function": "Move to right", "Privilege": "operator" }, "ptzMoveTopLeft": { "ExampleParams": null, "paramOptions": null, "Function": "Move to top left", "Privilege": "operator" }, "ptzMoveTopRight": { "ExampleParams": null, "paramOptions": null, "Function": "Move to top right", "Privilege": "operator" }, "ptzMoveBottomLeft": { "ExampleParams": null, "paramOptions": null, "Function": "Move to bottom left", "Privilege": "operator" }, "ptzMoveBottomRight": { "ExampleParams": null, "paramOptions": null, "Function": "Move to bottom right", "Privilege": "operator" }, "ptzStopRun": { "ExampleParams": null, "paramOptions": null, "Function": "Stop run PT", "Privilege": "operator" }, "ptzReset": { "ExampleParams": null, "paramOptions": null, "Function": "Reset PT to default position", "Privilege": "operator" }, "getPTZSpeed": { "ExampleParams": null, "paramOptions": null, "Function": "Get the speed of PT", "Privilege": "operator" }, "setPTZSpeed": { "ExampleParams": { "speed": "2" }, "paramOptions": { "speed": { "optionsType": "dict", "options": { "Slowest (Opticam: Fastest) (0)": 0, "Slow (Opticam: Fast) (1)": 1, "Normal speed (2)": 2, "Fast (Opticam: Slow) (3)": 3, "Fastest (Opticam: Slowest) (4)": 4 } } }, "Function": "Set the speed of PT", "Privilege": "operator" }, "getPTZPresetPointList": { "ExampleParams": null, "paramOptions": null, "Function": "Get all preset point. The device can support at most 16 preset points. There are four default points: LeftMost, RightMost, TopMost, BottomMost.", "Privilege": "operator" }, "ptzAddPresetPoint": { "ExampleParams": { "name": "1" }, "paramOptions": null, "Function": "Add preset point. The preset point position will be the current PT position. Third-party applications usually use simply numbers (e.g. 1 or 5) as the names. If a preset with the given name already exists, you may have to delete it first (see ptzDeletePresetPoint).", "Privilege": "admin" }, "ptzDeletePresetPoint": { "ExampleParams": { "name": "test" }, "paramOptions": null, "Function": "Delete preset point by name. If the preset is defined as the preset to go to after boot, you may have to change that definition to something else first (see setPTZPrePointForSelfTest).", "Privilege": "admin" }, "ptzGotoPresetPoint": { "ExampleParams": { "name": "test" }, "paramOptions": null, "Function": "Goto preset position", "Privilege": "operator" }, "ptzGetCruiseMapList": { "ExampleParams": null, "paramOptions": null, "Function": "Get all cruise map list", "Privilege": "operator" }, "ptzGetCruiseMapInfo": { "ExampleParams": { "name": "1" }, "paramOptions": null, "Function": "Get the detail info of a cruise map by name", "Privilege": "operator" }, "ptzSetCruiseMap": { "ExampleParams": { "name": "test", "point0": "test0", "point1": "test1", "point2": "test2", "point3": "test3", "point4": "test4", "point5": "test5", "point6": "", "point7": "" }, "paramOptions": null, "Function": "Add a cruise map. Our device can support at most 8 preset point one cruise map. The 'name' is the name of the cruise map. The 'point' parameters are preset names.", "Privilege": "admin" }, "ptzDelCruiseMap": { "ExampleParams": { "name": "test" }, "paramOptions": null, "Function": "Delete a cruise map", "Privilege": "admin" }, "ptzStartCruise": { "ExampleParams": { "mapName": "test" }, "paramOptions": null, "Function": "Start a specificate cruise", "Privilege": "operator" }, "ptzStopCruise": { "ExampleParams": null, "paramOptions": null, "Function": "Start current cruise", "Privilege": "operator" }, "setCruiseTime": { "ExampleParams": { "time": "5" }, "paramOptions": null, "Function": "Set time for continue cruise", "Privilege": "operator" }, "getCruiseTime": { "ExampleParams": {}, "paramOptions": null, "Function": "Get time for continue cruise. NOTE: The API PDF starting from here is full of errors and weird things, so the rest of the cruise commands probably contain some errors.", "Privilege": "operator" }, "setCruiseTimeCustomed": { "ExampleParams": { "time": "5", "customed": "1" }, "paramOptions": { "customed": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set time for continue cruise by costomer", "Privilege": "operator" }, "getCruiseTimeCustomed": { "ExampleParams": null, "paramOptions": null, "Function": "Get time for customed continue cruise.", "Privilege": "operator" }, "setCruiseLoopCnt": { "ExampleParams": { "count": "5" }, "paramOptions": null, "Function": "Set counts for continue cruise", "Privilege": "operator" }, "getCruiseLoopCnt": { "ExampleParams": null, "paramOptions": null, "Function": "Set counts for continue cruise", "Privilege": "operator" }, "setCruiseCtrlMode": { "ExampleParams": { "mode": "0" }, "paramOptions": { "mode": { "optionsType": "dict", "options": { "By time (0)": 0, "By loop count (1)": 1 } } }, "Function": "Set control mode for continue cruise, by time or count", "Privilege": "operator" }, "getCruiseCtrlMode": { "ExampleParams": null, "paramOptions": null, "Function": "Get control mode for continue cruise, by time or count", "Privilege": "operator" }, "setCruisePrePointLingerTime": { "ExampleParams": { "name": "123", "time0": "0", "time1": "1", "time2": "2", "time3": "3", "time4": "4", "time5": "5", "time6": "6", "time7": "7" }, "paramOptions": null, "Function": "Set linger time for cruise,when pt arrive prepoint", "Privilege": "operator" }, "getCruisePrePointLingerTime": { "ExampleParams": { "name": "test" }, "paramOptions": null, "Function": "Set linger time for cruise,when pt arrive prepoint", "Privilege": "operator" }, "zoomIn": { "ExampleParams": null, "paramOptions": null, "Function": "Zoom in", "Privilege": "operator" }, "zoomOut": { "ExampleParams": null, "paramOptions": null, "Function": "Zoom out", "Privilege": "operator" }, "zoomStop": { "ExampleParams": null, "paramOptions": null, "Function": "Stop zoom run", "Privilege": "operator" }, "getZoomSpeed": { "ExampleParams": null, "paramOptions": null, "Function": "Get the speed of Zoom", "Privilege": "operator" }, "setZoomSpeed": { "ExampleParams": { "speed": "1" }, "paramOptions": { "speed": { "optionsType": "dict", "options": { "Slow (or Fast?) (0)": 0, "Normal (1)": 1, "Fast (or Slow?) (2)": 2 } } }, "Function": "Set the speed of PTZ", "Privilege": "operator" }, "setPTZSelfTestMode": { "ExampleParams": { "mode": "2" }, "paramOptions": { "mode": { "optionsType": "dict", "options": { "No selftest (0)": 0, "Normal selftest (1)": 1, "After selftest, goto presetpoint set with setPTZPrePointForSelfTest (2)": 2 } } }, "Function": "Set the selftest mode of PTZ", "Privilege": "operator" }, "getPTZSelfTestMode": { "ExampleParams": null, "paramOptions": null, "Function": "Get the selftest mode of PTZ", "Privilege": "operator" }, "setPTZPrePointForSelfTest": { "ExampleParams": { "name": "1" }, "paramOptions": null, "Function": "Set presetpoint for selftest of PTZ. This setting defines the position of PTZ after the camera boots. Set the preset name with this, and enable mode 2 with 'setPTZSelfTestMode'.", "Privilege": "operator" }, "getPTZPrePointForSelfTest": { "ExampleParams": null, "paramOptions": null, "Function": "Get the presetpoint for selftest of PTZ", "Privilege": "operator" }, "set485Info": { "ExampleParams": { "rs485Protocol": "0", "rs485Addr": "1", "rs485Baud": "1200", "rs485DataBit": "7", "rs485StopBit": "1", "rs485Check": "0" }, "paramOptions": null, "Function": "Set informations of 485", "Privilege": "operator" }, "get485Info": { "ExampleParams": {}, "paramOptions": null, "Function": "Get informations of 485", "Privilege": "operator" }, "getIPInfo": { "ExampleParams": null, "paramOptions": null, "Function": "Get IP Info", "Privilege": "admin" }, "setIpInfo": { "ExampleParams": { "isDHCP": 1, "ip": "192.168.1.88", "gate": "192.168.1.1", "mask": "255.255.255.0", "dns1": "8.8.8.8", "dns2": "192.168.1.1" }, "paramOptions": { "isDHCP": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set IP Info", "Privilege": "admin" }, "refreshWifiList": { "ExampleParams": null, "paramOptions": null, "Function": "Start scan the aps around", "Privilege": "admin" }, "getWifiList": { "ExampleParams": { "startNo": "0" }, "paramOptions": null, "Function": "Get the aps around after refreshWifiList", "Privilege": "admin" }, "setWifiSetting": { "ExampleParams": { "isEnable": "1", "isUseWifi": "1", "ssid": "wifi_SSID", "netType": "0", "encryptType": 3, "psk": "wifi_password", "authMode": 2, "keyFormat": "0", "defaultKey": "1", "key1": "", "key2": "", "key3": "", "key4": "", "key1Len": "64", "key2Len": "64", "key3Len": "64", "key4Len": "64" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "isUseWifi": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "encryptType": { "optionsType": "dict", "options": { "Open mode (0)": 0, "WEP (1)": 1, "WPA (2)": 2, "WPA2 (3)": 3, "WPA/WPA2 (4)": 4 } } }, "Function": "Set wifi config. Give 'ssid' and 'psk'. Others you can usually ignore and leave as defaults.", "Privilege": "admin" }, "getWifiConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get wifi config", "Privilege": "admin" }, "getPortInfo": { "ExampleParams": null, "paramOptions": null, "Function": "Get ports of camera", "Privilege": "admin" }, "setPortInfo": { "ExampleParams": { "webPort": "88", "mediaPort": "88", "httpsPort": "443", "onvifPort": "888" }, "paramOptions": null, "Function": "Set ports of camera", "Privilege": "admin" }, "getUPnPConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get UpnP config", "Privilege": "admin" }, "setUPnPConfig": { "ExampleParams": { "isEnable": "1" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set UpnP config", "Privilege": "admin" }, "getDDNSConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get DDNS config", "Privilege": "admin" }, "setDDNSConfig": { "ExampleParams": { "isEnable": 0, "hostName": "test.dyndns.org", "ddnsServer": "4", "user": "test", "password": "test" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "ddnsServer": { "optionsType": "dict", "options": { "Factory DDNS (0)": 0, "Oray (1)": 1, "3322 (2)": 2, "no-ip (3)": 3, "dyndns (4)": 4 } } }, "Function": "Set DDNS config", "Privilege": "admin" }, "setFtpConfig": { "ExampleParams": { "ftpAddr": "ftp://192.168.1.2/sbuDir", "ftpPort": "21", "mode": "0", "userName": "test", "password": "test" }, "paramOptions": { "mode": { "optionsType": "dict", "options": { "PASV mode (0)": 0, "PORT mode (1)": 1 } } }, "Function": "Set FTP config", "Privilege": "admin" }, "getFtpConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get FTP config", "Privilege": "admin" }, "testFtpServer": { "ExampleParams": { "ftpAddr": "ftp://192.168.1.2/sbuDir", "ftpPort": "21", "mode": "0", "fptuserName": "test", "ftppassword": "test" }, "paramOptions": { "mode": { "optionsType": "dict", "options": { "PASV mode (0)": 0, "PORT mode (1)": 1 } } }, "Function": "Test FTP server", "Privilege": "admin" }, "getSMTPConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get mail config", "Privilege": "admin" }, "setSMTPConfig": { "ExampleParams": { "isEnable": "1", "server": "smtp.gmail.com", "port": "465", "isNeedAuth": "1", "user": "youraccount@gmail.com", "password": "your_gmail_account_app_password", "sender": "youraccount@gmail.com", "reciever": "youraccount@gmail.com,anotherreceiver@gmail.com", "tls": "1" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "isNeedAuth": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "tls": { "optionsType": "dict", "options": { "none (0)": 0, "TLS (use this for Gmail) (1)": 1, "STARTTLS (2)": 2 } } }, "Function": "Set mail config. Defaults work with Gmail. You have to enable 2-step verification for your Gmail account and then create an app password in Google account settings. Gmail probably also supports STARTTLS with port 587 or 25. For Hotmail use STARTTLS with port 587 or 25. Maximum length of password may be 16.", "Privilege": "admin" }, "smtpTest": { "ExampleParams": { "smtpServer": "smtp.gmail.com", "port": "465", "isNeedAuth": "1", "user": "youraccount@gmail.com", "password": "your_gmail_account_app_password", "sender": "youraccount@gmail.com" }, "paramOptions": { "isNeedAuth": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Test mail setting", "Privilege": "admin" }, "setP2PEnable": { "ExampleParams": { "enable": 0 }, "paramOptions": { "enable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set p2p status", "Privilege": "admin" }, "getP2PEnable": { "ExampleParams": null, "paramOptions": null, "Function": "Get p2p status", "Privilege": "admin" }, "setP2PPort": { "ExampleParams": { "port": "12345" }, "paramOptions": null, "Function": "Set p2p port", "Privilege": "admin" }, "getP2PPort": { "ExampleParams": null, "paramOptions": null, "Function": "Get p2p port", "Privilege": "admin" }, "getP2PInfo": { "ExampleParams": null, "paramOptions": null, "Function": "Get p2p UID", "Privilege": "admin" }, "getPPPoEConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get PPPoE config", "Privilege": "admin" }, "setPPPoEConfig": { "ExampleParams": { "isEnable": "1", "userName": "usr", "password": "pwd" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set PPPoE config", "Privilege": "admin" }, "setSystemTime": { "ExampleParams": { "timeSource": "0", "ntpServer": "Auto", "dateFormat": "0", "timeFormat": "1", "timeZone": "0", "isDst": "0", "dst": "0", "year": "2022", "mon": "1", "day": "6", "hour": "9", "minute": "35", "sec": "0" }, "paramOptions": { "timeSource": { "optionsType": "dict", "options": { "NTP (0)": 0, "set time manually below (1)": 1 } }, "timeFormat": { "optionsType": "dict", "options": { "12 h (0)": 0, "24 h (1)": 1 } }, "dateFormat": { "optionsType": "dict", "options": { "YYYY-MM-DD (0)": 0, "DD/MM/YYYY (1)": 1, "MM/DD/YYYY (2)": 2 } }, "isDst": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set system time. timeZone is the seconds between local time and GMT time. For example: timeZone=-7200 presents GMT+2 time (e.g. Finland), timeZone=3600 presents GMT-01:00, and timeZone=-3600 presents GMT+01:00", "Privilege": "admin" }, "getSystemTime": { "ExampleParams": null, "paramOptions": null, "Function": "Get system time", "Privilege": "admin" }, "openInfraLed": { "ExampleParams": null, "paramOptions": null, "Function": "Force open infra led. The 'setInfraLedConfig' mode must be 1 (manual) for this to work.", "Privilege": "admin" }, "closeInfraLed": { "ExampleParams": null, "paramOptions": null, "Function": "Force close infra led. The 'setInfraLedConfig' mode must be 1 (manual) for this to work.", "Privilege": "admin" }, "getInfraLedConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get infra led config", "Privilege": "admin" }, "setInfraLedConfig": { "ExampleParams": { "mode": 0 }, "paramOptions": { "mode": { "optionsType": "dict", "options": { "Auto mode (0)": 0, "Manual mode (1)": 1 } } }, "Function": "Set infra led config", "Privilege": "admin" }, "getScheduleInfraLedConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get config for infra led switch schedule", "Privilege": "admin" }, "setScheduleInfraLedConfig": { "ExampleParams": { "mode": "" }, "paramOptions": null, "Function": "Set config for infra led switch schedule", "Privilege": "admin" }, "getDevState": { "ExampleParams": null, "paramOptions": null, "Function": "Get all device state", "Privilege": "admin" }, "getDevName": { "ExampleParams": null, "paramOptions": null, "Function": "Get camera name", "Privilege": "admin" }, "setDevName": { "ExampleParams": { "devName": "test" }, "paramOptions": null, "Function": "Set camera name", "Privilege": "admin" }, "getDevInfo": { "ExampleParams": null, "paramOptions": null, "Function": "Get camera information", "Privilege": "admin" }, "getProductModel": { "ExampleParams": null, "paramOptions": null, "Function": "Get camera model number", "Privilege": "visitor" }, "getProductModelName": { "ExampleParams": null, "paramOptions": null, "Function": "Get camera model name", "Privilege": "visitor" }, "getProductLanguage": { "ExampleParams": null, "paramOptions": null, "Function": "Get camera main language", "Privilege": "visitor" }, "getProductSensorType": { "ExampleParams": null, "paramOptions": null, "Function": "Get camera sensor type number", "Privilege": "visitor" }, "getProductWifiType": { "ExampleParams": null, "paramOptions": null, "Function": "Get camera wifi type number", "Privilege": "visitor" }, "getProductSdFlag": { "ExampleParams": null, "paramOptions": null, "Function": "Whether camera support sd card", "Privilege": "visitor" }, "getProductOutdoorFlag": { "ExampleParams": null, "paramOptions": null, "Function": "Whether camera is outdoor machine", "Privilege": "visitor" }, "getProductPtFlag": { "ExampleParams": null, "paramOptions": null, "Function": "Whether camera is pt machine", "Privilege": "visitor" }, "getProductZoomFlag": { "ExampleParams": null, "paramOptions": null, "Function": "Whether camera is zoom machine", "Privilege": "visitor" }, "getProductRs485Flag": { "ExampleParams": null, "paramOptions": null, "Function": "Whether camera support rs485", "Privilege": "visitor" }, "getProductIoAlarmFlag": { "ExampleParams": null, "paramOptions": null, "Function": "Whether camera support IO alarm", "Privilege": "visitor" }, "getProductOnvifFlag": { "ExampleParams": null, "paramOptions": null, "Function": "Whether camera support Onvif", "Privilege": "visitor" }, "getProductP2pFlag": { "ExampleParams": null, "paramOptions": null, "Function": "Whether camera support P2p", "Privilege": "visitor" }, "getProductWpsFlag": { "ExampleParams": null, "paramOptions": null, "Function": "Whether camera support Wps", "Privilege": "visitor" }, "getProductAudioFlag": { "ExampleParams": null, "paramOptions": null, "Function": "Whether camera support audio-speak", "Privilege": "visitor" }, "getProductTalkFlag": { "ExampleParams": null, "paramOptions": null, "Function": "Whether camera support audio-talk", "Privilege": "visitor" }, "getProductAppVer": { "ExampleParams": null, "paramOptions": null, "Function": "Get camera application version", "Privilege": "visitor" }, "getProductAllInfo": { "ExampleParams": null, "paramOptions": null, "Function": "Get camera Information", "Privilege": "visitor" }, "getGeneratePubKey": { "ExampleParams": null, "paramOptions": null, "Function": "Get public key generated by camera for software reset", "Privilege": "visitor" }, "toolRestoreToFactory": { "ExampleParams": { "codeLen": "10", "code": "xxxxxxxxxx" }, "paramOptions": null, "Function": "Set camera reset to factory by software", "Privilege": "visitor" }, "rebootSystem": { "ExampleParams": null, "paramOptions": null, "Function": "Reboot camera", "Privilege": "admin" }, "restoreToFactorySetting": { "ExampleParams": null, "paramOptions": null, "Function": "Restore to factory setting. WARNING! This will erase all settings in camera and you may lose connection to the camera, especially wireless. Also remember to close the new opened tab so that you don't later accidentally reload that tab, performing the erase again. (For now clicking the button only shows the URL - copy and paste it manually to the address bar and send.)", "Privilege": "admin" }, "exportConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Export config file. After calling this command, you can get the config file by visiting the following address: /configs/export/configs.bin", "Privilege": "admin" }, "ImportConfig": { "ExampleParams": {}, "paramOptions": null, "Function": "Import config file. See the Foscam API PDF documentation how this actually works. This button probably does not work.", "Privilege": "admin" }, "FwUpgrade": { "ExampleParams": {}, "paramOptions": null, "Function": "Upgrade firmware. See the Foscam API PDF documentation how this actually works. This button probably does not work.", "Privilege": "admin" }, "removePatch": { "ExampleParams": {}, "paramOptions": null, "Function": "remove firewall patch", "Privilege": "admin" }, "getFirewallConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get firewall config", "Privilege": "admin" }, "setFirewallConfig": { "ExampleParams": { "isEnable": "1", "rule": "0", "ipList0": "0", "ipList1": "0", "ipList2": "0", "ipList3": "0", "ipList4": "0", "ipList5": "0", "ipList6": "0", "ipList7": "0" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set firewall config", "Privilege": "admin" }, "getLog": { "ExampleParams": { "offset": "0", "count": "20" }, "paramOptions": null, "Function": "Get system log", "Privilege": "admin" }, "getAudioVolume": { "ExampleParams": null, "paramOptions": null, "Function": "Get Audio Volume", "Privilege": "admin" }, "setAudioVolume": { "ExampleParams": { "volume": "100" }, "paramOptions": null, "Function": "Set Audio Volume", "Privilege": "admin" }, "getWifiMode": { "ExampleParams": null, "paramOptions": null, "Function": "Get Wifi Mode", "Privilege": "admin" }, "getTemperatureAlarmConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get Temperature Alarm Config", "Privilege": "admin" }, "setTemperatureAlarmConfig": { "ExampleParams": { "isEnable": "1", "linkage": "129", "topLimit": "40", "triggerInterval": "5", "schedule0": "1023", "schedule1": "1023", "schedule2": "1023", "schedule3": "1023", "schedule4": "1023", "schedule5": "1023", "schedule6": "1023" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set Temperature Alarm Config", "Privilege": "admin" }, "getTemperatureState": { "ExampleParams": {}, "paramOptions": null, "Function": "Get Temperature Degree", "Privilege": "admin" }, "setMusicDefaultListRefresh": { "ExampleParams": null, "paramOptions": null, "Function": "Refresh default list", "Privilege": "admin" }, "getMusicListsName": { "ExampleParams": {}, "paramOptions": null, "Function": "Get Music lists name", "Privilege": "admin" }, "getMusicsNameOfList": { "ExampleParams": { "name": "default", "startNo": "0", "musicNum": "50" }, "paramOptions": null, "Function": "Get Musics name of list", "Privilege": "admin" }, "addMusicList": { "ExampleParams": { "name": "1", "music0": "1.wav", "music1": "", "music2": "", "music3": "", "music4": "", "music5": "" }, "paramOptions": null, "Function": "Add Musics list and music", "Privilege": "admin" }, "delMusicList": { "ExampleParams": { "name": "1" }, "paramOptions": null, "Function": "Delete Musics list and music", "Privilege": "admin" }, "setMusicPlayMode": { "ExampleParams": { "mode": "1" }, "paramOptions": null, "Function": "Set Musics Play Mode", "Privilege": "admin" }, "getMusicPlayMode": { "ExampleParams": null, "paramOptions": null, "Function": "Get Musics Play Mode", "Privilege": "admin" }, "setMusicPlayNext": { "ExampleParams": null, "paramOptions": null, "Function": "Play Next Music", "Privilege": "admin" }, "setMusicPlayPre": { "ExampleParams": null, "paramOptions": null, "Function": "Play Precede Music", "Privilege": "admin" }, "getMusicPlayState": { "ExampleParams": null, "paramOptions": null, "Function": "Get Music Play State", "Privilege": "admin" }, "setMusicPlayStart": { "ExampleParams": { "mode": "1", "index": "0", "name": "default" }, "paramOptions": null, "Function": "Start Play Music", "Privilege": "admin" }, "setMusicPlayStop": { "ExampleParams": null, "paramOptions": null, "Function": "Stop Play Music", "Privilege": "admin" }, "setMusicDormantTime": { "ExampleParams": { "minutes": "10" }, "paramOptions": null, "Function": "Set Music Dormant Time", "Privilege": "admin" }, "getMusicDormantTime": { "ExampleParams": {}, "paramOptions": null, "Function": "Get Music Dormant Time", "Privilege": "admin" }, "getCloudConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get Cloud Config", "Privilege": "admin" }, "setCloudConfig": { "ExampleParams": { "isEnable": 0, "cloudServer": 1, "code": "Authorization code from server" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "cloudServer": { "optionsType": "dict", "options": { "Dropbox (1)": 1, "Baidu (2)": 2 } } }, "Function": "Set Cloud Config", "Privilege": "admin" }, "selectCloudServer": { "ExampleParams": { "isEnable": 0, "cloudServer": "2" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "cloudServer": { "optionsType": "dict", "options": { "Dropbox (1)": 1, "Baidu (2)": 2 } } }, "Function": "Select Cloud Server", "Privilege": "admin" }, "getCloudToken": { "ExampleParams": { "isEnable": "1", "cloudServer": "2", "code": "dfasdfdfadf" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "cloudServer": { "optionsType": "dict", "options": { "Dropbox (1)": 1, "Baidu (2)": 2 } } }, "Function": "Get Cloud Token. Call this cgi, then call getCloudConfig 10s later, find accessToken", "Privilege": "admin" }, "getCloudQuota": { "ExampleParams": { "isEnable": "1", "cloudServer": "2", "code": "dfasdfdfadf" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "cloudServer": { "optionsType": "dict", "options": { "Dropbox (1)": 1, "Baidu (2)": 2 } } }, "Function": "Get Cloud Quota. Call this cgi, then call getCloudConfig 10s later, find accessToken", "Privilege": "admin" }, "testCloudServer": { "ExampleParams": { "isEnable": "1", "cloudServer": "2", "code": "dfasdfdfadf" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } }, "cloudServer": { "optionsType": "dict", "options": { "Dropbox (1)": 1, "Baidu (2)": 2 } } }, "Function": "Get Cloud Quota. Call this cgi, then call getCloudConfig 10s later, find accessToken", "Privilege": "admin" }, "getPushConfig": { "ExampleParams": null, "paramOptions": null, "Function": "Get Push Config", "Privilege": "admin" }, "setPushConfig": { "ExampleParams": { "isEnable": "1", "pushServer": "1" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Set Push Config", "Privilege": "admin" }, "testPushServer": { "ExampleParams": { "isEnable": "1", "pushServer": "1", "usr": "admin" }, "paramOptions": { "isEnable": { "optionsType": "dict", "options": { "false/disabled (0)": 0, "true/enabled (1)": 1 } } }, "Function": "Test Push Server", "Privilege": "admin" }, "pushOperate": { "ExampleParams": null, "paramOptions": null, "Function": "Test Push pushOperate", "Privilege": "admin" }, "SetOnlineUpgrade": { "ExampleParams": { "update_type": "1", "url": "http://bcs.duapp.com/foscam/FosBaby_B_app_ver2.x.1.11.bin", "cycle": "0" }, "paramOptions": null, "Function": "Online upgrade", "Privilege": "admin" }, "setCloudStreamLevel": { "ExampleParams": { "level": "30" }, "paramOptions": null, "Function": "Set cloud stream level", "Privilege": "admin" }, "getCloudStreamLevel": { "ExampleParams": null, "paramOptions": null, "Function": "Get cloud stream level", "Privilege": "admin" }, "setSubVideoStreamType": { "ExampleParams": { "streamType": "0" }, "Function": "Set the stream type of sub stream", "Privilege": "admin", "paramOptions": { "streamType": { "optionsType": "list", "options": [ 0, 1, 2, 3 ] } } }, "importConfig": { "ExampleParams": {}, "paramOptions": null, "Function": "Import config file. See the Foscam API PDF documentation how this actually works. This button probably does not work.", "Privilege": "admin" }, "fwUpgrade": { "ExampleParams": {}, "paramOptions": null, "Function": "Upgrade firmware. See the Foscam API PDF documentation how this actually works. This button probably does not work.", "Privilege": "admin" }, "focusNear": { "ExampleParams": null, "Function": "Focus near.", "Privilege": "operator", "paramOptions": null }, "focusFar": { "ExampleParams": null, "Function": "Focus far.", "Privilege": "operator", "paramOptions": null }, "focusStop": { "ExampleParams": null, "Function": "Stop the focusing motor.", "Privilege": "operator", "paramOptions": null } } </script> </body> </html>