|
|
|
@@ -48,6 +48,7 @@ let _vsGradientEntitySelect: EntitySelect | null = null;
|
|
|
|
|
let _vsCSSSourceEntitySelect: EntitySelect | null = null;
|
|
|
|
|
let _vsGradientEasingIconSelect: IconSelect | null = null;
|
|
|
|
|
let _vsBehaviorIconSelect: IconSelect | null = null;
|
|
|
|
|
let _vsMetricIconSelect: IconSelect | null = null;
|
|
|
|
|
let _vsTagsInput: TagInput | null = null;
|
|
|
|
|
|
|
|
|
|
class ValueSourceModal extends Modal {
|
|
|
|
@@ -63,6 +64,7 @@ class ValueSourceModal extends Modal {
|
|
|
|
|
if (_vsCSSSourceEntitySelect) { _vsCSSSourceEntitySelect.destroy(); _vsCSSSourceEntitySelect = null; }
|
|
|
|
|
if (_vsGradientEasingIconSelect) { _vsGradientEasingIconSelect.destroy(); _vsGradientEasingIconSelect = null; }
|
|
|
|
|
if (_vsBehaviorIconSelect) { _vsBehaviorIconSelect.destroy(); _vsBehaviorIconSelect = null; }
|
|
|
|
|
if (_vsMetricIconSelect) { _vsMetricIconSelect.destroy(); _vsMetricIconSelect = null; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
snapshotValues() {
|
|
|
|
@@ -97,6 +99,14 @@ class ValueSourceModal extends Modal {
|
|
|
|
|
animatedColorEasing: (document.getElementById('value-source-animated-color-easing') as HTMLSelectElement).value,
|
|
|
|
|
colorSchedule: JSON.stringify(_colorSchedulePoints),
|
|
|
|
|
tags: JSON.stringify(_vsTagsInput ? _vsTagsInput.getValue() : []),
|
|
|
|
|
metric: (document.getElementById('value-source-metric') as HTMLSelectElement).value,
|
|
|
|
|
sysmetricMin: (document.getElementById('value-source-sysmetric-min') as HTMLInputElement).value,
|
|
|
|
|
sysmetricMax: (document.getElementById('value-source-sysmetric-max') as HTMLInputElement).value,
|
|
|
|
|
maxRate: (document.getElementById('value-source-max-rate') as HTMLInputElement).value,
|
|
|
|
|
diskPath: (document.getElementById('value-source-disk-path') as HTMLInputElement).value,
|
|
|
|
|
sensorLabel: (document.getElementById('value-source-sensor-label') as HTMLInputElement).value,
|
|
|
|
|
pollInterval: (document.getElementById('value-source-poll-interval') as HTMLInputElement).value,
|
|
|
|
|
sysmetricSmoothing: (document.getElementById('value-source-sysmetric-smoothing') as HTMLInputElement).value,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -123,13 +133,16 @@ function _autoGenerateVSName() {
|
|
|
|
|
const sel = document.getElementById('value-source-picture-source') as HTMLSelectElement;
|
|
|
|
|
const name = sel?.selectedOptions[0]?.textContent?.trim();
|
|
|
|
|
if (name) detail = name;
|
|
|
|
|
} else if (type === 'system_metrics') {
|
|
|
|
|
const metric = (document.getElementById('value-source-metric') as HTMLSelectElement).value;
|
|
|
|
|
detail = t(`value_source.metric.${metric}`);
|
|
|
|
|
}
|
|
|
|
|
(document.getElementById('value-source-name') as HTMLInputElement).value = detail ? `${typeLabel} · ${detail}` : typeLabel;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Icon-grid type selector ──────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
const VS_FLOAT_TYPE_KEYS = ['static', 'animated', 'audio', 'adaptive_time', 'adaptive_scene', 'daylight', 'ha_entity'];
|
|
|
|
|
const VS_FLOAT_TYPE_KEYS = ['static', 'animated', 'audio', 'adaptive_time', 'adaptive_scene', 'daylight', 'ha_entity', 'system_metrics'];
|
|
|
|
|
const VS_COLOR_TYPE_KEYS = ['static_color', 'animated_color', 'adaptive_time_color', 'gradient_map', 'css_extract'];
|
|
|
|
|
const VS_TYPE_KEYS = [...VS_FLOAT_TYPE_KEYS, ...VS_COLOR_TYPE_KEYS];
|
|
|
|
|
|
|
|
|
@@ -302,6 +315,39 @@ function _ensureBehaviorIconSelect() {
|
|
|
|
|
_vsBehaviorIconSelect = new IconSelect({ target: sel, items, columns: 2 } as any);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _ensureMetricIconSelect() {
|
|
|
|
|
const sel = document.getElementById('value-source-metric') as HTMLSelectElement | null;
|
|
|
|
|
if (!sel) return;
|
|
|
|
|
const items = [
|
|
|
|
|
{ value: 'cpu_load', icon: _icon(P.activity), label: t('value_source.metric.cpu_load') },
|
|
|
|
|
{ value: 'cpu_temp', icon: _icon(P.thermometer), label: t('value_source.metric.cpu_temp') },
|
|
|
|
|
{ value: 'gpu_load', icon: _icon(P.zap), label: t('value_source.metric.gpu_load') },
|
|
|
|
|
{ value: 'gpu_temp', icon: _icon(P.flame), label: t('value_source.metric.gpu_temp') },
|
|
|
|
|
{ value: 'ram_usage', icon: _icon(P.cpu), label: t('value_source.metric.ram_usage') },
|
|
|
|
|
{ value: 'disk_usage', icon: _icon(P.hardDrive), label: t('value_source.metric.disk_usage') },
|
|
|
|
|
{ value: 'network_rx', icon: _icon(P.download), label: t('value_source.metric.network_rx') },
|
|
|
|
|
{ value: 'network_tx', icon: _icon(P.send), label: t('value_source.metric.network_tx') },
|
|
|
|
|
{ value: 'battery_level', icon: _icon(P.batteryFull), label: t('value_source.metric.battery_level') },
|
|
|
|
|
{ value: 'fan_speed', icon: _icon(P.fan), label: t('value_source.metric.fan_speed') },
|
|
|
|
|
];
|
|
|
|
|
if (_vsMetricIconSelect) { _vsMetricIconSelect.updateItems(items); return; }
|
|
|
|
|
_vsMetricIconSelect = new IconSelect({ target: sel, items, columns: 2, onChange: (v: string) => { _onMetricChange(v); _autoGenerateVSName(); } } as any);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _onMetricChange(metric: string) {
|
|
|
|
|
const rangeFields = document.getElementById('value-source-sysmetric-range') as HTMLElement | null;
|
|
|
|
|
const networkFields = document.getElementById('value-source-sysmetric-network') as HTMLElement | null;
|
|
|
|
|
const diskFields = document.getElementById('value-source-sysmetric-disk') as HTMLElement | null;
|
|
|
|
|
const sensorFields = document.getElementById('value-source-sysmetric-sensor') as HTMLElement | null;
|
|
|
|
|
const rangeMetrics = ['cpu_temp', 'gpu_temp', 'fan_speed'];
|
|
|
|
|
const networkMetrics = ['network_rx', 'network_tx'];
|
|
|
|
|
const sensorMetrics = ['cpu_temp', 'fan_speed'];
|
|
|
|
|
if (rangeFields) rangeFields.style.display = rangeMetrics.includes(metric) ? '' : 'none';
|
|
|
|
|
if (networkFields) networkFields.style.display = networkMetrics.includes(metric) ? '' : 'none';
|
|
|
|
|
if (diskFields) diskFields.style.display = metric === 'disk_usage' ? '' : 'none';
|
|
|
|
|
if (sensorFields) sensorFields.style.display = sensorMetrics.includes(metric) ? '' : 'none';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _ensureVSTypeIconSelect() {
|
|
|
|
|
const sel = document.getElementById('value-source-type');
|
|
|
|
|
if (!sel) return;
|
|
|
|
@@ -427,6 +473,17 @@ export async function showValueSourceModal(editData: any, presetType: any = null
|
|
|
|
|
_populateCSSSourceDropdown(editData.color_strip_source_id || '');
|
|
|
|
|
(document.getElementById('value-source-led-start') as HTMLInputElement).value = String(editData.led_start ?? 0);
|
|
|
|
|
(document.getElementById('value-source-led-end') as HTMLInputElement).value = String(editData.led_end ?? -1);
|
|
|
|
|
} else if (editData.source_type === 'system_metrics') {
|
|
|
|
|
(document.getElementById('value-source-metric') as HTMLSelectElement).value = editData.metric || 'cpu_load';
|
|
|
|
|
_ensureMetricIconSelect();
|
|
|
|
|
(document.getElementById('value-source-sysmetric-min') as HTMLInputElement).value = String(editData.min_value ?? 0);
|
|
|
|
|
(document.getElementById('value-source-sysmetric-max') as HTMLInputElement).value = String(editData.max_value ?? 100);
|
|
|
|
|
(document.getElementById('value-source-max-rate') as HTMLInputElement).value = String(editData.max_rate ?? 125000000);
|
|
|
|
|
(document.getElementById('value-source-disk-path') as HTMLInputElement).value = editData.disk_path || '';
|
|
|
|
|
(document.getElementById('value-source-sensor-label') as HTMLInputElement).value = editData.sensor_label || '';
|
|
|
|
|
_setSlider('value-source-poll-interval', editData.poll_interval ?? 1.0);
|
|
|
|
|
_setSlider('value-source-sysmetric-smoothing', editData.smoothing ?? 0);
|
|
|
|
|
_onMetricChange(editData.metric || 'cpu_load');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
(document.getElementById('value-source-name') as HTMLInputElement).value = '';
|
|
|
|
@@ -479,6 +536,15 @@ export async function showValueSourceModal(editData: any, presetType: any = null
|
|
|
|
|
// CSS extract defaults
|
|
|
|
|
(document.getElementById('value-source-led-start') as HTMLInputElement).value = '0';
|
|
|
|
|
(document.getElementById('value-source-led-end') as HTMLInputElement).value = '-1';
|
|
|
|
|
// System metrics defaults
|
|
|
|
|
(document.getElementById('value-source-metric') as HTMLSelectElement).value = 'cpu_load';
|
|
|
|
|
(document.getElementById('value-source-sysmetric-min') as HTMLInputElement).value = '0';
|
|
|
|
|
(document.getElementById('value-source-sysmetric-max') as HTMLInputElement).value = '100';
|
|
|
|
|
(document.getElementById('value-source-max-rate') as HTMLInputElement).value = '125000000';
|
|
|
|
|
(document.getElementById('value-source-disk-path') as HTMLInputElement).value = '';
|
|
|
|
|
(document.getElementById('value-source-sensor-label') as HTMLInputElement).value = '';
|
|
|
|
|
_setSlider('value-source-poll-interval', 1.0);
|
|
|
|
|
_setSlider('value-source-sysmetric-smoothing', 0);
|
|
|
|
|
_autoGenerateVSName();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -519,6 +585,11 @@ export function onValueSourceTypeChange() {
|
|
|
|
|
(document.getElementById('value-source-ha-entity-section') as HTMLElement).style.display = type === 'ha_entity' ? '' : 'none';
|
|
|
|
|
(document.getElementById('value-source-gradient-map-section') as HTMLElement).style.display = type === 'gradient_map' ? '' : 'none';
|
|
|
|
|
(document.getElementById('value-source-css-extract-section') as HTMLElement).style.display = type === 'css_extract' ? '' : 'none';
|
|
|
|
|
(document.getElementById('value-source-system-metrics-section') as HTMLElement).style.display = type === 'system_metrics' ? '' : 'none';
|
|
|
|
|
if (type === 'system_metrics') {
|
|
|
|
|
_ensureMetricIconSelect();
|
|
|
|
|
_onMetricChange((document.getElementById('value-source-metric') as HTMLSelectElement).value);
|
|
|
|
|
}
|
|
|
|
|
(document.getElementById('value-source-adaptive-range-section') as HTMLElement).style.display =
|
|
|
|
|
(type === 'adaptive_time' || type === 'adaptive_scene' || type === 'daylight') ? '' : 'none';
|
|
|
|
|
|
|
|
|
@@ -674,6 +745,15 @@ export async function saveValueSource() {
|
|
|
|
|
errorEl.style.display = '';
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else if (sourceType === 'system_metrics') {
|
|
|
|
|
payload.metric = (document.getElementById('value-source-metric') as HTMLSelectElement).value;
|
|
|
|
|
payload.min_value = parseFloat((document.getElementById('value-source-sysmetric-min') as HTMLInputElement).value) || 0;
|
|
|
|
|
payload.max_value = parseFloat((document.getElementById('value-source-sysmetric-max') as HTMLInputElement).value) || 100;
|
|
|
|
|
payload.max_rate = parseFloat((document.getElementById('value-source-max-rate') as HTMLInputElement).value) || 125000000;
|
|
|
|
|
payload.disk_path = (document.getElementById('value-source-disk-path') as HTMLInputElement).value;
|
|
|
|
|
payload.sensor_label = (document.getElementById('value-source-sensor-label') as HTMLInputElement).value;
|
|
|
|
|
payload.poll_interval = parseFloat((document.getElementById('value-source-poll-interval') as HTMLInputElement).value) || 1.0;
|
|
|
|
|
payload.smoothing = parseFloat((document.getElementById('value-source-sysmetric-smoothing') as HTMLInputElement).value) || 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
@@ -1179,6 +1259,9 @@ export function createValueSourceCard(src: ValueSource) {
|
|
|
|
|
${cssBadge}
|
|
|
|
|
<span class="stream-card-prop">${ICON_MOVE_VERTICAL} LED ${rangeLabel}</span>
|
|
|
|
|
`;
|
|
|
|
|
} else if (src.source_type === 'system_metrics') {
|
|
|
|
|
const metricLabel = t(`value_source.metric.${(src as any).metric}`) || (src as any).metric;
|
|
|
|
|
propsHtml = `<span class="stream-card-prop">${ICON_ACTIVITY} ${escapeHtml(metricLabel)}</span>`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return wrapCard({
|
|
|
|
|