OctoPrint-BitBang 0.2.6__tar.gz → 0.2.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8/OctoPrint_BitBang.egg-info}/PKG-INFO +11 -6
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/OctoPrint_BitBang.egg-info/SOURCES.txt +2 -1
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/OctoPrint_BitBang.egg-info/requires.txt +0 -3
- {octoprint_bitbang-0.2.6/OctoPrint_BitBang.egg-info → octoprint_bitbang-0.2.8}/PKG-INFO +11 -6
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/README.md +10 -2
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/__init__.py +8 -1
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/_plugin.py +155 -2
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/bin/bitbang-linux-amd64 +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/bin/bitbang-linux-arm64 +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/bin/bitbang-linux-armv7 +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/static/js/bitbang.js +130 -0
- octoprint_bitbang-0.2.8/octoprint_bitbang/templates/bitbang_navbar.jinja2 +4 -0
- octoprint_bitbang-0.2.8/octoprint_bitbang/templates/bitbang_settings.jinja2 +110 -0
- octoprint_bitbang-0.2.8/octoprint_bitbang/templates/bitbang_wizard.jinja2 +47 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/pyproject.toml +1 -4
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/setup.py +1 -1
- octoprint_bitbang-0.2.6/octoprint_bitbang/templates/bitbang_navbar.jinja2 +0 -29
- octoprint_bitbang-0.2.6/octoprint_bitbang/templates/bitbang_settings.jinja2 +0 -169
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/LICENSE +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/MANIFEST.in +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/OctoPrint_BitBang.egg-info/dependency_links.txt +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/OctoPrint_BitBang.egg-info/entry_points.txt +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/OctoPrint_BitBang.egg-info/top_level.txt +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/__main__.py +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/app.py +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/camera.py +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/flip_track.py +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/index.html +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/octoprint_adapter.py +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/pi_camera_track.py +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/pi_h264_source.py +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/static/favicon.png +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/templates/bitbang_webcam.jinja2 +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/usb_camera_source.py +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/v4l2_h264_source.py +0 -0
- {octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: OctoPrint-BitBang
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: Remote OctoPrint access with live H.264 video via BitBang WebRTC
|
|
5
5
|
Author-email: Rich LeGrand <rich.m.legrand@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -24,9 +24,6 @@ Requires-Python: >=3.10
|
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
26
|
Requires-Dist: bitbang>=0.1.53
|
|
27
|
-
Requires-Dist: aiohttp>=3.8.0
|
|
28
|
-
Requires-Dist: Pillow>=9.0.0
|
|
29
|
-
Requires-Dist: numpy>=1.20.0
|
|
30
27
|
Requires-Dist: aiortc<1.11
|
|
31
28
|
Requires-Dist: av<12; platform_machine == "armv7l"
|
|
32
29
|
Dynamic: license-file
|
|
@@ -118,12 +115,20 @@ All settings live in **Settings → BitBang**:
|
|
|
118
115
|
| Setting | Effect |
|
|
119
116
|
|---|---|
|
|
120
117
|
| Enabled | Toggle BitBang remote access |
|
|
121
|
-
| PIN |
|
|
118
|
+
| PIN | **Required by default.** At least 4 characters; prompted on the remote URL. Remote access stays **off** until a PIN is set (or "Allow no PIN" is ticked). |
|
|
119
|
+
| Allow no PIN | Expose remote access with no PIN (not recommended — anyone with the link can control the printer) |
|
|
122
120
|
| Camera | Auto-detect, or select from dropdown list |
|
|
123
121
|
| Resolution | VGA → HD (depending on what selected camera supports) |
|
|
124
122
|
| Flip horizontal / vertical | Flip video if necessary |
|
|
125
123
|
|
|
126
|
-
|
|
124
|
+
Changes to the PIN and Enabled settings take effect immediately (no OctoPrint restart). Full-screen button and brightness slider are overlaid on the video window (Control tab) and update immediately.
|
|
125
|
+
|
|
126
|
+
### Set a PIN (required)
|
|
127
|
+
|
|
128
|
+
BitBang exposes a public link to your printer, so it is protected by a PIN. On
|
|
129
|
+
first install a **setup wizard** prompts you to choose one — remote access does
|
|
130
|
+
not start until you do (fail-closed). You can change it any time under
|
|
131
|
+
**Settings → BitBang**.
|
|
127
132
|
|
|
128
133
|
## How it works
|
|
129
134
|
|
|
@@ -28,4 +28,5 @@ octoprint_bitbang/static/favicon.png
|
|
|
28
28
|
octoprint_bitbang/static/js/bitbang.js
|
|
29
29
|
octoprint_bitbang/templates/bitbang_navbar.jinja2
|
|
30
30
|
octoprint_bitbang/templates/bitbang_settings.jinja2
|
|
31
|
-
octoprint_bitbang/templates/bitbang_webcam.jinja2
|
|
31
|
+
octoprint_bitbang/templates/bitbang_webcam.jinja2
|
|
32
|
+
octoprint_bitbang/templates/bitbang_wizard.jinja2
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: OctoPrint-BitBang
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: Remote OctoPrint access with live H.264 video via BitBang WebRTC
|
|
5
5
|
Author-email: Rich LeGrand <rich.m.legrand@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -24,9 +24,6 @@ Requires-Python: >=3.10
|
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
26
|
Requires-Dist: bitbang>=0.1.53
|
|
27
|
-
Requires-Dist: aiohttp>=3.8.0
|
|
28
|
-
Requires-Dist: Pillow>=9.0.0
|
|
29
|
-
Requires-Dist: numpy>=1.20.0
|
|
30
27
|
Requires-Dist: aiortc<1.11
|
|
31
28
|
Requires-Dist: av<12; platform_machine == "armv7l"
|
|
32
29
|
Dynamic: license-file
|
|
@@ -118,12 +115,20 @@ All settings live in **Settings → BitBang**:
|
|
|
118
115
|
| Setting | Effect |
|
|
119
116
|
|---|---|
|
|
120
117
|
| Enabled | Toggle BitBang remote access |
|
|
121
|
-
| PIN |
|
|
118
|
+
| PIN | **Required by default.** At least 4 characters; prompted on the remote URL. Remote access stays **off** until a PIN is set (or "Allow no PIN" is ticked). |
|
|
119
|
+
| Allow no PIN | Expose remote access with no PIN (not recommended — anyone with the link can control the printer) |
|
|
122
120
|
| Camera | Auto-detect, or select from dropdown list |
|
|
123
121
|
| Resolution | VGA → HD (depending on what selected camera supports) |
|
|
124
122
|
| Flip horizontal / vertical | Flip video if necessary |
|
|
125
123
|
|
|
126
|
-
|
|
124
|
+
Changes to the PIN and Enabled settings take effect immediately (no OctoPrint restart). Full-screen button and brightness slider are overlaid on the video window (Control tab) and update immediately.
|
|
125
|
+
|
|
126
|
+
### Set a PIN (required)
|
|
127
|
+
|
|
128
|
+
BitBang exposes a public link to your printer, so it is protected by a PIN. On
|
|
129
|
+
first install a **setup wizard** prompts you to choose one — remote access does
|
|
130
|
+
not start until you do (fail-closed). You can change it any time under
|
|
131
|
+
**Settings → BitBang**.
|
|
127
132
|
|
|
128
133
|
## How it works
|
|
129
134
|
|
|
@@ -85,12 +85,20 @@ All settings live in **Settings → BitBang**:
|
|
|
85
85
|
| Setting | Effect |
|
|
86
86
|
|---|---|
|
|
87
87
|
| Enabled | Toggle BitBang remote access |
|
|
88
|
-
| PIN |
|
|
88
|
+
| PIN | **Required by default.** At least 4 characters; prompted on the remote URL. Remote access stays **off** until a PIN is set (or "Allow no PIN" is ticked). |
|
|
89
|
+
| Allow no PIN | Expose remote access with no PIN (not recommended — anyone with the link can control the printer) |
|
|
89
90
|
| Camera | Auto-detect, or select from dropdown list |
|
|
90
91
|
| Resolution | VGA → HD (depending on what selected camera supports) |
|
|
91
92
|
| Flip horizontal / vertical | Flip video if necessary |
|
|
92
93
|
|
|
93
|
-
|
|
94
|
+
Changes to the PIN and Enabled settings take effect immediately (no OctoPrint restart). Full-screen button and brightness slider are overlaid on the video window (Control tab) and update immediately.
|
|
95
|
+
|
|
96
|
+
### Set a PIN (required)
|
|
97
|
+
|
|
98
|
+
BitBang exposes a public link to your printer, so it is protected by a PIN. On
|
|
99
|
+
first install a **setup wizard** prompts you to choose one — remote access does
|
|
100
|
+
not start until you do (fail-closed). You can change it any time under
|
|
101
|
+
**Settings → BitBang**.
|
|
94
102
|
|
|
95
103
|
## How it works
|
|
96
104
|
|
|
@@ -4,8 +4,15 @@ Remote OctoPrint access with live H.264 video via BitBang WebRTC.
|
|
|
4
4
|
No account, no subscription, no port forwarding. One shareable link.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
8
|
+
|
|
7
9
|
__plugin_name__ = "BitBang"
|
|
8
|
-
|
|
10
|
+
try:
|
|
11
|
+
# Single source of truth: read the version from the installed package
|
|
12
|
+
# metadata (declared once in pyproject.toml) rather than duplicating it here.
|
|
13
|
+
__plugin_version__ = version("OctoPrint-BitBang")
|
|
14
|
+
except PackageNotFoundError: # running from a source checkout, not pip-installed
|
|
15
|
+
__plugin_version__ = "0.0.0+unknown"
|
|
9
16
|
__plugin_description__ = "Remote OctoPrint access with live H.264 video via BitBang WebRTC. No account, no port forwarding, one shareable link."
|
|
10
17
|
__plugin_url__ = "https://github.com/richlegrand/OctoPrint-BitBang"
|
|
11
18
|
__plugin_author__ = "Rich LeGrand"
|
|
@@ -47,6 +47,12 @@ except ImportError as e:
|
|
|
47
47
|
)
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
# A PIN must be empty (remote access then gated/off) or at least this many
|
|
51
|
+
# characters. Mirrored by the wizard/settings JS; enforced server-side in
|
|
52
|
+
# on_settings_save as the authoritative backstop.
|
|
53
|
+
MIN_PIN_LENGTH = 4
|
|
54
|
+
|
|
55
|
+
|
|
50
56
|
class BitBangPlugin(
|
|
51
57
|
octoprint.plugin.StartupPlugin,
|
|
52
58
|
octoprint.plugin.ShutdownPlugin,
|
|
@@ -55,12 +61,14 @@ class BitBangPlugin(
|
|
|
55
61
|
octoprint.plugin.AssetPlugin,
|
|
56
62
|
octoprint.plugin.BlueprintPlugin,
|
|
57
63
|
octoprint.plugin.WebcamProviderPlugin,
|
|
64
|
+
octoprint.plugin.WizardPlugin,
|
|
58
65
|
):
|
|
59
66
|
def __init__(self):
|
|
60
67
|
super().__init__()
|
|
61
68
|
self._adapter = None
|
|
62
69
|
self._thread = None
|
|
63
70
|
self._local_pcs = set() # track local WebRTC peer connections
|
|
71
|
+
self._running = False # whether the remote-access proxy is live
|
|
64
72
|
|
|
65
73
|
def on_shutdown(self):
|
|
66
74
|
# Release the camera cleanly when OctoPrint shuts down via its own
|
|
@@ -93,8 +101,30 @@ class BitBangPlugin(
|
|
|
93
101
|
if not self._settings.get_boolean(["enabled"]):
|
|
94
102
|
self._logger.info("BitBang disabled in settings")
|
|
95
103
|
return
|
|
104
|
+
if not self._remote_access_allowed():
|
|
105
|
+
self._logger.warning(
|
|
106
|
+
"BitBang: remote access NOT started — no PIN is set. Set a PIN "
|
|
107
|
+
"in the BitBang settings (or, advanced, explicitly allow running "
|
|
108
|
+
"without one) to enable remote access."
|
|
109
|
+
)
|
|
110
|
+
return
|
|
96
111
|
self._start_bitbang()
|
|
97
112
|
|
|
113
|
+
def _remote_access_allowed(self):
|
|
114
|
+
"""Secure-by-default gate. The public tunnel is only exposed when a
|
|
115
|
+
PIN protects it, or the user has explicitly opted into running without
|
|
116
|
+
one. An empty PIN with no opt-in means remote access stays OFF — this
|
|
117
|
+
is the enforcement behind the setup wizard (which merely prompts).
|
|
118
|
+
|
|
119
|
+
Without this, anyone holding the share URL reaches OctoPrint with no
|
|
120
|
+
BitBang-layer auth; for users who enabled OctoPrint's autologinLocal
|
|
121
|
+
that is a full remote takeover (see the X-Forwarded-For handling in
|
|
122
|
+
the Go proxy)."""
|
|
123
|
+
pin = (self._settings.get(["pin"]) or "").strip()
|
|
124
|
+
if pin:
|
|
125
|
+
return True
|
|
126
|
+
return self._settings.get_boolean(["allow_no_pin"])
|
|
127
|
+
|
|
98
128
|
def _probe_picamera2_sensor(self):
|
|
99
129
|
# Cache before the adapter opens the camera — picamera2 can't be
|
|
100
130
|
# opened twice, so the resolutions endpoint relies on this.
|
|
@@ -201,11 +231,15 @@ class BitBangPlugin(
|
|
|
201
231
|
self._settings.set(["url"], url)
|
|
202
232
|
self._settings.save()
|
|
203
233
|
self._logger.info(f"BitBang remote access: {url}")
|
|
234
|
+
# Push the URL to the frontend so the navbar reflects it live, instead
|
|
235
|
+
# of the browser polling the settings API.
|
|
236
|
+
self._plugin_manager.send_plugin_message(self._identifier, {"url": url})
|
|
204
237
|
|
|
205
238
|
# Split-transport: a supervised Go proxy (data + HTTP/WS over native-SCTP
|
|
206
239
|
# pion) owns the transport under our shared identity, and our camera
|
|
207
240
|
# track is fed into a video PeerConnection over a socketpair relay.
|
|
208
241
|
self._start_video_bridge()
|
|
242
|
+
self._running = True
|
|
209
243
|
|
|
210
244
|
def _run_aiortc_loop(self):
|
|
211
245
|
"""Bare asyncio loop for aiortc. Replaces the adapter's own bitbang
|
|
@@ -331,6 +365,39 @@ class BitBangPlugin(
|
|
|
331
365
|
time.sleep(backoff)
|
|
332
366
|
backoff = min(backoff * 2, 30)
|
|
333
367
|
|
|
368
|
+
def _stop_bitbang(self):
|
|
369
|
+
"""Tear down the remote-access proxy (Go subprocess supervisor +
|
|
370
|
+
aiortc loop + camera). Best-effort; each step is independently
|
|
371
|
+
guarded so a failure in one doesn't strand the others."""
|
|
372
|
+
self._running = False
|
|
373
|
+
# Tell the supervisor not to respawn, then kill the current proxy.
|
|
374
|
+
self._go_stop = True
|
|
375
|
+
proc = getattr(self, "_go_proc", None)
|
|
376
|
+
if proc is not None and proc.poll() is None:
|
|
377
|
+
try:
|
|
378
|
+
proc.terminate()
|
|
379
|
+
except Exception:
|
|
380
|
+
pass
|
|
381
|
+
# Release the camera.
|
|
382
|
+
try:
|
|
383
|
+
player = getattr(self._adapter, "player", None) if self._adapter else None
|
|
384
|
+
if player is not None:
|
|
385
|
+
player.stop()
|
|
386
|
+
except Exception as e:
|
|
387
|
+
self._logger.warning(f"BitBang: error stopping camera: {e}")
|
|
388
|
+
# Stop the aiortc event loop so its thread can exit.
|
|
389
|
+
try:
|
|
390
|
+
loop = getattr(self._adapter, "_loop", None) if self._adapter else None
|
|
391
|
+
if loop is not None and loop.is_running():
|
|
392
|
+
loop.call_soon_threadsafe(loop.stop)
|
|
393
|
+
except Exception:
|
|
394
|
+
pass
|
|
395
|
+
self._adapter = None
|
|
396
|
+
# Clear the persisted URL so the navbar doesn't show a dead link.
|
|
397
|
+
self._settings.set(["url"], "")
|
|
398
|
+
self._settings.save()
|
|
399
|
+
self._plugin_manager.send_plugin_message(self._identifier, {"url": ""})
|
|
400
|
+
|
|
334
401
|
# -- Local WebRTC video signaling --
|
|
335
402
|
|
|
336
403
|
@octoprint.plugin.BlueprintPlugin.route("/ice-servers", methods=["GET"])
|
|
@@ -753,6 +820,9 @@ class BitBangPlugin(
|
|
|
753
820
|
return {
|
|
754
821
|
"enabled": True,
|
|
755
822
|
"pin": "",
|
|
823
|
+
# Explicit opt-in to expose remote access with NO PIN. Default
|
|
824
|
+
# False so a fresh install is gated until the wizard runs.
|
|
825
|
+
"allow_no_pin": False,
|
|
756
826
|
"url": "",
|
|
757
827
|
"camera_device": "",
|
|
758
828
|
"camera_resolution": "640x480",
|
|
@@ -762,16 +832,99 @@ class BitBangPlugin(
|
|
|
762
832
|
"signaling_server": "bitba.ng",
|
|
763
833
|
}
|
|
764
834
|
|
|
835
|
+
def on_settings_save(self, data):
|
|
836
|
+
# Server-side PIN-policy backstop. The wizard/settings JS validates
|
|
837
|
+
# too, but this is authoritative: a non-empty PIN below the minimum
|
|
838
|
+
# length is rejected (the prior value is kept) rather than persisted.
|
|
839
|
+
if data.get("pin"):
|
|
840
|
+
pin = str(data["pin"]).strip()
|
|
841
|
+
if 0 < len(pin) < MIN_PIN_LENGTH:
|
|
842
|
+
self._logger.warning(
|
|
843
|
+
"BitBang: rejected PIN shorter than %d characters",
|
|
844
|
+
MIN_PIN_LENGTH,
|
|
845
|
+
)
|
|
846
|
+
data["pin"] = self._settings.get(["pin"])
|
|
847
|
+
else:
|
|
848
|
+
data["pin"] = pin
|
|
849
|
+
|
|
850
|
+
# Snapshot gate-relevant state, save, then reconcile if it changed so
|
|
851
|
+
# the wizard's "set a PIN" takes effect without an OctoPrint restart.
|
|
852
|
+
before = self._gate_state()
|
|
853
|
+
octoprint.plugin.SettingsPlugin.on_settings_save(self, data)
|
|
854
|
+
if self._gate_state() != before:
|
|
855
|
+
self._reconcile_remote_access()
|
|
856
|
+
|
|
857
|
+
def _gate_state(self):
|
|
858
|
+
"""The tuple of settings that determines whether/how the proxy runs."""
|
|
859
|
+
return (
|
|
860
|
+
self._settings.get_boolean(["enabled"]),
|
|
861
|
+
(self._settings.get(["pin"]) or "").strip(),
|
|
862
|
+
self._settings.get_boolean(["allow_no_pin"]),
|
|
863
|
+
)
|
|
864
|
+
|
|
865
|
+
def _reconcile_remote_access(self):
|
|
866
|
+
"""Bring the running proxy in line with current settings. Best-effort
|
|
867
|
+
and defensive: on any failure the user can still restart OctoPrint to
|
|
868
|
+
pick up the change (the prior behavior)."""
|
|
869
|
+
if _VIDEO_IMPORT_ERROR:
|
|
870
|
+
return # video stack unavailable; nothing to start/stop
|
|
871
|
+
try:
|
|
872
|
+
should_run = self._settings.get_boolean(["enabled"]) and self._remote_access_allowed()
|
|
873
|
+
if should_run and not self._running:
|
|
874
|
+
self._logger.info("BitBang: remote access now permitted — starting")
|
|
875
|
+
self._start_bitbang()
|
|
876
|
+
elif not should_run and self._running:
|
|
877
|
+
self._logger.info("BitBang: remote access no longer permitted — stopping")
|
|
878
|
+
self._stop_bitbang()
|
|
879
|
+
elif should_run and self._running:
|
|
880
|
+
# Still running, but the PIN may have changed. The Go proxy
|
|
881
|
+
# reads -pin fresh on each spawn, so bouncing the subprocess
|
|
882
|
+
# (the supervisor respawns it) applies the new PIN.
|
|
883
|
+
self._logger.info("BitBang: PIN/config changed — restarting proxy")
|
|
884
|
+
self._restart_go_proxy()
|
|
885
|
+
except Exception as e:
|
|
886
|
+
self._logger.warning(
|
|
887
|
+
"BitBang: live reconcile failed (%s); restart OctoPrint to apply", e
|
|
888
|
+
)
|
|
889
|
+
|
|
890
|
+
def _restart_go_proxy(self):
|
|
891
|
+
"""Bounce just the Go subprocess; the supervisor loop respawns it with
|
|
892
|
+
the current -pin. Falls back to a full restart if no proc is tracked."""
|
|
893
|
+
proc = getattr(self, "_go_proc", None)
|
|
894
|
+
if proc is not None and proc.poll() is None:
|
|
895
|
+
proc.terminate()
|
|
896
|
+
else:
|
|
897
|
+
self._stop_bitbang()
|
|
898
|
+
if self._settings.get_boolean(["enabled"]) and self._remote_access_allowed():
|
|
899
|
+
self._start_bitbang()
|
|
900
|
+
|
|
765
901
|
def get_template_configs(self):
|
|
766
902
|
return [
|
|
767
|
-
{"type": "settings", "custom_bindings":
|
|
768
|
-
{"type": "navbar", "custom_bindings":
|
|
903
|
+
{"type": "settings", "custom_bindings": True},
|
|
904
|
+
{"type": "navbar", "custom_bindings": True},
|
|
905
|
+
{"type": "wizard", "name": "BitBang", "template": "bitbang_wizard.jinja2"},
|
|
769
906
|
# Render the live view as a proper webcam provider template so
|
|
770
907
|
# OctoPrint shows it only when "BitBang Camera" is the selected
|
|
771
908
|
# webcam -- no DOM-replacing the classic webcam.
|
|
772
909
|
{"type": "webcam", "name": "BitBang Camera", "template": "bitbang_webcam.jinja2"},
|
|
773
910
|
]
|
|
774
911
|
|
|
912
|
+
# -- Setup wizard (secure-by-default PIN prompt) --
|
|
913
|
+
|
|
914
|
+
def is_wizard_required(self):
|
|
915
|
+
# Show the wizard whenever remote access would be enabled but is
|
|
916
|
+
# currently ungated (no PIN, no explicit opt-out). Covers fresh
|
|
917
|
+
# installs and existing no-PIN users on upgrade.
|
|
918
|
+
return self._settings.get_boolean(["enabled"]) and not self._remote_access_allowed()
|
|
919
|
+
|
|
920
|
+
def get_wizard_version(self):
|
|
921
|
+
# Bump to re-show the wizard to users who already cleared an older
|
|
922
|
+
# version (e.g. if the PIN policy changes again).
|
|
923
|
+
return 1
|
|
924
|
+
|
|
925
|
+
def get_wizard_details(self):
|
|
926
|
+
return {"required": self.is_wizard_required()}
|
|
927
|
+
|
|
775
928
|
def get_template_vars(self):
|
|
776
929
|
return {"plugin_version": __plugin_version__}
|
|
777
930
|
|
{octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/bin/bitbang-linux-amd64
RENAMED
|
Binary file
|
{octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/bin/bitbang-linux-arm64
RENAMED
|
Binary file
|
{octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/octoprint_bitbang/bin/bitbang-linux-armv7
RENAMED
|
Binary file
|
|
@@ -321,3 +321,133 @@
|
|
|
321
321
|
}
|
|
322
322
|
});
|
|
323
323
|
})();
|
|
324
|
+
|
|
325
|
+
/*
|
|
326
|
+
* Settings + navbar viewmodel.
|
|
327
|
+
*
|
|
328
|
+
* The navbar link and the camera/resolution dropdowns used to read plugin
|
|
329
|
+
* state via raw fetch('/api/settings') + manual DOM. This binds them through
|
|
330
|
+
* the standard settingsViewModel and Knockout instead: settings come from the
|
|
331
|
+
* settings observable, camera lists from the plugin's blueprint endpoints, and
|
|
332
|
+
* the live remote URL arrives via a plugin message (no polling).
|
|
333
|
+
*/
|
|
334
|
+
function BitBangViewModel(parameters) {
|
|
335
|
+
var self = this;
|
|
336
|
+
self.settings = parameters[0];
|
|
337
|
+
|
|
338
|
+
self.cameras = ko.observableArray([]);
|
|
339
|
+
self.resolutions = ko.observableArray([]);
|
|
340
|
+
self.remoteUrl = ko.observable("");
|
|
341
|
+
|
|
342
|
+
function bb() { return self.settings.settings.plugins.bitbang; }
|
|
343
|
+
|
|
344
|
+
self.loadResolutions = function (device) {
|
|
345
|
+
return OctoPrint.get(
|
|
346
|
+
"plugin/bitbang/resolutions?device=" + encodeURIComponent(device || "")
|
|
347
|
+
).done(function (resolutions) {
|
|
348
|
+
self.resolutions(resolutions);
|
|
349
|
+
// Keep the saved resolution if it's still valid for this device,
|
|
350
|
+
// otherwise fall back to the first available.
|
|
351
|
+
if (resolutions.indexOf(bb().camera_resolution()) < 0) {
|
|
352
|
+
bb().camera_resolution(resolutions[0] || "");
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
self.loadCameras = function () {
|
|
358
|
+
return OctoPrint.get("plugin/bitbang/cameras").done(function (cameras) {
|
|
359
|
+
// Lead with an explicit auto-detect entry (value "") so the saved
|
|
360
|
+
// empty setting round-trips cleanly.
|
|
361
|
+
self.cameras([{ device: "", name: "Auto-detect" }].concat(cameras));
|
|
362
|
+
self.loadResolutions(bb().camera_device());
|
|
363
|
+
});
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
self.refreshCameras = function () { self.loadCameras(); };
|
|
367
|
+
|
|
368
|
+
self.showUrl = function () {
|
|
369
|
+
var url = self.remoteUrl();
|
|
370
|
+
if (url) {
|
|
371
|
+
window.prompt("BitBang URL (Ctrl+C to copy):", url);
|
|
372
|
+
} else {
|
|
373
|
+
alert("BitBang URL not available yet");
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
// Repopulate the dropdowns each time the settings dialog opens.
|
|
378
|
+
self.onSettingsShown = function () { self.loadCameras(); };
|
|
379
|
+
|
|
380
|
+
self.onStartupComplete = function () {
|
|
381
|
+
self.remoteUrl(bb().url() || "");
|
|
382
|
+
// Reload resolutions whenever the selected camera changes.
|
|
383
|
+
bb().camera_device.subscribe(function (device) {
|
|
384
|
+
self.loadResolutions(device);
|
|
385
|
+
});
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
// The plugin pushes the remote URL when BitBang connects, so the navbar
|
|
389
|
+
// reflects it live.
|
|
390
|
+
self.onDataUpdaterPluginMessage = function (plugin, data) {
|
|
391
|
+
if (plugin === "bitbang" && data && data.url !== undefined) {
|
|
392
|
+
self.remoteUrl(data.url);
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
OCTOPRINT_VIEWMODELS.push({
|
|
398
|
+
construct: BitBangViewModel,
|
|
399
|
+
dependencies: ["settingsViewModel"],
|
|
400
|
+
elements: ["#navbar_plugin_bitbang", "#settings_plugin_bitbang"]
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
/*
|
|
404
|
+
* Setup-wizard viewmodel. The wizard fields bind directly to the shared
|
|
405
|
+
* settings observables, so OctoPrint persists them on Finish (which then
|
|
406
|
+
* runs the server-side PIN-length backstop in on_settings_save). This VM
|
|
407
|
+
* only supplies live validation feedback for the PIN field.
|
|
408
|
+
*/
|
|
409
|
+
function BitBangWizardViewModel(parameters) {
|
|
410
|
+
var self = this;
|
|
411
|
+
self.settings = parameters[0];
|
|
412
|
+
|
|
413
|
+
function bb() {
|
|
414
|
+
var plugins = self.settings.settings.plugins;
|
|
415
|
+
return plugins && plugins.bitbang ? plugins.bitbang : null;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function pinOk() {
|
|
419
|
+
var b = bb();
|
|
420
|
+
if (!b) return true;
|
|
421
|
+
return b.allow_no_pin() || (b.pin() || "").trim().length >= 4;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
self.pinTooShort = ko.pureComputed(function () {
|
|
425
|
+
var b = bb();
|
|
426
|
+
if (!b) return false;
|
|
427
|
+
var pin = (b.pin() || "").trim();
|
|
428
|
+
return pin.length > 0 && pin.length < 4;
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// Set once the user tries to finish without a usable PIN. Drives the
|
|
432
|
+
// blocking message; clears reactively once they set a valid PIN or
|
|
433
|
+
// tick the no-PIN opt-out.
|
|
434
|
+
self.finishAttempted = ko.observable(false);
|
|
435
|
+
self.finishBlocked = ko.pureComputed(function () {
|
|
436
|
+
return self.finishAttempted() && !pinOk();
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// Veto the wizard's Finish button unless a valid PIN is set (or the user
|
|
440
|
+
// explicitly opted out). Returning false keeps the dialog open — see
|
|
441
|
+
// OctoPrint wizard.js onBeforeWizardFinish.
|
|
442
|
+
self.onBeforeWizardFinish = function () {
|
|
443
|
+
if (pinOk()) return true;
|
|
444
|
+
self.finishAttempted(true);
|
|
445
|
+
return false;
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
OCTOPRINT_VIEWMODELS.push({
|
|
450
|
+
construct: BitBangWizardViewModel,
|
|
451
|
+
dependencies: ["settingsViewModel"],
|
|
452
|
+
elements: ["#wizard_plugin_bitbang"]
|
|
453
|
+
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<div id="settings_bitbang">
|
|
2
|
+
<h3>BitBang Remote Access</h3>
|
|
3
|
+
|
|
4
|
+
<form class="form-horizontal">
|
|
5
|
+
<div class="control-group">
|
|
6
|
+
<label class="control-label">Enabled</label>
|
|
7
|
+
<div class="controls">
|
|
8
|
+
<input type="checkbox" data-bind="checked: settings.settings.plugins.bitbang.enabled">
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="control-group">
|
|
13
|
+
<label class="control-label">PIN Protection</label>
|
|
14
|
+
<div class="controls">
|
|
15
|
+
<input type="text" class="input-medium"
|
|
16
|
+
data-bind="value: settings.settings.plugins.bitbang.pin"
|
|
17
|
+
placeholder="At least 4 characters">
|
|
18
|
+
<span class="help-inline">
|
|
19
|
+
Protects remote access. At least 4 characters. Leave empty
|
|
20
|
+
only if you tick “allow no PIN” below —
|
|
21
|
+
otherwise remote access stays disabled.
|
|
22
|
+
</span>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div class="control-group">
|
|
27
|
+
<div class="controls">
|
|
28
|
+
<label class="checkbox">
|
|
29
|
+
<input type="checkbox"
|
|
30
|
+
data-bind="checked: settings.settings.plugins.bitbang.allow_no_pin">
|
|
31
|
+
Allow remote access without a PIN (not recommended)
|
|
32
|
+
</label>
|
|
33
|
+
<span class="help-block muted">
|
|
34
|
+
Anyone who has the share link could control this printer.
|
|
35
|
+
</span>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div class="control-group">
|
|
40
|
+
<label class="control-label">Remote URL</label>
|
|
41
|
+
<div class="controls">
|
|
42
|
+
<span data-bind="visible: settings.settings.plugins.bitbang.url()">
|
|
43
|
+
<a data-bind="attr: {href: settings.settings.plugins.bitbang.url()}, text: settings.settings.plugins.bitbang.url()"
|
|
44
|
+
target="_blank" rel="noopener"></a>
|
|
45
|
+
</span>
|
|
46
|
+
<span data-bind="visible: !settings.settings.plugins.bitbang.url()" class="muted">
|
|
47
|
+
URL will appear after BitBang connects
|
|
48
|
+
</span>
|
|
49
|
+
<span class="help-inline">Share this link for remote access</span>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<h3>Camera</h3>
|
|
54
|
+
|
|
55
|
+
<div class="control-group">
|
|
56
|
+
<label class="control-label">Camera</label>
|
|
57
|
+
<div class="controls">
|
|
58
|
+
<select class="input-xlarge"
|
|
59
|
+
data-bind="options: cameras,
|
|
60
|
+
optionsText: function(c) { return c.device ? c.name + ' (' + c.device + ')' : c.name; },
|
|
61
|
+
optionsValue: 'device',
|
|
62
|
+
value: settings.settings.plugins.bitbang.camera_device,
|
|
63
|
+
valueAllowUnset: true"></select>
|
|
64
|
+
<button class="btn btn-mini" type="button" data-bind="click: refreshCameras" title="Refresh camera list">
|
|
65
|
+
<i class="fas fa-sync"></i>
|
|
66
|
+
</button>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div class="control-group">
|
|
71
|
+
<label class="control-label">Resolution</label>
|
|
72
|
+
<div class="controls">
|
|
73
|
+
<select class="input-medium"
|
|
74
|
+
data-bind="options: resolutions,
|
|
75
|
+
value: settings.settings.plugins.bitbang.camera_resolution,
|
|
76
|
+
valueAllowUnset: true"></select>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div class="control-group">
|
|
81
|
+
<label class="control-label">Image orientation</label>
|
|
82
|
+
<div class="controls">
|
|
83
|
+
<label class="checkbox inline">
|
|
84
|
+
<input type="checkbox" data-bind="checked: settings.settings.plugins.bitbang.flip_horizontal">
|
|
85
|
+
Flip horizontal
|
|
86
|
+
</label>
|
|
87
|
+
<label class="checkbox inline">
|
|
88
|
+
<input type="checkbox" data-bind="checked: settings.settings.plugins.bitbang.flip_vertical">
|
|
89
|
+
Flip vertical
|
|
90
|
+
</label>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<h3>Advanced</h3>
|
|
95
|
+
|
|
96
|
+
<div class="control-group">
|
|
97
|
+
<label class="control-label">Signaling server</label>
|
|
98
|
+
<div class="controls">
|
|
99
|
+
<input type="text" class="input-xlarge"
|
|
100
|
+
data-bind="value: settings.settings.plugins.bitbang.signaling_server">
|
|
101
|
+
<span class="help-inline">Default <code>bitba.ng</code>. To use your own <a href="https://github.com/richlegrand/bitbang-server" target="_blank" rel="noopener">bitbang-server</a>, replace with the hostname (e.g. <code>signaling.example.com</code>).</span>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</form>
|
|
105
|
+
|
|
106
|
+
<div class="alert alert-info" style="margin-top: 16px;">
|
|
107
|
+
<i class="fas fa-info-circle"></i>
|
|
108
|
+
<strong>Restart OctoPrint</strong> after changing settings for them to take effect.
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<div id="wizard_plugin_bitbang">
|
|
2
|
+
<h3>Secure your BitBang remote access</h3>
|
|
3
|
+
<p>
|
|
4
|
+
BitBang gives this OctoPrint a private link you can use to reach it from
|
|
5
|
+
anywhere. Protect that link with a PIN so only you can connect — we
|
|
6
|
+
recommend at least {{ 4 }} characters.
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<form class="form-horizontal">
|
|
10
|
+
<div class="control-group">
|
|
11
|
+
<label class="control-label">PIN</label>
|
|
12
|
+
<div class="controls">
|
|
13
|
+
<input type="text" class="input-medium"
|
|
14
|
+
data-bind="value: settings.settings.plugins.bitbang.pin, valueUpdate: 'afterkeydown'"
|
|
15
|
+
placeholder="At least 4 characters">
|
|
16
|
+
<span class="help-inline" style="color: #b94a48;"
|
|
17
|
+
data-bind="visible: pinTooShort">
|
|
18
|
+
PIN must be at least 4 characters.
|
|
19
|
+
</span>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div class="control-group">
|
|
24
|
+
<div class="controls">
|
|
25
|
+
<label class="checkbox">
|
|
26
|
+
<input type="checkbox"
|
|
27
|
+
data-bind="checked: settings.settings.plugins.bitbang.allow_no_pin">
|
|
28
|
+
Advanced: run without a PIN
|
|
29
|
+
</label>
|
|
30
|
+
<span class="help-block muted">
|
|
31
|
+
Not recommended — anyone who has the link could control
|
|
32
|
+
this printer.
|
|
33
|
+
</span>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</form>
|
|
37
|
+
|
|
38
|
+
<p class="muted">
|
|
39
|
+
Until you set a PIN (or tick the box above), BitBang remote access stays
|
|
40
|
+
disabled.
|
|
41
|
+
</p>
|
|
42
|
+
|
|
43
|
+
<div class="alert alert-error" data-bind="visible: finishBlocked" style="display: none;">
|
|
44
|
+
Set a PIN of at least 4 characters, or tick “run without a
|
|
45
|
+
PIN” above, before finishing.
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "OctoPrint-BitBang"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.8"
|
|
8
8
|
description = "Remote OctoPrint access with live H.264 video via BitBang WebRTC"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -27,9 +27,6 @@ classifiers = [
|
|
|
27
27
|
]
|
|
28
28
|
dependencies = [
|
|
29
29
|
"bitbang>=0.1.53",
|
|
30
|
-
"aiohttp>=3.8.0",
|
|
31
|
-
"Pillow>=9.0.0",
|
|
32
|
-
"numpy>=1.20.0",
|
|
33
30
|
# aiortc/av come transitively via bitbang. aiortc<1.11 already caps av<14.
|
|
34
31
|
# 32-bit (armv7l) has NO PyAV wheels at any in-range version, so av builds
|
|
35
32
|
# from source against the system FFmpeg (Bookworm 5.1); av<12 lands it on the
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
from setuptools import setup
|
|
2
|
-
setup()
|
|
2
|
+
setup(license="MIT")
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
<a id="bitbang_navbar_link" href="javascript:void(0)" title="BitBang remote access" style="display:none" onclick="
|
|
2
|
-
fetch('/api/settings')
|
|
3
|
-
.then(function(r) { return r.json(); })
|
|
4
|
-
.then(function(data) {
|
|
5
|
-
var url = data.plugins && data.plugins.bitbang && data.plugins.bitbang.url;
|
|
6
|
-
if (url) {
|
|
7
|
-
prompt('BitBang URL (Ctrl+C to copy):', url);
|
|
8
|
-
} else {
|
|
9
|
-
alert('BitBang URL not available yet');
|
|
10
|
-
}
|
|
11
|
-
})
|
|
12
|
-
.catch(function() { alert('Could not load settings'); });
|
|
13
|
-
">
|
|
14
|
-
<i id="bitbang_navbar_icon" class="fas fa-link"></i> <span class="hidden-phone">BitBang</span>
|
|
15
|
-
</a>
|
|
16
|
-
<script>
|
|
17
|
-
(function () {
|
|
18
|
-
fetch('/api/settings')
|
|
19
|
-
.then(function (r) { return r.json(); })
|
|
20
|
-
.then(function (data) {
|
|
21
|
-
var enabled = data.plugins && data.plugins.bitbang && data.plugins.bitbang.enabled;
|
|
22
|
-
if (enabled) {
|
|
23
|
-
var link = document.getElementById('bitbang_navbar_link');
|
|
24
|
-
if (link) link.style.display = '';
|
|
25
|
-
}
|
|
26
|
-
})
|
|
27
|
-
.catch(function () {});
|
|
28
|
-
})();
|
|
29
|
-
</script>
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
<div id="settings_bitbang">
|
|
2
|
-
<h3>BitBang Remote Access</h3>
|
|
3
|
-
|
|
4
|
-
<form class="form-horizontal">
|
|
5
|
-
<div class="control-group">
|
|
6
|
-
<label class="control-label">Enabled</label>
|
|
7
|
-
<div class="controls">
|
|
8
|
-
<input type="checkbox" data-bind="checked: settings.plugins.bitbang.enabled">
|
|
9
|
-
</div>
|
|
10
|
-
</div>
|
|
11
|
-
|
|
12
|
-
<div class="control-group">
|
|
13
|
-
<label class="control-label">PIN Protection</label>
|
|
14
|
-
<div class="controls">
|
|
15
|
-
<input type="text" class="input-medium"
|
|
16
|
-
data-bind="value: settings.plugins.bitbang.pin"
|
|
17
|
-
placeholder="Empty to disable">
|
|
18
|
-
<span class="help-inline">Optional PIN to protect remote access</span>
|
|
19
|
-
</div>
|
|
20
|
-
</div>
|
|
21
|
-
|
|
22
|
-
<div class="control-group">
|
|
23
|
-
<label class="control-label">Remote URL</label>
|
|
24
|
-
<div class="controls">
|
|
25
|
-
<span data-bind="visible: settings.plugins.bitbang.url()">
|
|
26
|
-
<a data-bind="attr: {href: settings.plugins.bitbang.url()}, text: settings.plugins.bitbang.url()"
|
|
27
|
-
target="_blank" rel="noopener"></a>
|
|
28
|
-
</span>
|
|
29
|
-
<span data-bind="visible: !settings.plugins.bitbang.url()" class="muted">
|
|
30
|
-
URL will appear after BitBang connects
|
|
31
|
-
</span>
|
|
32
|
-
<span class="help-inline">Share this link for remote access</span>
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
|
|
36
|
-
<h3>Camera</h3>
|
|
37
|
-
|
|
38
|
-
<div class="control-group">
|
|
39
|
-
<label class="control-label">Camera</label>
|
|
40
|
-
<div class="controls">
|
|
41
|
-
<select id="bitbang_camera_select" class="input-xlarge"
|
|
42
|
-
data-bind="value: settings.plugins.bitbang.camera_device, valueAllowUnset: true">
|
|
43
|
-
<option value="">Auto-detect</option>
|
|
44
|
-
</select>
|
|
45
|
-
<button class="btn btn-mini" id="bitbang_refresh_cameras" type="button">
|
|
46
|
-
<i class="fas fa-sync"></i>
|
|
47
|
-
</button>
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
50
|
-
|
|
51
|
-
<div class="control-group">
|
|
52
|
-
<label class="control-label">Resolution</label>
|
|
53
|
-
<div class="controls">
|
|
54
|
-
<select id="bitbang_resolution_select" class="input-medium"
|
|
55
|
-
data-bind="value: settings.plugins.bitbang.camera_resolution, valueAllowUnset: true">
|
|
56
|
-
<option value="640x480">640x480</option>
|
|
57
|
-
</select>
|
|
58
|
-
</div>
|
|
59
|
-
</div>
|
|
60
|
-
|
|
61
|
-
<div class="control-group">
|
|
62
|
-
<label class="control-label">Image orientation</label>
|
|
63
|
-
<div class="controls">
|
|
64
|
-
<label class="checkbox inline">
|
|
65
|
-
<input type="checkbox" data-bind="checked: settings.plugins.bitbang.flip_horizontal">
|
|
66
|
-
Flip horizontal
|
|
67
|
-
</label>
|
|
68
|
-
<label class="checkbox inline">
|
|
69
|
-
<input type="checkbox" data-bind="checked: settings.plugins.bitbang.flip_vertical">
|
|
70
|
-
Flip vertical
|
|
71
|
-
</label>
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
|
|
75
|
-
<h3>Advanced</h3>
|
|
76
|
-
|
|
77
|
-
<div class="control-group">
|
|
78
|
-
<label class="control-label">Signaling server</label>
|
|
79
|
-
<div class="controls">
|
|
80
|
-
<input type="text" class="input-xlarge"
|
|
81
|
-
data-bind="value: settings.plugins.bitbang.signaling_server">
|
|
82
|
-
<span class="help-inline">Default <code>bitba.ng</code>. To use your own <a href="https://github.com/richlegrand/bitbang-server" target="_blank" rel="noopener">bitbang-server</a>, replace with the hostname (e.g. <code>signaling.example.com</code>).</span>
|
|
83
|
-
</div>
|
|
84
|
-
</div>
|
|
85
|
-
</form>
|
|
86
|
-
|
|
87
|
-
<div class="alert alert-info" style="margin-top: 16px;">
|
|
88
|
-
<i class="fas fa-info-circle"></i>
|
|
89
|
-
<strong>Restart OctoPrint</strong> after changing settings for them to take effect.
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
|
|
93
|
-
<script>
|
|
94
|
-
(function () {
|
|
95
|
-
var cameraSelect = document.getElementById("bitbang_camera_select");
|
|
96
|
-
var resolutionSelect = document.getElementById("bitbang_resolution_select");
|
|
97
|
-
var refreshBtn = document.getElementById("bitbang_refresh_cameras");
|
|
98
|
-
if (!cameraSelect || !resolutionSelect) return;
|
|
99
|
-
|
|
100
|
-
// Set a <select> and notify Knockout's value binding — it doesn't see
|
|
101
|
-
// options added via raw DOM, so without the dispatched 'change' the
|
|
102
|
-
// observable (and thus the saved setting) won't follow the selection.
|
|
103
|
-
// Suppress the camera change handler during programmatic sets so we don't
|
|
104
|
-
// reload resolutions twice.
|
|
105
|
-
function setSelect(sel, value) {
|
|
106
|
-
var ok = value && Array.prototype.some.call(
|
|
107
|
-
sel.options, function (o) { return o.value === value; });
|
|
108
|
-
sel._suppress = true;
|
|
109
|
-
if (ok) sel.value = value;
|
|
110
|
-
sel.dispatchEvent(new Event("change"));
|
|
111
|
-
sel._suppress = false;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function loadResolutions(device, wanted) {
|
|
115
|
-
return fetch("/plugin/bitbang/resolutions?device=" + encodeURIComponent(device || ""))
|
|
116
|
-
.then(function (r) { return r.json(); })
|
|
117
|
-
.then(function (resolutions) {
|
|
118
|
-
resolutionSelect.innerHTML = "";
|
|
119
|
-
resolutions.forEach(function (res) {
|
|
120
|
-
var opt = document.createElement("option");
|
|
121
|
-
opt.value = res;
|
|
122
|
-
opt.textContent = res;
|
|
123
|
-
resolutionSelect.appendChild(opt);
|
|
124
|
-
});
|
|
125
|
-
setSelect(resolutionSelect,
|
|
126
|
-
resolutions.indexOf(wanted) >= 0 ? wanted : resolutions[0]);
|
|
127
|
-
})
|
|
128
|
-
.catch(function () {});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function loadCameras(wantedCamera, wantedRes) {
|
|
132
|
-
return fetch("/plugin/bitbang/cameras")
|
|
133
|
-
.then(function (r) { return r.json(); })
|
|
134
|
-
.then(function (cameras) {
|
|
135
|
-
while (cameraSelect.options.length > 1) cameraSelect.remove(1);
|
|
136
|
-
cameras.forEach(function (cam) {
|
|
137
|
-
var opt = document.createElement("option");
|
|
138
|
-
opt.value = cam.device;
|
|
139
|
-
opt.textContent = cam.name + " (" + cam.device + ")";
|
|
140
|
-
cameraSelect.appendChild(opt);
|
|
141
|
-
});
|
|
142
|
-
setSelect(cameraSelect, wantedCamera);
|
|
143
|
-
return loadResolutions(cameraSelect.value, wantedRes);
|
|
144
|
-
})
|
|
145
|
-
.catch(function () {});
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
cameraSelect.addEventListener("change", function () {
|
|
149
|
-
if (cameraSelect._suppress) return; // programmatic set — skip reload
|
|
150
|
-
loadResolutions(cameraSelect.value, resolutionSelect.value);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
if (refreshBtn) {
|
|
154
|
-
refreshBtn.addEventListener("click", function () {
|
|
155
|
-
loadCameras(cameraSelect.value, resolutionSelect.value);
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Populate from the SAVED config — not the (Knockout-clobbered) DOM value —
|
|
160
|
-
// so the dropdowns reflect what's persisted and a Save round-trips cleanly.
|
|
161
|
-
fetch("/api/settings")
|
|
162
|
-
.then(function (r) { return r.json(); })
|
|
163
|
-
.then(function (s) {
|
|
164
|
-
var bb = (s.plugins && s.plugins.bitbang) || {};
|
|
165
|
-
loadCameras(bb.camera_device || "", bb.camera_resolution || "");
|
|
166
|
-
})
|
|
167
|
-
.catch(function () { loadCameras("", ""); });
|
|
168
|
-
})();
|
|
169
|
-
</script>
|
|
File without changes
|
|
File without changes
|
{octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/OctoPrint_BitBang.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/OctoPrint_BitBang.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{octoprint_bitbang-0.2.6 → octoprint_bitbang-0.2.8}/OctoPrint_BitBang.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|