Improved gamepad support
- Gamepads are now recognized by their "vendor" and "product" codes only. - It was previously including other information like name, version etc - Added a new item on the Settings page for "default gamepad type" - If a gamepad is not "recognized", it is treated as the default type
This commit is contained in:
@@ -29,6 +29,8 @@ class Config(object):
|
|||||||
self.log.exception(
|
self.log.exception(
|
||||||
'Internal error: Failed to load config template')
|
'Internal error: Failed to load config template')
|
||||||
|
|
||||||
|
self.reload()
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
path = self.ctrl.get_path('config.json')
|
path = self.ctrl.get_path('config.json')
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class Ctrl(object):
|
|||||||
self.mach = bbctrl.Mach(self, self.avr)
|
self.mach = bbctrl.Mach(self, self.avr)
|
||||||
self.preplanner = bbctrl.Preplanner(self)
|
self.preplanner = bbctrl.Preplanner(self)
|
||||||
if not args.demo:
|
if not args.demo:
|
||||||
self.jog = bbctrl.Jog(self)
|
self.gamepadSupport = bbctrl.GamepadSupport(self)
|
||||||
self.pwr = bbctrl.Pwr(self)
|
self.pwr = bbctrl.Pwr(self)
|
||||||
|
|
||||||
self.mach.connect()
|
self.mach.connect()
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ from evdev.ecodes import EV, EV_ABS, EV_KEY
|
|||||||
import errno
|
import errno
|
||||||
import evdev
|
import evdev
|
||||||
import functools
|
import functools
|
||||||
import hashlib
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import pyudev
|
import pyudev
|
||||||
@@ -14,16 +13,16 @@ import typing
|
|||||||
|
|
||||||
userGamepadConfigs = {}
|
userGamepadConfigs = {}
|
||||||
|
|
||||||
gamepadConfigs = {
|
factoryGamepadConfigs = {
|
||||||
"default": {
|
"default": {
|
||||||
"sign-x": 1,
|
"sign-x": 1,
|
||||||
"sign-y": -1,
|
"sign-y": -1,
|
||||||
"sign-z": -1,
|
"sign-z": -1,
|
||||||
"deadband": 0.15,
|
"deadband": 0.15,
|
||||||
"debug": False,
|
"debug": False,
|
||||||
|
"type": "XBOX",
|
||||||
},
|
},
|
||||||
"9E2B3A63": {
|
"XBOX": {
|
||||||
"description": "Logitech 710, X mode",
|
|
||||||
"EV_KEY:308": "speed-4",
|
"EV_KEY:308": "speed-4",
|
||||||
"EV_KEY:305": "speed-3",
|
"EV_KEY:305": "speed-3",
|
||||||
"EV_KEY:304": "speed-2",
|
"EV_KEY:304": "speed-2",
|
||||||
@@ -38,8 +37,7 @@ gamepadConfigs = {
|
|||||||
"EV_ABS:2": "lock-y",
|
"EV_ABS:2": "lock-y",
|
||||||
"EV_ABS:5": "lock-x",
|
"EV_ABS:5": "lock-x",
|
||||||
},
|
},
|
||||||
"B98EF4EC": {
|
"PLAYSTATION": {
|
||||||
"description": "Logitech 710, D mode",
|
|
||||||
"EV_KEY:307": "speed-4",
|
"EV_KEY:307": "speed-4",
|
||||||
"EV_KEY:306": "speed-3",
|
"EV_KEY:306": "speed-3",
|
||||||
"EV_KEY:305": "speed-2",
|
"EV_KEY:305": "speed-2",
|
||||||
@@ -54,24 +52,7 @@ gamepadConfigs = {
|
|||||||
"EV_KEY:310": "lock-y",
|
"EV_KEY:310": "lock-y",
|
||||||
"EV_KEY:311": "lock-x",
|
"EV_KEY:311": "lock-x",
|
||||||
},
|
},
|
||||||
"268256FD": {
|
"SMX-LEFT": {
|
||||||
"description": "EasySMX ESM-9013, top lights mode",
|
|
||||||
"EV_KEY:308": "speed-4",
|
|
||||||
"EV_KEY:305": "speed-3",
|
|
||||||
"EV_KEY:304": "speed-2",
|
|
||||||
"EV_KEY:307": "speed-1",
|
|
||||||
"EV_ABS:0": "axis-x",
|
|
||||||
"EV_ABS:16": "axis-x",
|
|
||||||
"EV_ABS:1": "axis-y",
|
|
||||||
"EV_ABS:17": "axis-y",
|
|
||||||
"EV_ABS:4": "axis-z",
|
|
||||||
"EV_KEY:310": "lock-y",
|
|
||||||
"EV_KEY:311": "lock-x",
|
|
||||||
"EV_ABS:2": "lock-y",
|
|
||||||
"EV_ABS:5": "lock-x",
|
|
||||||
},
|
|
||||||
"23CEC0CB": {
|
|
||||||
"description": "EasySMX ESM-9013, left lights mode",
|
|
||||||
"EV_KEY:304": "speed-4",
|
"EV_KEY:304": "speed-4",
|
||||||
"EV_KEY:305": "speed-3",
|
"EV_KEY:305": "speed-3",
|
||||||
"EV_KEY:306": "speed-2",
|
"EV_KEY:306": "speed-2",
|
||||||
@@ -86,8 +67,7 @@ gamepadConfigs = {
|
|||||||
"EV_KEY:310": "lock-y",
|
"EV_KEY:310": "lock-y",
|
||||||
"EV_KEY:311": "lock-x",
|
"EV_KEY:311": "lock-x",
|
||||||
},
|
},
|
||||||
"370DCB72": {
|
"SMX-BOTTOM": {
|
||||||
"description": "EasySMX ESM-9013, bottom lights mode",
|
|
||||||
"EV_KEY:308": "speed-4",
|
"EV_KEY:308": "speed-4",
|
||||||
"EV_KEY:305": "speed-3",
|
"EV_KEY:305": "speed-3",
|
||||||
"EV_KEY:304": "speed-2",
|
"EV_KEY:304": "speed-2",
|
||||||
@@ -102,117 +82,77 @@ gamepadConfigs = {
|
|||||||
"EV_KEY:312": "lock-y",
|
"EV_KEY:312": "lock-y",
|
||||||
"EV_KEY:313": "lock-x",
|
"EV_KEY:313": "lock-x",
|
||||||
},
|
},
|
||||||
"0BD0841F": {
|
"045E:02A1": {
|
||||||
"description": "Sony Playstation 4 Dual-Shock Controller",
|
|
||||||
"EV_KEY:307": "speed-4",
|
|
||||||
"EV_KEY:306": "speed-3",
|
|
||||||
"EV_KEY:305": "speed-2",
|
|
||||||
"EV_KEY:304": "speed-1",
|
|
||||||
"EV_ABS:0": "axis-x",
|
|
||||||
"EV_ABS:16": "axis-x",
|
|
||||||
"EV_ABS:1": "axis-y",
|
|
||||||
"EV_ABS:17": "axis-y",
|
|
||||||
"EV_ABS:5": "axis-z",
|
|
||||||
"EV_KEY:308": "lock-y",
|
|
||||||
"EV_KEY:309": "lock-x",
|
|
||||||
"EV_KEY:310": "lock-y",
|
|
||||||
"EV_KEY:311": "lock-x",
|
|
||||||
},
|
|
||||||
"D09463DD": {
|
|
||||||
"description": "Sony Playstation 5 Controller",
|
|
||||||
"EV_KEY:307": "speed-4",
|
|
||||||
"EV_KEY:306": "speed-3",
|
|
||||||
"EV_KEY:305": "speed-2",
|
|
||||||
"EV_KEY:304": "speed-1",
|
|
||||||
"EV_ABS:0": "axis-x",
|
|
||||||
"EV_ABS:16": "axis-x",
|
|
||||||
"EV_ABS:1": "axis-y",
|
|
||||||
"EV_ABS:17": "axis-y",
|
|
||||||
"EV_ABS:5": "axis-z",
|
|
||||||
"EV_KEY:308": "lock-y",
|
|
||||||
"EV_KEY:309": "lock-x",
|
|
||||||
"EV_KEY:310": "lock-y",
|
|
||||||
"EV_KEY:311": "lock-x",
|
|
||||||
},
|
|
||||||
"06656EBD": {
|
|
||||||
"description": "XBox One Controller",
|
|
||||||
"EV_KEY:308": "speed-4",
|
|
||||||
"EV_KEY:305": "speed-3",
|
|
||||||
"EV_KEY:304": "speed-2",
|
|
||||||
"EV_KEY:307": "speed-1",
|
|
||||||
"EV_ABS:0": "axis-x",
|
|
||||||
"EV_ABS:16": "axis-x",
|
|
||||||
"EV_ABS:1": "axis-y",
|
|
||||||
"EV_ABS:17": "axis-y",
|
|
||||||
"EV_ABS:4": "axis-z",
|
|
||||||
"EV_KEY:310": "lock-y",
|
|
||||||
"EV_KEY:311": "lock-x",
|
|
||||||
"EV_ABS:2": "lock-y",
|
|
||||||
"EV_ABS:5": "lock-x",
|
|
||||||
},
|
|
||||||
"BFF99E89": {
|
|
||||||
"description": "XBox 360 Controller",
|
"description": "XBox 360 Controller",
|
||||||
"EV_KEY:308": "speed-4",
|
"type": "XBOX"
|
||||||
"EV_KEY:305": "speed-3",
|
|
||||||
"EV_KEY:304": "speed-2",
|
|
||||||
"EV_KEY:307": "speed-1",
|
|
||||||
"EV_ABS:0": "axis-x",
|
|
||||||
"EV_ABS:16": "axis-x",
|
|
||||||
"EV_ABS:1": "axis-y",
|
|
||||||
"EV_ABS:17": "axis-y",
|
|
||||||
"EV_ABS:4": "axis-z",
|
|
||||||
"EV_KEY:310": "lock-y",
|
|
||||||
"EV_KEY:311": "lock-x",
|
|
||||||
"EV_ABS:2": "lock-y",
|
|
||||||
"EV_ABS:5": "lock-x",
|
|
||||||
},
|
},
|
||||||
"4E0C75F7": {
|
"045E:028E": {
|
||||||
"description": "EasySMX ESM-9100 XBox Controller, top lights mode",
|
"description": "Xbox360 Controller",
|
||||||
"EV_KEY:308": "speed-4",
|
"type": "XBOX",
|
||||||
"EV_KEY:305": "speed-3",
|
|
||||||
"EV_KEY:304": "speed-2",
|
|
||||||
"EV_KEY:307": "speed-1",
|
|
||||||
"EV_ABS:0": "axis-x",
|
|
||||||
"EV_ABS:16": "axis-x",
|
|
||||||
"EV_ABS:1": "axis-y",
|
|
||||||
"EV_ABS:17": "axis-y",
|
|
||||||
"EV_ABS:4": "axis-z",
|
|
||||||
"EV_KEY:310": "lock-y",
|
|
||||||
"EV_KEY:311": "lock-x",
|
|
||||||
"EV_ABS:17": "axis-y",
|
|
||||||
"EV_ABS:4": "axis-z"
|
|
||||||
},
|
},
|
||||||
"E310BCC0": {
|
"045E:028F": {
|
||||||
"description": "EasySMX ESM-9100, left lights mode",
|
"description": "Xbox360 Wireless Controller",
|
||||||
"EV_KEY:304": "speed-4",
|
"type": "XBOX",
|
||||||
"EV_KEY:305": "speed-3",
|
|
||||||
"EV_KEY:306": "speed-2",
|
|
||||||
"EV_KEY:307": "speed-1",
|
|
||||||
"EV_ABS:0": "axis-x",
|
|
||||||
"EV_ABS:16": "axis-x",
|
|
||||||
"EV_ABS:1": "axis-y",
|
|
||||||
"EV_ABS:17": "axis-y",
|
|
||||||
"EV_ABS:5": "axis-z",
|
|
||||||
"EV_KEY:308": "lock-y",
|
|
||||||
"EV_KEY:309": "lock-x",
|
|
||||||
"EV_KEY:310": "lock-y",
|
|
||||||
"EV_KEY:311": "lock-x",
|
|
||||||
},
|
},
|
||||||
"96D2AC48": {
|
"045E:0B12": {
|
||||||
|
"description": "XBox One Controller",
|
||||||
|
"type": "XBOX"
|
||||||
|
},
|
||||||
|
"046D:C216": {
|
||||||
|
"description": "Logitech F310, D mode",
|
||||||
|
"type": "PLAYSTATION"
|
||||||
|
},
|
||||||
|
"046D:C216": {
|
||||||
|
"description": "Logitech F510, D mode",
|
||||||
|
"type": "PLAYSTATION"
|
||||||
|
},
|
||||||
|
"046D:C219": {
|
||||||
|
"description": "Logitech F710, D mode",
|
||||||
|
"type": "PLAYSTATION"
|
||||||
|
},
|
||||||
|
"046D:C21D": {
|
||||||
|
"description": "Logitech F310, X mode",
|
||||||
|
"type": "XBOX"
|
||||||
|
},
|
||||||
|
"046D:C21E": {
|
||||||
|
"description": "Logitech F510, X mode",
|
||||||
|
"type": "XBOX"
|
||||||
|
},
|
||||||
|
"046D:C21F": {
|
||||||
|
"description": "Logitech 710, X mode",
|
||||||
|
"type": "XBOX"
|
||||||
|
},
|
||||||
|
"054C:05C4": {
|
||||||
|
"description": "Sony Playstation 4 DualShock Controller",
|
||||||
|
"type": "PLAYSTATION"
|
||||||
|
},
|
||||||
|
"054C:09CC": {
|
||||||
|
"description": "Sony Playstation 4 DualShock Controller",
|
||||||
|
"type": "PLAYSTATION"
|
||||||
|
},
|
||||||
|
"054C:0BA0": {
|
||||||
|
"description": "Sony Playstation 4 DualShock Controller",
|
||||||
|
"type": "PLAYSTATION"
|
||||||
|
},
|
||||||
|
"054C:0CE6": {
|
||||||
|
"description": "Sony Playstation 5 DualSense Controller",
|
||||||
|
"type": "PLAYSTATION"
|
||||||
|
},
|
||||||
|
"11C0:5500": {
|
||||||
|
"description": "EasySMX ESM-9013, bottom lights mode",
|
||||||
|
"type": "SMX-BOTTOM"
|
||||||
|
},
|
||||||
|
"11C1:9101": {
|
||||||
|
"description": "EasySMX ESM-9013, left lights mode",
|
||||||
|
"type": "SMX-LEFT"
|
||||||
|
},
|
||||||
|
"20BC:5500": {
|
||||||
"description": "EasySMX ESM-9100, bottom lights mode",
|
"description": "EasySMX ESM-9100, bottom lights mode",
|
||||||
"EV_KEY:308": "speed-4",
|
"type": "SMX-BOTTOM"
|
||||||
"EV_KEY:305": "speed-3",
|
},
|
||||||
"EV_KEY:304": "speed-2",
|
"20BC:9100": {
|
||||||
"EV_KEY:307": "speed-1",
|
"description": "EasySMX ESM-9100, left lights mode",
|
||||||
"EV_ABS:0": "axis-x",
|
"type": "SMX-LEFT"
|
||||||
"EV_ABS:16": "axis-x",
|
|
||||||
"EV_ABS:1": "axis-y",
|
|
||||||
"EV_ABS:17": "axis-y",
|
|
||||||
"EV_ABS:5": "axis-z",
|
|
||||||
"EV_KEY:310": "lock-y",
|
|
||||||
"EV_KEY:311": "lock-x",
|
|
||||||
"EV_KEY:312": "lock-y",
|
|
||||||
"EV_KEY:313": "lock-x",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,6 +194,25 @@ def processCapabilities(capabilities):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def loadConfig(id):
|
||||||
|
config = {
|
||||||
|
**factoryGamepadConfigs.get("default"),
|
||||||
|
**userGamepadConfigs.get("default", {}),
|
||||||
|
**factoryGamepadConfigs.get(id, {}),
|
||||||
|
**userGamepadConfigs.get(id, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
while "type" in config:
|
||||||
|
type = config.pop("type")
|
||||||
|
config = {
|
||||||
|
**factoryGamepadConfigs.get(type, {}),
|
||||||
|
**userGamepadConfigs.get(type, {}),
|
||||||
|
**config,
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
# A forward declaration, so Command can reference it
|
# A forward declaration, so Command can reference it
|
||||||
class Gamepad(object):
|
class Gamepad(object):
|
||||||
pass
|
pass
|
||||||
@@ -298,15 +257,11 @@ class Gamepad(object):
|
|||||||
for key in _udev.properties}
|
for key in _udev.properties}
|
||||||
}
|
}
|
||||||
|
|
||||||
json = sorted_json(self._details["evdev"])
|
self.id = "{:04X}:{:04X}".format(_evdev.info.vendor,
|
||||||
self.hash = hashlib.sha256(json.encode()).hexdigest()[-8:].upper()
|
_evdev.info.product)
|
||||||
|
self.config = loadConfig(self.id)
|
||||||
|
|
||||||
self.config = {
|
self.log("Configuration Settings: {}".format(self.config))
|
||||||
**gamepadConfigs.get("default"),
|
|
||||||
**userGamepadConfigs.get("default", {}),
|
|
||||||
**gamepadConfigs.get(self.hash, {}),
|
|
||||||
**userGamepadConfigs.get(self.hash, {})
|
|
||||||
}
|
|
||||||
|
|
||||||
def read(self):
|
def read(self):
|
||||||
return self._evdev.read()
|
return self._evdev.read()
|
||||||
@@ -374,7 +329,7 @@ class Gamepad(object):
|
|||||||
return round(value, 3)
|
return round(value, 3)
|
||||||
|
|
||||||
def log(self, msg):
|
def log(self, msg):
|
||||||
self._log.info("{}: {}".format(self.hash, msg))
|
self._log.info("{}: {}".format(self.id, msg))
|
||||||
|
|
||||||
def logOnce(self, msg):
|
def logOnce(self, msg):
|
||||||
if self.config.get("debug") or msg not in self._logOnceRecord:
|
if self.config.get("debug") or msg not in self._logOnceRecord:
|
||||||
@@ -390,11 +345,11 @@ class Gamepad(object):
|
|||||||
"devicePath": self.devicePath,
|
"devicePath": self.devicePath,
|
||||||
"bustype": self._evdev.info.bustype,
|
"bustype": self._evdev.info.bustype,
|
||||||
"details": self._details,
|
"details": self._details,
|
||||||
"hash": self.hash
|
"id": self.id
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class Jog(object):
|
class GamepadSupport(object):
|
||||||
gamepads = {} # type: dict[typing.Union[int, str], Gamepad]
|
gamepads = {} # type: dict[typing.Union[int, str], Gamepad]
|
||||||
lock = {"x": False, "y": False}
|
lock = {"x": False, "y": False}
|
||||||
axes = {"x": 0, "y": 0, "z": 0}
|
axes = {"x": 0, "y": 0, "z": 0}
|
||||||
@@ -419,8 +374,9 @@ class Jog(object):
|
|||||||
global userGamepadConfigs
|
global userGamepadConfigs
|
||||||
userGamepadConfigs = json.load(f)
|
userGamepadConfigs = json.load(f)
|
||||||
except:
|
except:
|
||||||
|
self.log.info("Failed to read 'gamepads.json':")
|
||||||
self.log.info(traceback.format_exc())
|
self.log.info(traceback.format_exc())
|
||||||
self.log.info("Failed to read 'gamepads.json'")
|
userGamepadConfigs = {}
|
||||||
|
|
||||||
def _startMonitoring(self):
|
def _startMonitoring(self):
|
||||||
self.udev_context = pyudev.Context()
|
self.udev_context = pyudev.Context()
|
||||||
@@ -465,6 +421,8 @@ class Jog(object):
|
|||||||
self._listen("/dev/input/{}".format(match.group()))
|
self._listen("/dev/input/{}".format(match.group()))
|
||||||
|
|
||||||
def _listen(self, devicePath: str):
|
def _listen(self, devicePath: str):
|
||||||
|
self._refreshDefaultGamepadType()
|
||||||
|
|
||||||
gamepad = Gamepad(
|
gamepad = Gamepad(
|
||||||
self.log, evdev.InputDevice(devicePath),
|
self.log, evdev.InputDevice(devicePath),
|
||||||
pyudev.Devices.from_device_file(self.udev_context, devicePath))
|
pyudev.Devices.from_device_file(self.udev_context, devicePath))
|
||||||
@@ -558,6 +516,18 @@ class Jog(object):
|
|||||||
def _processDisabled(self, command: Command):
|
def _processDisabled(self, command: Command):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _refreshDefaultGamepadType(self):
|
||||||
|
defaultGamepadType = self.ctrl.config.get("gamepad-default-type")
|
||||||
|
|
||||||
|
if defaultGamepadType is not None:
|
||||||
|
default = factoryGamepadConfigs.get("default", {})
|
||||||
|
default["type"] = defaultGamepadType
|
||||||
|
factoryGamepadConfigs["default"] = default
|
||||||
|
|
||||||
|
default = userGamepadConfigs.get("default")
|
||||||
|
if default is not None:
|
||||||
|
default.pop("type", None)
|
||||||
|
|
||||||
def _updateJogging(self):
|
def _updateJogging(self):
|
||||||
try:
|
try:
|
||||||
if not self.changed:
|
if not self.changed:
|
||||||
@@ -14,7 +14,7 @@ from bbctrl.FileHandler import FileHandler
|
|||||||
from bbctrl.Config import Config
|
from bbctrl.Config import Config
|
||||||
from bbctrl.Mach import Mach
|
from bbctrl.Mach import Mach
|
||||||
from bbctrl.Web import Web
|
from bbctrl.Web import Web
|
||||||
from bbctrl.Jog import Jog
|
from bbctrl.GamepadSupport import GamepadSupport
|
||||||
from bbctrl.Ctrl import Ctrl
|
from bbctrl.Ctrl import Ctrl
|
||||||
from bbctrl.Pwr import Pwr
|
from bbctrl.Pwr import Pwr
|
||||||
from bbctrl.I2C import I2C
|
from bbctrl.I2C import I2C
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,11 @@
|
|||||||
|
|
||||||
type Template = {
|
type Template = {
|
||||||
type?: string;
|
type?: string;
|
||||||
values?: (string | number)[];
|
values?: (
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| { title: string; value: string | number }
|
||||||
|
)[];
|
||||||
unit?: "string";
|
unit?: "string";
|
||||||
iunit?: "string";
|
iunit?: "string";
|
||||||
min?: number;
|
min?: number;
|
||||||
@@ -97,8 +101,11 @@
|
|||||||
{#if template.values}
|
{#if template.values}
|
||||||
<select {name} bind:value on:change={onChange}>
|
<select {name} bind:value on:change={onChange}>
|
||||||
{#each template.values as opt}
|
{#each template.values as opt}
|
||||||
<option value={opt} disabled={opt === "-----"}>
|
<option
|
||||||
{opt}
|
value={opt?.value ?? opt}
|
||||||
|
disabled={opt === "-----"}
|
||||||
|
>
|
||||||
|
{opt?.title ?? opt}
|
||||||
</option>
|
</option>
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -44,6 +44,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
<h2>Gamepads / Joypads</h2>
|
||||||
|
<fieldset>
|
||||||
|
<ConfigTemplatedInput key={`settings.gamepad-default-type`} />
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If you have a gamepad that is not officially supported, and doesn't seem
|
||||||
|
to be working right, try changing <tt>gamepad-default-type</tt> to one of
|
||||||
|
the other types.
|
||||||
|
</p>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<h2>Probe Dimensions</h2>
|
<h2>Probe Dimensions</h2>
|
||||||
{#each Object.keys(configTemplate.probe) as key}
|
{#each Object.keys(configTemplate.probe) as key}
|
||||||
|
|||||||
Reference in New Issue
Block a user