diff --git a/.gitignore b/.gitignore
index e43b0f9..dd5c568 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
.DS_Store
+/.vscode
diff --git a/BMD-Camera-Control.js b/BMD-Camera-Control.js
deleted file mode 100644
index d612fef..0000000
--- a/BMD-Camera-Control.js
+++ /dev/null
@@ -1,483 +0,0 @@
-/* Blackmagic Camera Control JS Class
- Written based on the Camera Control
- API Documentation from Blackmagic's
- Developer info website.
-
- (c) Dylan Speiser 2024
- github.com/DylanSpeiser
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published.
-
- This program is distributed in the hope that it will be useful but without
- any warranty; without even the implied warranty of merchantability or fitness
- for a particular purpose as specified by the License, which you should consult
- for more details at LICENSE.txt in this repository.
-*/
-
-class BMDCamera {
- // Pretty name and network hostname (strings)
- name;
- hostname;
- APIAddress;
-
- // Codec and Video Format (JSON object)
- format;
-
- // Current Transport Mode (string)
- transportMode;
-
- // Current Playback state (JSON object)
- playbackState;
-
- // Current Record state (JSON object)
- recordState;
-
- // Timecode (JSON Object)
- timecode;
-
- // Presets (JSON object)
- presets;
- activePreset;
-
- // Iris (floats)
- apertureStop;
- apertureNormalised;
-
- // Zoom (floats)
- zoomMM;
- zoomNormalised;
-
- // Focus (float)
- focusNormalised;
-
- // ISO (int)
- ISO;
-
- // Gain (int)
- gain;
-
- // White Balance (ints)
- WhiteBalance;
- WhiteBalanceTint;
-
- // ND Filter (int, string)
- NDStop;
- NDMode;
-
- // Shutter (JSON object)
- shutter;
- // has to be an object because it either returns with shutterSpeed or shutterAngle
-
- // AE Mode (JSON Object)
- AutoExposureMode;
-
- // Basic Color Correction (JSON objects w/ RGBL)
- CClift;
- CCgamma;
- CCgain;
- CCoffset;
-
- // Other Color Correction (JSON objects w/ 2 numbers)
- CCcontrast;
- CCcolor;
- CClumacontribution;
-
- // Keep track of unimplemented functions on the camera (array of endpoint strings)
- UnimplementedFunctionality = [];
-
- // UI refreshing functions, will get called after every get method to keep the UI updated,
- // For BYOUI purposes (Bring-Your-Own-UI). If you're using this class for your own UI,
- // set this function to point to your UI updater.
- static updateUIAll() {};
- static updateUIname() {};
- static updateUIhostname() {};
- static updateUIFormat() {};
- static updateUITransportMode() {};
- static updateUIPlaybackState() {};
- static updateUIRecordState() {};
- static updateUITimecode() {};
- static updateUIPresets() {};
- static updateUIActivePreset() {};
- static updateUIAperture() {};
- static updateUIZoom() {};
- static updateUIFocus() {};
- static updateUIISO() {};
- static updateUIgain() {};
- static updateUIWhiteBalance() {};
- static updateUINDStop() {};
- static updateUIshutter() {};
- static updateUIAutoExposureMode() {};
- static updateUIColorCorrection() {};
- static updateUILinks() {};
-
- // ============= CONSTRUCTOR ================
- constructor(hostname) {
- this.hostname = hostname;
- this.APIAddress = "http://"+hostname+"/control/api/v1";
- this.name = this.hostname.replace(".local","").replaceAll("-"," ");
- }
-
- // Wrapper for API call, returns the JSON object from the camera
- async pullData(endpoint) {
- // Ask the camera a question
- let response;
-
- // Only send the request if it's not an Unimplemented Function
- if (this.UnimplementedFunctionality.indexOf(endpoint) < 0) {
- response = await sendRequest("GET",this.APIAddress+endpoint,"");
- } else {
- response = "Unimplemented";
- }
-
- // Check for unimplemented features
- if (response.status == 501) {
- this.UnimplementedFunctionality.push(endpoint);
- response = "Unimplemented";
- }
-
- return response
- }
-
- // Wrapper for API call, sends data to the camera.
- async pushData(endpoint, data) {
- return await sendRequest("PUT",this.APIAddress+endpoint,data);
- }
-
- // =============== GETTERS ==================
-
- // name, hostname, APIaddress handled by constructor
- // since these are all asynchronous, they will return promises to use with await or .then()
-
- async getFormat() {
- this.format = await this.pullData("/system/format");
- BMDCamera.updateUIFormat();
- return this.format;
- }
-
- async getTransportMode() {
- this.transportMode = await this.pullData("/transports/0");
- BMDCamera.updateUITransportMode();
- return this.transportMode;
- }
-
- async getPlaybackState() {
- this.playbackState = await this.pullData("/transports/0/playback");
- BMDCamera.updateUIPlaybackState();
- return this.playbackState;
- }
-
- async getRecordState() {
- this.recordState = await this.pullData("/transports/0/record");
- BMDCamera.updateUIRecordState();
- return this.recordState;
- }
-
- async getTimecode() {
- this.timecode = await this.pullData("/transports/0/timecode");
- BMDCamera.updateUITimecode();
- return this.timecode;
- }
-
- async getPresets() {
- this.pullData("/presets").then((value) => {this.presets = value.presets; BMDCamera.updateUIPresets();});
- return this.presets;
- }
-
- async getActivePreset() {
- this.activePreset = await this.pullData("/presets/active");
- BMDCamera.updateUIActivePreset();
- return this.activePreset;
- }
-
- async getAperture() {
- this.pullData("/lens/iris").then((value) => {this.apertureStop = value.apertureStop; this.apertureNormalised = value.normalised; BMDCamera.updateUIAperture();});
- return this.apertureNormalised;
- }
-
- async getZoom() {
- this.pullData("/lens/zoom").then((value) => {this.zoomMM = value.focalLength; this.zoomNormalised = value.normalised; BMDCamera.updateUIZoom();});
- return this.zoomNormalised;
- }
-
- async getFocus() {
- this.pullData("/lens/focus").then((value) => {this.focusNormalised = value.normalised; BMDCamera.updateUIFocus();});
- return this.focusNormalised;
- }
-
- async getISO() {
- this.pullData("/video/iso").then((value) => {this.ISO = value.iso; BMDCamera.updateUIISO();});
- return this.ISO;
- }
-
- async getGain() {
- this.pullData("/video/gain").then((value) => {this.gain = value.gain; BMDCamera.updateUIgain();});
- return this.gain;
- }
-
- async getWhiteBalance() {
- this.pullData("/video/whiteBalance").then((value) => {this.WhiteBalance = value.whiteBalance});
- this.pullData("/video/whiteBalanceTint").then((value) => {this.WhiteBalanceTint = value.whiteBalanceTint; BMDCamera.updateUIWhiteBalance();});
- return this.WhiteBalance;
- }
-
- async getND() {
- this.pullData("/video/ndFilter").then((value) => {this.NDStop = value.stop});
- this.pullData("/video/ndFilter/displayMode").then((value) => {this.NDMode = value.displayMode; BMDCamera.updateUINDStop();});
- return this.NDStop;
- }
-
- async getShutter() {
- this.shutter = await this.pullData("/video/shutter");
- BMDCamera.updateUIshutter();
- return this.shutter;
- }
-
- async getAutoExposureMode() {
- this.AutoExposureMode = await this.pullData("/video/autoExposure");
- BMDCamera.updateUIAutoExposureMode();
- return this.AutoExposureMode;
- }
-
- // This one just fetches the data and stores it in the normal objects. No return value.
- async getColorCorrection() {
- this.CClift = await this.pullData("/colorCorrection/lift");
- this.CCgamma = await this.pullData("/colorCorrection/gamma");
- this.CCgain = await this.pullData("/colorCorrection/gain");
- this.CCoffset = await this.pullData("/colorCorrection/offset");
- this.CCcontrast = await this.pullData("/colorCorrection/contrast");
- this.CCcolor = await this.pullData("/colorCorrection/color");
- this.CClumacontribution = await this.pullData("/colorCorrection/lumaContribution");
- BMDCamera.updateUIColorCorrection();
- }
-
- // This method usually takes 200-250 ms
- async getAllInfo() {
- await this.getFormat();
- await this.getTransportMode();
- await this.getPlaybackState();
- await this.getRecordState();
- await this.getTimecode();
- await this.getPresets();
- await this.getActivePreset();
- await this.getAperture();
- await this.getZoom();
- await this.getFocus();
- await this.getISO();
- await this.getGain();
- await this.getWhiteBalance();
- await this.getND();
- await this.getShutter();
- await this.getAutoExposureMode();
- await this.getColorCorrection();
- }
-
- // =============== SETTERS ==================
-
- // name, hostname, APIaddress should never have to be set
-
- async setCodecFormat(newCodecFormatObject) {
- await this.pushData("/system/codecFormat",newCodecFormatObject);
- await sleep(500);
- await this.getCodecFormat();
- }
-
- async setVideoFormat(newVideoFormatObject) {
- await this.pushData("/system/videoFormat",newVideoFormatObject);
- await sleep(500);
- await this.getCodecFormat();
- }
-
- async setTransportMode(newTransportModeString) {
- await this.pushData("/transports/0",{"mode": newTransportModeString});
- await sleep(500);
- await this.getTransportMode();
- }
-
- async setPlaybackState(playbackStateObject) {
- await this.pushData("/transports/0/playback",playbackStateObject);
- await sleep(500);
- await this.getPlaybackState();
- }
-
- async sendPresetFile(file) {
- await sendRequest("POST",this.APIAddress+"/presets",file);
- }
-
- async setActivePreset(presetString) {
- await this.pushData("/presets/active",{"preset": presetString});
- await sleep(500);
- await this.getAllInfo();
- }
-
- async updatePreset(presetString) {
- await this.pushData("/presets/active",{"preset": presetString});
- await sleep(500);
- await this.getPresets();
- }
-
- async setAperture(apertureNormalisedFloat) {
- await this.pushData("/lens/iris",{"normalised": apertureNormalisedFloat});
- await sleep(1500);
- await this.getAperture();
- }
-
- async setZoom(zoomNormalisedFloat) {
- await this.pushData("/lens/zoom",{"normalised": zoomNormalisedFloat});
- await sleep(1500);
- await this.getZoom();
- }
-
- async setFocus(focusNormalisedFloat) {
- await this.pushData("/lens/focus",{"normalised": focusNormalisedFloat});
- await sleep(1500);
- await this.getFocus();
- }
-
- async setISO(ISOint) {
- await this.pushData("/video/iso",{"iso":ISOint});
- await sleep(500);
- await this.getISO();
- await this.getGain();
- }
-
- async setGain(gainInt) {
- await this.pushData("/video/gain",{"gain":gainInt});
- await sleep(500);
- await this.getGain();
- await this.getISO();
- }
-
- async setWhiteBalance(whiteBalanceInt, whiteBalanceTintInt) {
- await this.pushData("/video/whiteBalance",{"whiteBalance": whiteBalanceInt});
- await this.pushData("/video/whiteBalanceTint",{"whiteBalanceTint": whiteBalanceTintInt});
- await sleep(500);
- await this.getWhiteBalance();
- }
-
- async setND(NDstopInt) {
- await this.pushData("/video/ndFilter",{"stop": NDstopInt});
- await sleep(500);
- await this.getND();
- }
-
- async setNDDisplayMode(displayModeString) {
- await this.pushData("/video/ndFilter/displayMode",{"displayMode": displayModeString});
- await sleep(500);
- await this.getND();
- }
-
- // Accepts JSON obejcts with either shutterSpeed or shutterAngle properties
- // Note that the shutterAngle value returned by the API is 100x the actual value
- async setShutter(shutterObject) {
- await this.pushData("/video/shutter",shutterObject);
- await sleep(500);
- await this.getShutter();
- }
-
- async setAutoExposureMode(AEmodeObject) {
- await this.pushData("/video/autoExposure",AEmodeObject);
- await sleep(500);
- await this.getAutoExposureMode();
- }
-
- async setCCLift(CCliftObject) {
- await this.pushData("/colorCorrection/lift",CCliftObject);
- await sleep(500);
- await this.getColorCorrection();
- }
-
- async setCCGamma(CCgammaObject) {
- await this.pushData("/colorCorrection/gamma",CCgammaObject);
- await sleep(500);
- await this.getColorCorrection();
- }
-
- async setCCGain(CCgainObject) {
- await this.pushData("/colorCorrection/gain",CCgainObject);
- await sleep(500);
- await this.getColorCorrection();
- }
-
- async setCCOffset(CCoffsetObject) {
- await this.pushData("/colorCorrection/offset",CCoffsetObject);
- await sleep(500);
- await this.getColorCorrection();
- }
-
- async setCCContrast(CCcontrastObject) {
- await this.pushData("/colorCorrection/contrast",CCcontrastObject);
- await sleep(500);
- await this.getColorCorrection();
- }
-
- async setCCColor(CCcolorObject) {
- await this.pushData("/colorCorrection/color",CCcolorObject);
- await sleep(500);
- await this.getColorCorrection();
- }
-
- async setCCLumaContribuion(CClumacontributionObject) {
- await this.pushData("/colorCorrection/lumaContribution",CClumacontributionObject);
- await sleep(500);
- await this.getColorCorrection();
- }
-
- // =============== Other Commands =======================
- async doAutoFocus() {
- await this.pushData("/lens/focus/doAutoFocus");
- sleep(1500).then(() => this.getFocus());
- }
-
- async play() {
- await this.pushData("/transports/0/play");
- await sleep(500);
- await this.getPlaybackState();
- }
-
- async record() {
- await this.pushData("/transports/0/record",{"recording": true});
- await sleep(1000);
- await this.getRecordState();
- }
-
- async stopTransport() {
- await this.pushData("/transports/0/stop");
- await sleep(1000);
- await this.getPlaybackState();
- }
-
- async stopRecord() {
- await this.pushData("/transports/0/record",{"recording": false});
- await sleep(1000);
- await this.getRecordState();
- }
-}
-
-/* Helper Functions */
-async function sendRequest(method, url, data) {
- const xhttp = new XMLHttpRequest();
- var responseObject;
-
- xhttp.onload = function() {
- if (this.status == 200) {
- // Success w/ Data
- responseObject = JSON.parse(this.responseText);
- } else {
- // Pass along response data and stuff
- responseObject = this;
- }
- }
-
- xhttp.open(method, url, false);
- xhttp.send(JSON.stringify(data));
-
- return responseObject;
-}
-
-function sleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
-}
-
-/* (c) Dylan Speiser 2024
- github.com/DylanSpeiser */
\ No newline at end of file
diff --git a/BMDevice.js b/BMDevice.js
new file mode 100644
index 0000000..69324fe
--- /dev/null
+++ b/BMDevice.js
@@ -0,0 +1,272 @@
+/* Blackmagic Camera Control JS Class
+ Written based on the Camera Control
+ API Documentation from Blackmagic's
+ Developer info website.
+
+ (c) Dylan Speiser 2024
+ github.com/DylanSpeiser
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published.
+
+ This program is distributed in the hope that it will be useful but without
+ any warranty; without even the implied warranty of merchantability or fitness
+ for a particular purpose as specified by the License, which you should consult
+ for more details at LICENSE.txt in this repository.
+*/
+
+// Generic Blackmagic Device class
+class BMDevice {
+ // Pretty name and network hostname (strings)
+ name;
+ hostname;
+ APIAddress;
+
+ // WebSocket items
+ ws;
+ availableProperties;
+
+ // Active Flag
+ // Won't call updateUI if this is false
+ active = false;
+
+ // JSON Object to store all data
+ propertyData = {};
+
+ // Reference to UI Updating callback function
+ // For BYOUI purposes (Bring-Your-Own-UI). If you're using this class for your own UI,
+ // set this function to point to your UI updater.
+ updateUI() {};
+
+ // ============= CONSTRUCTOR ================
+ constructor(hostname) {
+ // Set name properties
+ this.hostname = hostname;
+ this.APIAddress = "http://"+hostname+"/control/api/v1";
+ this.name = this.hostname.replace(".local","").replaceAll("-"," ");
+
+ // Initialize WebSocket
+ this.ws = new WebSocket("ws://"+hostname+"/control/api/v1/event/websocket");
+
+ // Get a self object for accessing within callback fns
+ var self = this;
+
+ // Set the onmessage behavior
+ this.ws.onmessage = (event) => {
+ // Parse the event's data as JSON
+ let eventData = JSON.parse(event.data);
+
+ // Extract data we really care about
+ let messageData = eventData.data;
+
+ // If it's a listProperties message, update the available properties array
+ if (messageData.action == "listProperties") {
+ self.availableProperties = messageData.properties;
+ }
+
+ // If we get a response from the camera with property information, save it.
+ if (eventData.type == "response") {
+ Object.assign(this.propertyData, messageData.values);
+ }
+
+ // If it's a propertyValueChanged event, update the camera object accordingly and show it on the web page.
+ if (messageData.action == "propertyValueChanged") {
+ this.propertyData[messageData.property] = messageData.value;
+ }
+
+ if (this.active) {
+ // Update the UI
+ this.updateUI();
+ }
+
+ // Output info to console.
+ // console.log("WebSocket message received: ", eventData);
+ }
+
+ // Wait for the WebSocket to open
+ this.ws.onopen = (event) => {
+ // Once the WebSocket is open,
+
+ // Ask it for all the properties
+ self.ws.send(JSON.stringify({type: "request", data: {action: "listProperties"}}));
+
+ sleep(100).then(() => {
+ // Subscribe to all available events
+ this.availableProperties.forEach((str) => {
+ self.ws.send(JSON.stringify({type: "request", data: {action: "subscribe", properties: [str]}}));
+ });
+ });
+ }
+ }
+
+ // Returns a JSON Object of data we got from the device
+ GETdata(endpoint) {
+ // Just call sendRequest
+ return sendRequest("GET", this.APIAddress+endpoint);
+ }
+
+ // Send JSON Object data to the device
+ PUTdata(endpoint, data) {
+ // Just call sendRequest
+ return sendRequest("PUT", this.APIAddress+endpoint, data);
+ }
+
+ // ================= SETTERS =================
+ // Basically just wrappers for PUT requests to specific endpoints
+
+ // If the optional parameter is set to false, it will stop recording
+ record(state = true) {
+ this.PUTdata("/transports/0/record",{recording: state});
+ }
+
+ toggleRecord() {
+ let recordState = this.propertyData['/transports/0/record'].recording;
+
+ this.PUTdata("/transports/0/record",{recording: !recordState});
+ }
+
+ play() {
+ this.PUTdata("/transports/0/play");
+ }
+
+ stop() {
+ this.PUTdata("/transports/0/stop");
+ }
+
+ // Boolean parameter, true = forward, false = backwards
+ seek(direction) {
+ let clips = this.GETdata("/timelines/0")?.clips;
+ let playbackData = this.GETdata("/transports/playback");
+
+ let runningSum = 0;
+ let currentClipIndex = 0;
+ let clipStartingTimecodes = [];
+ let i = 0;
+
+ clips.forEach((clip) => {
+ if (runningSum+clip.frameCount > playbackData.position) {
+ currentClipIndex = i;
+ }
+ clipStartingTimecodes[i] = runningSum;
+ runningSum += clip.frameCount;
+ i++;
+ });
+
+ let newClipIndex = min(max(0,(direction ? currentClipIndex+1 : currentClipIndex-1)), clips.length);
+
+ playbackData.position = clipStartingTimecodes[newClipIndex];
+
+ this.PUTdata("/transports/playback", playbackData);
+ }
+
+ // Sets Timeline / Clip Looping
+ // Argument can be either "None", "Loop", or "Loop Clip"
+ setLoopMode(modeString) {
+ let newStateObj = this.propertyData['/transports/0/playback'];
+
+ if (modeString === "None") {
+ newStateObj.loop = false;
+ newStateObj.singleClip = false;
+ } else if (modeString === "Loop") {
+ newStateObj.loop = true;
+ newStateObj.singleClip = false;
+ } else if (modeString === "Loop Clip") {
+ newStateObj.loop = true;
+ newStateObj.singleClip = true;
+ }
+
+ this.PUTdata("/transports/0/playback", newStateObj);
+ }
+}
+
+// Child Class Specifically for Cameras
+class BMCamera extends BMDevice {
+ // Child class constructor
+ // Just passing the hostname to the superclass's constructor
+ constructor(hostname) {
+ super(hostname);
+ }
+
+ // Sets the white balance and tint based on the following preset:
+ // 0: Sunlight, 1: Tungsten, 2: Fluorescent, 3: Shade, 4: Cloudy
+ // Any other value will not affect the WB setting
+ setWhiteBalancePreset(presetIndex) {
+ let newWhiteBalance;
+ let newWhiteBalanceTint;
+
+ switch (presetIndex) {
+ case 0:
+ // Sunlight
+ newWhiteBalance = 5600;
+ newWhiteBalanceTint = 10;
+ break;
+ case 1:
+ // Tungsten
+ newWhiteBalance = 3200;
+ newWhiteBalanceTint = 0;
+ break;
+ case 2:
+ // Fluorescent
+ newWhiteBalance = 4000;
+ newWhiteBalanceTint = 15;
+ break;
+ case 3:
+ // Shade
+ newWhiteBalance = 4500;
+ newWhiteBalanceTint = 15;
+ break;
+ case 4:
+ // Cloudy
+ newWhiteBalance = 6500;
+ newWhiteBalanceTint = 10;
+ break;
+ default:
+ // If any other value is set, don't change anything
+ newWhiteBalance = this.GETdata("/video/whiteBalance").whiteBalance;
+ newWhiteBalanceTint = this.GETdata("/video/whiteBalanceTint").whiteBalanceTint;
+ }
+
+ this.PUTdata("/video/whiteBalance",{whiteBalance: newWhiteBalance});
+ this.PUTdata("/video/whiteBalanceTint",{whiteBalanceTint: newWhiteBalanceTint});
+ }
+}
+
+/* Helper Functions */
+
+// Send request with other method type
+function sendRequest(method, url, data) {
+ // Instantiate the XMLHttpRequest object
+ let xhr = new XMLHttpRequest();
+
+ // Create an object to store and return the response
+ let responseObject = {};
+
+ // Define the onload function
+ xhr.onload = function() {
+ if (this.status < 300) { // If the operation is successful
+ if (this.responseText)
+ responseObject = JSON.parse(this.responseText); // Give the data to the responseObject
+ responseObject.status = this.status; // Also pass along the status code for error handling
+ } else { // If there has been an error
+ responseObject = this; // Give the XMLHttpRequest data to the responseObject
+ console.error("Error ", this.status, ": ", this.statusText); // Log the error in the console
+ }
+ };
+
+ // Open the connection
+ // The "false" here specifies that we want to wait for the response to come back before returning from xhr.send()
+ xhr.open(method, url, false);
+
+ // Send the request with data
+ xhr.send(JSON.stringify(data));
+
+ // Return response data
+ return responseObject;
+}
+
+function sleep(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+/* (c) Dylan Speiser 2024
+ github.com/DylanSpeiser */
\ No newline at end of file
diff --git a/README.md b/README.md
index 132e7aa..58c6034 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@ This web app is utilizes the [Blackmagic](https://blackmagicdesign.com) Camera C
>This program was written based on the official REST API documentation from Blackmagic, which can be found [here](https://documents.blackmagicdesign.com/DeveloperManuals/RESTAPIforBlackmagicCameras.pdf)
-Using this tool, you can control your Blackmagic studio and cinema cameras *without any extra hardware!* Use it for remote monitoring, color correction, focus pulling, or keeping tabs on your eqiupment. The `BMD-Camera-Control.js` file is also useful if you want to write your own web app using the REST API. More details on how to interface with it can be found below.
+Using this tool, you can control your Blackmagic studio and cinema cameras *without any extra hardware!* Use it for remote monitoring, color correction, focus pulling, or keeping tabs on your eqiupment. The `BMCamera.js` file is also useful if you want to write your own web app using the REST API. More details on how to interface with it can be found below.

@@ -74,10 +74,10 @@ If you like this project and want it to improve, consider making a Pull Request
## Tutorials
For more information about using the BMD REST API, check out [my tutorial series](https://github.com/DylanSpeiser/BM-API-Tutorial/) that explains the basics of how to interact with the camera in JavaScript and Python.
-## Using `BMD-Camera-Control.js`
+## Using `BMCamera.js`
You are more than welcome to use this JavaScript class in your own projects. Just include the file (with its attributions).
-Cameras are represented as BMDCamera objects, instantiated with the `new` keyword and the constructor, which takes the hostname as a String argument.
+Cameras are represented as BMCamera objects, instantiated with the `new` keyword and the constructor, which takes the hostname as a String argument.
After instantiation, the constructor does NOT automatically fetch any data from the camera. This can be done using the `getAllInfo()` method, or the individual getters if you only need a few details. These are all asynchronous and so must be waited upon using `await` or `.then()`. Consult your nearest Google search bar for help implementing asynchronous JavaScript, that's how I did it.
@@ -86,7 +86,7 @@ Many of setter functions take Objects as arguments, rather than Strings or ints.
This file is heavily commented so everything _should_ be pretty clear, but let me know in the Issue tracker if you're having trouble.
## BYOUI
-If you want to use `BMD-Camera-Control.js` in your own UI, there are static references to functions in the BMDCamera class that get called after changing a value. They all look like `updateUIxxxxxx()`. Set these references to point to your UI updating routines in _your_ source file. No need to write them in `BMD-Camera-Control.js`.
+If you want to use `BMCamera.js` in your own UI, there are static references to functions in the BMCamera class that get called after changing a value. They all look like `updateUIxxxxxx()`. Set these references to point to your UI updating routines in _your_ source file. No need to write them in `BMCamera.js`.
# Issues and To-Dos
## Known Issues
diff --git a/index.html b/index.html
index 9f67aeb..7853cba 100644
--- a/index.html
+++ b/index.html
@@ -14,7 +14,7 @@