feat: import presets into adapter prefs
This commit is contained in:
parent
6c3c96a70b
commit
16e16a6180
@ -5,6 +5,7 @@
|
||||
'require mqtt-bridge/nav as Nav';
|
||||
'require ui';
|
||||
'require form';
|
||||
'require dom';
|
||||
|
||||
var lang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
@ -19,14 +20,17 @@ return view.extend({
|
||||
render: function(payload) {
|
||||
var settings = (payload && payload.settings) || {};
|
||||
var adapters = (payload && payload.adapters) || [];
|
||||
this.currentAdapters = adapters;
|
||||
var profiles = (payload && payload.profiles) || [];
|
||||
this.currentAdapters = this.cloneAdapters(adapters || []);
|
||||
this.liveProfiles = profiles || [];
|
||||
|
||||
var container = E('div', { 'class': 'mqtt-bridge-dashboard' }, [
|
||||
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox-theme/secubox-theme.css') }),
|
||||
E('link', { 'rel': 'stylesheet', 'href': L.resource('mqtt-bridge/common.css') }),
|
||||
Nav.renderTabs('settings'),
|
||||
this.renderSettingsCard(settings || {}),
|
||||
this.renderAdapterCard(adapters || [])
|
||||
this.renderAdapterCard(this.currentAdapters),
|
||||
this.renderPresetCard(this.liveProfiles)
|
||||
]);
|
||||
return container;
|
||||
},
|
||||
@ -51,16 +55,28 @@ return view.extend({
|
||||
},
|
||||
|
||||
renderAdapterCard: function(adapters) {
|
||||
var items = adapters && adapters.length ? adapters.map(this.renderAdapterRow.bind(this)) :
|
||||
[E('p', { 'style': 'color:var(--mb-muted);' }, _('No adapters configured yet. UCI sections named `config adapter` will appear here.'))];
|
||||
var grid = E('div', { 'class': 'mb-adapter-grid' }, this.renderAdapterRows(adapters));
|
||||
this.adapterGrid = grid;
|
||||
|
||||
return E('div', { 'class': 'mb-card' }, [
|
||||
E('div', { 'class': 'mb-card-header' }, [
|
||||
E('div', { 'class': 'mb-card-title' }, [E('span', {}, '🧩'), _('Adapter preferences')])
|
||||
]),
|
||||
E('div', { 'class': 'mb-adapter-grid' }, items)
|
||||
grid
|
||||
]);
|
||||
},
|
||||
|
||||
renderAdapterRows: function(adapters) {
|
||||
var self = this;
|
||||
if (!adapters || !adapters.length) {
|
||||
return [E('p', { 'style': 'color:var(--mb-muted);' },
|
||||
_('No adapters configured yet. Existing `config adapter` entries will appear once the monitor updates.'))];
|
||||
}
|
||||
return adapters.map(function(adapter) {
|
||||
return self.renderAdapterRow(adapter);
|
||||
});
|
||||
},
|
||||
|
||||
renderAdapterRow: function(adapter) {
|
||||
var id = adapter.id || adapter.section || adapter.preset || adapter.vendor + ':' + adapter.product;
|
||||
var inputId = this.makeAdapterInputId(id, 'label');
|
||||
@ -90,6 +106,48 @@ return view.extend({
|
||||
]);
|
||||
},
|
||||
|
||||
renderPresetCard: function(profiles) {
|
||||
var self = this;
|
||||
var rows = (profiles && profiles.length) ? profiles.map(function(profile) {
|
||||
return self.renderPresetRow(profile);
|
||||
}) : [
|
||||
E('p', { 'style': 'color:var(--mb-muted);' },
|
||||
_('When USB presets are detected (VID/PID), they will be displayed here so you can import them as adapters.'))
|
||||
];
|
||||
|
||||
return E('div', { 'class': 'mb-card' }, [
|
||||
E('div', { 'class': 'mb-card-header' }, [
|
||||
E('div', { 'class': 'mb-card-title' }, [E('span', {}, '🛰️'), _('Detected presets')])
|
||||
]),
|
||||
E('div', { 'class': 'mb-profile-grid' }, rows)
|
||||
]);
|
||||
},
|
||||
|
||||
renderPresetRow: function(profile) {
|
||||
var self = this;
|
||||
var meta = [
|
||||
(profile.vendor && profile.product) ? _('VID:PID ') + profile.vendor + ':' + profile.product : null,
|
||||
profile.bus ? _('Bus ') + profile.bus : null,
|
||||
profile.device ? _('Device ') + profile.device : null,
|
||||
profile.port ? _('Port ') + profile.port : null
|
||||
].filter(Boolean);
|
||||
return E('div', { 'class': 'mb-profile-card' }, [
|
||||
E('div', { 'class': 'mb-profile-header' }, [
|
||||
E('div', {}, [
|
||||
E('strong', {}, profile.label || profile.title || _('USB profile')),
|
||||
E('div', { 'class': 'mb-profile-meta' }, meta.map(function(entry) {
|
||||
return E('span', {}, entry);
|
||||
}))
|
||||
]),
|
||||
E('button', {
|
||||
'class': 'mb-btn mb-btn-secondary',
|
||||
'click': function() { self.importProfile(profile); }
|
||||
}, ['➕ ', _('Import preset')])
|
||||
]),
|
||||
profile.notes ? E('p', { 'class': 'mb-profile-notes' }, profile.notes) : null
|
||||
]);
|
||||
},
|
||||
|
||||
input: function(id, label, value, type) {
|
||||
return E('div', { 'class': 'mb-input-group' }, [
|
||||
E('label', { 'class': 'mb-stat-label', 'for': id }, label),
|
||||
@ -106,6 +164,24 @@ return view.extend({
|
||||
return 'adapter-' + (id || 'x').replace(/[^a-z0-9_-]/ig, '_') + '-' + field;
|
||||
},
|
||||
|
||||
normalizeAdapterId: function(id) {
|
||||
return (id || '').replace(/[^a-z0-9_-]/ig, '_') || 'adapter_' + Math.random().toString(36).slice(2, 7);
|
||||
},
|
||||
|
||||
cloneAdapters: function(list) {
|
||||
var cloned = [];
|
||||
(list || []).forEach(function(item) {
|
||||
var copy = {};
|
||||
if (item) {
|
||||
Object.keys(item).forEach(function(key) {
|
||||
copy[key] = item[key];
|
||||
});
|
||||
}
|
||||
cloned.push(copy);
|
||||
});
|
||||
return cloned;
|
||||
},
|
||||
|
||||
collectSettings: function() {
|
||||
return {
|
||||
host: document.getElementById('broker-host').value,
|
||||
@ -139,6 +215,42 @@ return view.extend({
|
||||
return adapters;
|
||||
},
|
||||
|
||||
refreshAdapterGrid: function() {
|
||||
if (!this.adapterGrid)
|
||||
return;
|
||||
dom.content(this.adapterGrid, this.renderAdapterRows(this.currentAdapters));
|
||||
},
|
||||
|
||||
importProfile: function(profile) {
|
||||
if (!profile)
|
||||
return;
|
||||
var idSource = profile.id || profile.preset || (profile.vendor ? profile.vendor + '_' + profile.product : null);
|
||||
var id = this.normalizeAdapterId(idSource);
|
||||
|
||||
if (!this.currentAdapters)
|
||||
this.currentAdapters = [];
|
||||
|
||||
var exists = this.currentAdapters.some(function(entry) {
|
||||
return (entry.id || entry.section) === id;
|
||||
});
|
||||
if (exists) {
|
||||
ui.addNotification(null, E('p', {}, _('Preset already imported. Adjust preferences below.')), 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentAdapters.push({
|
||||
id: id,
|
||||
label: profile.label || profile.title || id,
|
||||
vendor: profile.vendor || '',
|
||||
product: profile.product || '',
|
||||
port: profile.port || '',
|
||||
enabled: profile.detected ? 1 : 0,
|
||||
preset: profile.id || profile.preset || ''
|
||||
});
|
||||
this.refreshAdapterGrid();
|
||||
ui.addNotification(null, E('p', {}, _('Preset added. Remember to save preferences.')), 'info');
|
||||
},
|
||||
|
||||
savePreferences: function() {
|
||||
var payload = this.collectSettings();
|
||||
payload.adapters = this.collectAdapters();
|
||||
|
||||
@ -128,6 +128,10 @@ apply_adapter_settings() {
|
||||
json_select ..
|
||||
[ -n "$adapter" ] || continue
|
||||
|
||||
if ! uci -q get mqtt-bridge.adapter."$adapter" >/dev/null 2>&1; then
|
||||
uci set mqtt-bridge.adapter."$adapter"="adapter"
|
||||
fi
|
||||
|
||||
[ -n "$enabled" ] && uci set mqtt-bridge.adapter."$adapter".enabled="$enabled"
|
||||
if [ -n "$label" ]; then
|
||||
uci set mqtt-bridge.adapter."$adapter".title="$label"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user