v0.9.5
This commit is contained in:
+124
-124
@@ -1,161 +1,161 @@
|
||||
function renderHistorySummary() {
|
||||
const host=$('#historySummary'); if(!host) return;
|
||||
const deviceRows=app.historyData.devices, zoneRows=app.historyData.zones, sensorRows=app.historyData.sensors;
|
||||
const cards=[
|
||||
const host = $('#historySummary'); if (!host) return;
|
||||
const deviceRows = app.historyData.devices, zoneRows = app.historyData.zones, sensorRows = app.historyData.sensors;
|
||||
const cards = [
|
||||
[tr('history.deviceSamples'), app.historyCounts.devices ?? deviceRows.length, tr('history.greeHistory')],
|
||||
[tr('history.zoneSamples'), app.historyCounts.zones ?? zoneRows.length, tr('history.zoneHistory')],
|
||||
[tr('history.haSamples'), app.historyCounts.ha ?? sensorRows.length, tr('history.haHistory')],
|
||||
];
|
||||
host.innerHTML=cards.map(([label,value,detail])=>`<div class="history-stat"><small>${esc(label)}</small><strong>${esc(value)}</strong><span>${esc(detail)}</span></div>`).join('');
|
||||
host.innerHTML = cards.map(([label, value, detail]) => `<div class="history-stat"><small>${esc(label)}</small><strong>${esc(value)}</strong><span>${esc(detail)}</span></div>`).join('');
|
||||
}
|
||||
|
||||
function renderOverviewHistory() {
|
||||
const host=$('#historyCharts');
|
||||
host.innerHTML = historyChartMarkup('overviewIndoorChart',tr('history.allGreeIndoor'),tr('history.allGreeIndoorHint'))
|
||||
+ historyChartMarkup('overviewOutdoorChart',tr('history.allOutdoor'),tr('history.allOutdoorHint'))
|
||||
+ historyChartMarkup('overviewZonesChart',tr('history.allZoneControl'),tr('history.allZoneControlHint'));
|
||||
const host = $('#historyCharts');
|
||||
host.innerHTML = historyChartMarkup('overviewIndoorChart', tr('history.allGreeIndoor'), tr('history.allGreeIndoorHint'))
|
||||
+ historyChartMarkup('overviewOutdoorChart', tr('history.allOutdoor'), tr('history.allOutdoorHint'))
|
||||
+ historyChartMarkup('overviewZonesChart', tr('history.allZoneControl'), tr('history.allZoneControlHint'));
|
||||
|
||||
const deviceRows=app.historyData.devices;
|
||||
const indoorSeries=app.devices.map((device,index)=>({label:device.name,color:historySeriesColor(index),value:row=>!row.zone_id&&!row.entity_id&&row.device_id===device.id?historyNumber(row.indoor_temperature):NaN}));
|
||||
drawLineChart($('#overviewIndoorChart'),indoorSeries,deviceRows,{height:360}); renderLegend($('#overviewIndoorChartLegend'),indoorSeries);
|
||||
const deviceRows = app.historyData.devices;
|
||||
const indoorSeries = app.devices.map((device, index) => ({ label: device.name, color: historySeriesColor(index), value: row => !row.zone_id && !row.entity_id && row.device_id === device.id ? historyNumber(row.indoor_temperature) : NaN }));
|
||||
drawLineChart($('#overviewIndoorChart'), indoorSeries, deviceRows, { height: 360 }); renderLegend($('#overviewIndoorChartLegend'), indoorSeries);
|
||||
|
||||
const outdoorDeviceSeries=app.devices.filter(device=>deviceRows.some(row=>row.device_id===device.id&&Number.isFinite(historyNumber(row.outdoor_temperature)))).map((device,index)=>({label:`${device.name} · ${tr('history.greeOutdoor')}`,color:historySeriesColor(index),value:row=>!row.zone_id&&!row.entity_id&&row.device_id===device.id?historyNumber(row.outdoor_temperature):NaN}));
|
||||
const outdoorEntities=[...new Set(app.historyData.sensors.filter(row=>row.kind==='outdoor').map(row=>row.entity_id))];
|
||||
const outdoorHaSeries=outdoorEntities.map((entity,index)=>({label:`HA · ${haSensorLabel(entity)}`,color:historySeriesColor(index+outdoorDeviceSeries.length),dash:[6,4],value:row=>row.entity_id===entity?historyNumber(row.temperature):NaN}));
|
||||
const outdoorRows=[...deviceRows,...app.historyData.sensors.filter(row=>row.kind==='outdoor')];
|
||||
const outdoorSeries=[...outdoorDeviceSeries,...outdoorHaSeries];
|
||||
drawLineChart($('#overviewOutdoorChart'),outdoorSeries,outdoorRows,{height:320}); renderLegend($('#overviewOutdoorChartLegend'),outdoorSeries);
|
||||
const outdoorDeviceSeries = app.devices.filter(device => deviceRows.some(row => row.device_id === device.id && Number.isFinite(historyNumber(row.outdoor_temperature)))).map((device, index) => ({ label: `${device.name} · ${tr('history.greeOutdoor')}`, color: historySeriesColor(index), value: row => !row.zone_id && !row.entity_id && row.device_id === device.id ? historyNumber(row.outdoor_temperature) : NaN }));
|
||||
const outdoorEntities = [...new Set(app.historyData.sensors.filter(row => row.kind === 'outdoor').map(row => row.entity_id))];
|
||||
const outdoorHaSeries = outdoorEntities.map((entity, index) => ({ label: `HA · ${haSensorLabel(entity)}`, color: historySeriesColor(index + outdoorDeviceSeries.length), dash: [6, 4], value: row => row.entity_id === entity ? historyNumber(row.temperature) : NaN }));
|
||||
const outdoorRows = [...deviceRows, ...app.historyData.sensors.filter(row => row.kind === 'outdoor')];
|
||||
const outdoorSeries = [...outdoorDeviceSeries, ...outdoorHaSeries];
|
||||
drawLineChart($('#overviewOutdoorChart'), outdoorSeries, outdoorRows, { height: 320 }); renderLegend($('#overviewOutdoorChartLegend'), outdoorSeries);
|
||||
|
||||
const zoneRows=app.historyData.zones;
|
||||
const zoneSeries=app.zones.map((zone,index)=>({label:zone.name,color:historySeriesColor(index),value:row=>row.zone_id===zone.id?historyNumber(row.control_temperature):NaN}));
|
||||
drawLineChart($('#overviewZonesChart'),zoneSeries,zoneRows,{height:340}); renderLegend($('#overviewZonesChartLegend'),zoneSeries);
|
||||
const zoneRows = app.historyData.zones;
|
||||
const zoneSeries = app.zones.map((zone, index) => ({ label: zone.name, color: historySeriesColor(index), value: row => row.zone_id === zone.id ? historyNumber(row.control_temperature) : NaN }));
|
||||
drawLineChart($('#overviewZonesChart'), zoneSeries, zoneRows, { height: 340 }); renderLegend($('#overviewZonesChartLegend'), zoneSeries);
|
||||
}
|
||||
|
||||
function renderZoneHistory() {
|
||||
const selected=app.historyZone;
|
||||
const rows=selected==='all'?app.historyData.zones:app.historyData.zones.filter(row=>row.zone_id===selected);
|
||||
const host=$('#historyCharts');
|
||||
host.innerHTML=historyChartMarkup('zoneTemperatureChart',tr('history.temperatureOverview'),tr('history.temperatureOverviewHint'))+historyChartMarkup('zoneOperationChart',tr('history.operationOverview'),tr('history.operationOverviewHint'),true);
|
||||
if(selected==='all'){
|
||||
const series=app.zones.map((zone,index)=>({label:zone.name,color:historySeriesColor(index),value:row=>row.zone_id===zone.id?historyNumber(row.control_temperature):NaN}));
|
||||
drawLineChart($('#zoneTemperatureChart'),series,rows,{height:360}); renderLegend($('#zoneTemperatureChartLegend'),series);
|
||||
const targets=app.zones.map((zone,index)=>({label:`${zone.name} · ${tr('history.targetShort')}`,color:historySeriesColor(index),dash:[5,4],value:row=>row.zone_id===zone.id?historyNumber(row.target_temperature):NaN}));
|
||||
drawLineChart($('#zoneOperationChart'),targets,rows,{height:260}); renderLegend($('#zoneOperationChartLegend'),targets);
|
||||
const selected = app.historyZone;
|
||||
const rows = selected === 'all' ? app.historyData.zones : app.historyData.zones.filter(row => row.zone_id === selected);
|
||||
const host = $('#historyCharts');
|
||||
host.innerHTML = historyChartMarkup('zoneTemperatureChart', tr('history.temperatureOverview'), tr('history.temperatureOverviewHint')) + historyChartMarkup('zoneOperationChart', tr('history.operationOverview'), tr('history.operationOverviewHint'), true);
|
||||
if (selected === 'all') {
|
||||
const series = app.zones.map((zone, index) => ({ label: zone.name, color: historySeriesColor(index), value: row => row.zone_id === zone.id ? historyNumber(row.control_temperature) : NaN }));
|
||||
drawLineChart($('#zoneTemperatureChart'), series, rows, { height: 360 }); renderLegend($('#zoneTemperatureChartLegend'), series);
|
||||
const targets = app.zones.map((zone, index) => ({ label: `${zone.name} · ${tr('history.targetShort')}`, color: historySeriesColor(index), dash: [5, 4], value: row => row.zone_id === zone.id ? historyNumber(row.target_temperature) : NaN }));
|
||||
drawLineChart($('#zoneOperationChart'), targets, rows, { height: 260 }); renderLegend($('#zoneOperationChartLegend'), targets);
|
||||
return;
|
||||
}
|
||||
const temperatureSeries=[
|
||||
{label:tr('history.greeSensor'),color:cssColor('--accent','#3ecf8e'),value:row=>historyNumber(row.gree_temperature)},
|
||||
{label:tr('history.roomSensor'),color:cssColor('--info','#60a5fa'),value:row=>historyNumber(row.external_temperature)},
|
||||
{label:tr('history.controlTemperature'),color:cssColor('--teal','#2dd4bf'),width:2.8,value:row=>historyNumber(row.control_temperature)},
|
||||
{label:tr('history.comfortTarget'),color:cssColor('--warning','#f59e0b'),dash:[7,5],value:row=>historyNumber(row.target_temperature)},
|
||||
{label:tr('history.deviceSetpoint'),color:cssColor('--purple','#a78bfa'),dash:[3,4],value:row=>historyNumber(row.device_setpoint)},
|
||||
{label:tr('history.outdoorTemperature'),color:cssColor('--muted-strong','#9ca3af'),dash:[2,5],value:row=>historyNumber(row.outdoor_temperature)},
|
||||
const temperatureSeries = [
|
||||
{ label: tr('history.greeSensor'), color: cssColor('--accent', '#3ecf8e'), value: row => historyNumber(row.gree_temperature) },
|
||||
{ label: tr('history.roomSensor'), color: cssColor('--info', '#60a5fa'), value: row => historyNumber(row.external_temperature) },
|
||||
{ label: tr('history.controlTemperature'), color: cssColor('--teal', '#2dd4bf'), width: 2.8, value: row => historyNumber(row.control_temperature) },
|
||||
{ label: tr('history.comfortTarget'), color: cssColor('--warning', '#f59e0b'), dash: [7, 5], value: row => historyNumber(row.target_temperature) },
|
||||
{ label: tr('history.deviceSetpoint'), color: cssColor('--purple', '#a78bfa'), dash: [3, 4], value: row => historyNumber(row.device_setpoint) },
|
||||
{ label: tr('history.outdoorTemperature'), color: cssColor('--muted-strong', '#9ca3af'), dash: [2, 5], value: row => historyNumber(row.outdoor_temperature) },
|
||||
];
|
||||
drawLineChart($('#zoneTemperatureChart'),temperatureSeries,rows,{height:360}); renderLegend($('#zoneTemperatureChartLegend'),temperatureSeries);
|
||||
const operationSeries=[
|
||||
{label:tr('history.fanSpeed'),color:cssColor('--info','#60a5fa'),step:true,value:row=>historyNumber(row.fan_speed),tooltipValue:value=>fanLabel(Math.round(value))},
|
||||
{label:tr('history.demand'),color:cssColor('--accent','#3ecf8e'),step:true,width:2.4,value:row=>row.demand?4.5:0.5,tooltipValue:(_,row)=>row.demand?tr('common.active'):tr('common.disabled')},
|
||||
{label:tr('history.power'),color:cssColor('--warning','#f59e0b'),step:true,dash:[5,4],value:row=>row.power?3.5:0.5,tooltipValue:(_,row)=>row.power?tr('common.on'):tr('common.off')},
|
||||
drawLineChart($('#zoneTemperatureChart'), temperatureSeries, rows, { height: 360 }); renderLegend($('#zoneTemperatureChartLegend'), temperatureSeries);
|
||||
const operationSeries = [
|
||||
{ label: tr('history.fanSpeed'), color: cssColor('--info', '#60a5fa'), step: true, value: row => historyNumber(row.fan_speed), tooltipValue: value => fanLabel(Math.round(value)) },
|
||||
{ label: tr('history.demand'), color: cssColor('--accent', '#3ecf8e'), step: true, width: 2.4, value: row => row.demand ? 4.5 : 0.5, tooltipValue: (_, row) => row.demand ? tr('common.active') : tr('common.disabled') },
|
||||
{ label: tr('history.power'), color: cssColor('--warning', '#f59e0b'), step: true, dash: [5, 4], value: row => row.power ? 3.5 : 0.5, tooltipValue: (_, row) => row.power ? tr('common.on') : tr('common.off') },
|
||||
];
|
||||
drawLineChart($('#zoneOperationChart'),operationSeries,rows,{height:260,minValue:0,maxValue:5,binaryLabels:true}); renderLegend($('#zoneOperationChartLegend'),operationSeries);
|
||||
drawLineChart($('#zoneOperationChart'), operationSeries, rows, { height: 260, minValue: 0, maxValue: 5, binaryLabels: true }); renderLegend($('#zoneOperationChartLegend'), operationSeries);
|
||||
}
|
||||
|
||||
function renderDeviceHistory() {
|
||||
const selected=app.historyDevice;
|
||||
const rows=selected==='all'?app.historyData.devices:app.historyData.devices.filter(row=>row.device_id===selected);
|
||||
const host=$('#historyCharts');
|
||||
host.innerHTML=historyChartMarkup('deviceIndoorChart',tr('history.deviceIndoor'),tr('history.deviceIndoorHint'))+historyChartMarkup('deviceOutdoorChart',tr('history.deviceOutdoor'),tr('history.deviceOutdoorHint'))+historyChartMarkup('deviceTargetChart',tr('history.deviceTargets'),tr('history.deviceTargetsHint'),true);
|
||||
const devices=selected==='all'?app.devices:app.devices.filter(device=>device.id===selected);
|
||||
const indoor=devices.map((device,index)=>({label:device.name,color:historySeriesColor(index),value:row=>row.device_id===device.id?historyNumber(row.indoor_temperature):NaN}));
|
||||
const outdoor=devices.filter(device=>rows.some(row=>row.device_id===device.id&&Number.isFinite(historyNumber(row.outdoor_temperature)))).map((device,index)=>({label:device.name,color:historySeriesColor(index),value:row=>row.device_id===device.id?historyNumber(row.outdoor_temperature):NaN}));
|
||||
const targets=devices.map((device,index)=>({label:device.name,color:historySeriesColor(index),dash:[6,4],value:row=>row.device_id===device.id?historyNumber(row.target_temperature):NaN}));
|
||||
drawLineChart($('#deviceIndoorChart'),indoor,rows,{height:350}); renderLegend($('#deviceIndoorChartLegend'),indoor);
|
||||
drawLineChart($('#deviceOutdoorChart'),outdoor,rows,{height:310}); renderLegend($('#deviceOutdoorChartLegend'),outdoor);
|
||||
drawLineChart($('#deviceTargetChart'),targets,rows,{height:260}); renderLegend($('#deviceTargetChartLegend'),targets);
|
||||
const selected = app.historyDevice;
|
||||
const rows = selected === 'all' ? app.historyData.devices : app.historyData.devices.filter(row => row.device_id === selected);
|
||||
const host = $('#historyCharts');
|
||||
host.innerHTML = historyChartMarkup('deviceIndoorChart', tr('history.deviceIndoor'), tr('history.deviceIndoorHint')) + historyChartMarkup('deviceOutdoorChart', tr('history.deviceOutdoor'), tr('history.deviceOutdoorHint')) + historyChartMarkup('deviceTargetChart', tr('history.deviceTargets'), tr('history.deviceTargetsHint'), true);
|
||||
const devices = selected === 'all' ? app.devices : app.devices.filter(device => device.id === selected);
|
||||
const indoor = devices.map((device, index) => ({ label: device.name, color: historySeriesColor(index), value: row => row.device_id === device.id ? historyNumber(row.indoor_temperature) : NaN }));
|
||||
const outdoor = devices.filter(device => rows.some(row => row.device_id === device.id && Number.isFinite(historyNumber(row.outdoor_temperature)))).map((device, index) => ({ label: device.name, color: historySeriesColor(index), value: row => row.device_id === device.id ? historyNumber(row.outdoor_temperature) : NaN }));
|
||||
const targets = devices.map((device, index) => ({ label: device.name, color: historySeriesColor(index), dash: [6, 4], value: row => row.device_id === device.id ? historyNumber(row.target_temperature) : NaN }));
|
||||
drawLineChart($('#deviceIndoorChart'), indoor, rows, { height: 350 }); renderLegend($('#deviceIndoorChartLegend'), indoor);
|
||||
drawLineChart($('#deviceOutdoorChart'), outdoor, rows, { height: 310 }); renderLegend($('#deviceOutdoorChartLegend'), outdoor);
|
||||
drawLineChart($('#deviceTargetChart'), targets, rows, { height: 260 }); renderLegend($('#deviceTargetChartLegend'), targets);
|
||||
}
|
||||
|
||||
function renderSensorHistory() {
|
||||
const selected=app.historySensor;
|
||||
const rows=selected==='all'?app.historyData.sensors:app.historyData.sensors.filter(row=>row.entity_id===selected);
|
||||
const entities=[...new Set(rows.map(row=>row.entity_id))];
|
||||
const host=$('#historyCharts');
|
||||
host.innerHTML=historyChartMarkup('haSensorsChart',tr('history.haSensors'),tr('history.haSensorsHint'));
|
||||
const series=entities.map((entity,index)=>({label:haSensorLabel(entity),color:historySeriesColor(index),dash:rows.some(row=>row.entity_id===entity&&row.kind==='outdoor')?[6,4]:[],value:row=>row.entity_id===entity?historyNumber(row.temperature):NaN}));
|
||||
drawLineChart($('#haSensorsChart'),series,rows,{height:370}); renderLegend($('#haSensorsChartLegend'),series);
|
||||
const selected = app.historySensor;
|
||||
const rows = selected === 'all' ? app.historyData.sensors : app.historyData.sensors.filter(row => row.entity_id === selected);
|
||||
const entities = [...new Set(rows.map(row => row.entity_id))];
|
||||
const host = $('#historyCharts');
|
||||
host.innerHTML = historyChartMarkup('haSensorsChart', tr('history.haSensors'), tr('history.haSensorsHint'));
|
||||
const series = entities.map((entity, index) => ({ label: haSensorLabel(entity), color: historySeriesColor(index), dash: rows.some(row => row.entity_id === entity && row.kind === 'outdoor') ? [6, 4] : [], value: row => row.entity_id === entity ? historyNumber(row.temperature) : NaN }));
|
||||
drawLineChart($('#haSensorsChart'), series, rows, { height: 370 }); renderLegend($('#haSensorsChartLegend'), series);
|
||||
}
|
||||
|
||||
function customSeriesOptions() {
|
||||
const items=[];
|
||||
app.devices.forEach(device=>{
|
||||
items.push([`device|${device.id}|indoor`,`${device.name} · ${tr('history.indoorTemperature')}`]);
|
||||
items.push([`device|${device.id}|outdoor`,`${device.name} · ${tr('history.greeOutdoor')}`]);
|
||||
items.push([`device|${device.id}|target`,`${device.name} · ${tr('history.deviceTarget')}`]);
|
||||
const items = [];
|
||||
app.devices.forEach(device => {
|
||||
items.push([`device|${device.id}|indoor`, `${device.name} · ${tr('history.indoorTemperature')}`]);
|
||||
items.push([`device|${device.id}|outdoor`, `${device.name} · ${tr('history.greeOutdoor')}`]);
|
||||
items.push([`device|${device.id}|target`, `${device.name} · ${tr('history.deviceTarget')}`]);
|
||||
});
|
||||
app.zones.forEach(zone=>{
|
||||
items.push([`zone|${zone.id}|control`,`${zone.name} · ${tr('history.controlTemperature')}`]);
|
||||
items.push([`zone|${zone.id}|gree`,`${zone.name} · ${tr('history.greeSensor')}`]);
|
||||
items.push([`zone|${zone.id}|external`,`${zone.name} · ${tr('history.roomSensor')}`]);
|
||||
items.push([`zone|${zone.id}|target`,`${zone.name} · ${tr('history.comfortTarget')}`]);
|
||||
items.push([`zone|${zone.id}|device_target`,`${zone.name} · ${tr('history.deviceSetpoint')}`]);
|
||||
items.push([`zone|${zone.id}|outdoor`,`${zone.name} · ${tr('history.outdoorTemperature')}`]);
|
||||
app.zones.forEach(zone => {
|
||||
items.push([`zone|${zone.id}|control`, `${zone.name} · ${tr('history.controlTemperature')}`]);
|
||||
items.push([`zone|${zone.id}|gree`, `${zone.name} · ${tr('history.greeSensor')}`]);
|
||||
items.push([`zone|${zone.id}|external`, `${zone.name} · ${tr('history.roomSensor')}`]);
|
||||
items.push([`zone|${zone.id}|target`, `${zone.name} · ${tr('history.comfortTarget')}`]);
|
||||
items.push([`zone|${zone.id}|device_target`, `${zone.name} · ${tr('history.deviceSetpoint')}`]);
|
||||
items.push([`zone|${zone.id}|outdoor`, `${zone.name} · ${tr('history.outdoorTemperature')}`]);
|
||||
});
|
||||
[...new Set(app.historyData.sensors.map(row=>row.entity_id))].forEach(entity=>items.push([`ha|${entity}|temperature`,`HA · ${haSensorLabel(entity)}`]));
|
||||
[...new Set(app.historyData.sensors.map(row => row.entity_id))].forEach(entity => items.push([`ha|${entity}|temperature`, `HA · ${haSensorLabel(entity)}`]));
|
||||
return items;
|
||||
}
|
||||
|
||||
function customSeriesDefinition(key,index=0) {
|
||||
const [kind,id,field]=String(key).split('|');
|
||||
const color=historySeriesColor(index);
|
||||
if(kind==='device'){
|
||||
const device=app.devices.find(item=>item.id===id); if(!device) return null;
|
||||
const labels={indoor:tr('history.indoorTemperature'),outdoor:tr('history.greeOutdoor'),target:tr('history.deviceTarget')};
|
||||
const fields={indoor:'indoor_temperature',outdoor:'outdoor_temperature',target:'target_temperature'};
|
||||
return {key,label:`${device.name} · ${labels[field]||field}`,color,dash:field==='target'?[6,4]:[],value:row=>!row.zone_id&&!row.entity_id&&row.device_id===id?historyNumber(row[fields[field]]):NaN};
|
||||
function customSeriesDefinition(key, index = 0) {
|
||||
const [kind, id, field] = String(key).split('|');
|
||||
const color = historySeriesColor(index);
|
||||
if (kind === 'device') {
|
||||
const device = app.devices.find(item => item.id === id); if (!device) return null;
|
||||
const labels = { indoor: tr('history.indoorTemperature'), outdoor: tr('history.greeOutdoor'), target: tr('history.deviceTarget') };
|
||||
const fields = { indoor: 'indoor_temperature', outdoor: 'outdoor_temperature', target: 'target_temperature' };
|
||||
return { key, label: `${device.name} · ${labels[field] || field}`, color, dash: field === 'target' ? [6, 4] : [], value: row => !row.zone_id && !row.entity_id && row.device_id === id ? historyNumber(row[fields[field]]) : NaN };
|
||||
}
|
||||
if(kind==='zone'){
|
||||
const zone=app.zones.find(item=>item.id===id); if(!zone) return null;
|
||||
const fields={control:['control_temperature',tr('history.controlTemperature')],gree:['gree_temperature',tr('history.greeSensor')],external:['external_temperature',tr('history.roomSensor')],target:['target_temperature',tr('history.comfortTarget')],device_target:['device_setpoint',tr('history.deviceSetpoint')],outdoor:['outdoor_temperature',tr('history.outdoorTemperature')]};
|
||||
const info=fields[field]; if(!info) return null;
|
||||
return {key,label:`${zone.name} · ${info[1]}`,color,dash:['target','device_target','outdoor'].includes(field)?[6,4]:[],value:row=>row.zone_id===id?historyNumber(row[info[0]]):NaN};
|
||||
if (kind === 'zone') {
|
||||
const zone = app.zones.find(item => item.id === id); if (!zone) return null;
|
||||
const fields = { control: ['control_temperature', tr('history.controlTemperature')], gree: ['gree_temperature', tr('history.greeSensor')], external: ['external_temperature', tr('history.roomSensor')], target: ['target_temperature', tr('history.comfortTarget')], device_target: ['device_setpoint', tr('history.deviceSetpoint')], outdoor: ['outdoor_temperature', tr('history.outdoorTemperature')] };
|
||||
const info = fields[field]; if (!info) return null;
|
||||
return { key, label: `${zone.name} · ${info[1]}`, color, dash: ['target', 'device_target', 'outdoor'].includes(field) ? [6, 4] : [], value: row => row.zone_id === id ? historyNumber(row[info[0]]) : NaN };
|
||||
}
|
||||
if(kind==='ha') return {key,label:`HA · ${haSensorLabel(id)}`,color,dash:[3,4],value:row=>row.entity_id===id?historyNumber(row.temperature):NaN};
|
||||
if (kind === 'ha') return { key, label: `HA · ${haSensorLabel(id)}`, color, dash: [3, 4], value: row => row.entity_id === id ? historyNumber(row.temperature) : NaN };
|
||||
return null;
|
||||
}
|
||||
|
||||
function persistSavedCharts() {
|
||||
localStorage.setItem('gree_controller_saved_charts',JSON.stringify(app.savedCharts.slice(0,30)));
|
||||
localStorage.setItem('gree_controller_saved_charts', JSON.stringify(app.savedCharts.slice(0, 30)));
|
||||
}
|
||||
|
||||
function renderCustomBuilder() {
|
||||
const host=$('#historyCustomBuilder'); if(!host) return;
|
||||
if(app.historyTab!=='custom'){host.innerHTML='';host.classList.remove('active');return;}
|
||||
const host = $('#historyCustomBuilder'); if (!host) return;
|
||||
if (app.historyTab !== 'custom') { host.innerHTML = ''; host.classList.remove('active'); return; }
|
||||
host.classList.add('active');
|
||||
const options=customSeriesOptions();
|
||||
const selected=app.customChartSeries.map((key,index)=>customSeriesDefinition(key,index)).filter(Boolean);
|
||||
host.innerHTML=`<div class="panel custom-chart-panel"><div class="chart-title-row"><div><h3>${esc(tr('history.customTitle'))}</h3><p>${esc(tr('history.customDescription'))}</p></div></div>
|
||||
<div class="custom-chart-add"><select id="customSeriesSelect">${options.map(([value,label])=>`<option value="${esc(value)}">${esc(label)}</option>`).join('')}</select><button class="secondary" data-history-action="add-series">${esc(tr('history.addSeries'))}</button></div>
|
||||
<div class="custom-series-list">${selected.length?selected.map((item,index)=>`<span class="custom-series-chip"><i style="--chip-color:${esc(item.color)}"></i>${esc(item.label)}<button data-history-action="remove-series" data-index="${index}" aria-label="${esc(tr('actions.remove'))}">×</button></span>`).join(''):`<span class="field-note">${esc(tr('history.noCustomSeries'))}</span>`}</div>
|
||||
const options = customSeriesOptions();
|
||||
const selected = app.customChartSeries.map((key, index) => customSeriesDefinition(key, index)).filter(Boolean);
|
||||
host.innerHTML = `<div class="panel custom-chart-panel"><div class="chart-title-row"><div><h3>${esc(tr('history.customTitle'))}</h3><p>${esc(tr('history.customDescription'))}</p></div></div>
|
||||
<div class="custom-chart-add"><select id="customSeriesSelect">${options.map(([value, label]) => `<option value="${esc(value)}">${esc(label)}</option>`).join('')}</select><button class="secondary" data-history-action="add-series">${esc(tr('history.addSeries'))}</button></div>
|
||||
<div class="custom-series-list">${selected.length ? selected.map((item, index) => `<span class="custom-series-chip"><i style="--chip-color:${esc(item.color)}"></i>${esc(item.label)}<button data-history-action="remove-series" data-index="${index}" aria-label="${esc(tr('actions.remove'))}">×</button></span>`).join('') : `<span class="field-note">${esc(tr('history.noCustomSeries'))}</span>`}</div>
|
||||
<div class="custom-chart-save"><input id="customChartName" placeholder="${esc(tr('history.chartName'))}"><button class="secondary" data-history-action="save-chart">${esc(tr('actions.save'))}</button><button class="primary" data-history-action="copy-chart-link">${esc(tr('history.copyLink'))}</button></div>
|
||||
<div class="saved-chart-list">${app.savedCharts.length?app.savedCharts.map(item=>`<div class="saved-chart-row"><button data-history-action="load-chart" data-id="${esc(item.id)}"><strong>${esc(item.name)}</strong><small>${esc(item.series.length)} ${esc(tr('history.series'))}</small></button><button class="danger" data-history-action="delete-chart" data-id="${esc(item.id)}">×</button></div>`).join(''):`<span class="field-note">${esc(tr('history.noSavedCharts'))}</span>`}</div>
|
||||
<div class="saved-chart-list">${app.savedCharts.length ? app.savedCharts.map(item => `<div class="saved-chart-row"><button data-history-action="load-chart" data-id="${esc(item.id)}"><strong>${esc(item.name)}</strong><small>${esc(item.series.length)} ${esc(tr('history.series'))}</small></button><button class="danger" data-history-action="delete-chart" data-id="${esc(item.id)}">×</button></div>`).join('') : `<span class="field-note">${esc(tr('history.noSavedCharts'))}</span>`}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function renderCustomHistory() {
|
||||
renderCustomBuilder();
|
||||
const host=$('#historyCharts');
|
||||
host.innerHTML=historyChartMarkup('customHistoryChart',tr('history.customChart'),tr('history.customChartHint'));
|
||||
const series=app.customChartSeries.map((key,index)=>customSeriesDefinition(key,index)).filter(Boolean);
|
||||
const rows=[...app.historyData.devices,...app.historyData.zones,...app.historyData.sensors];
|
||||
drawLineChart($('#customHistoryChart'),series,rows,{height:390}); renderLegend($('#customHistoryChartLegend'),series);
|
||||
const host = $('#historyCharts');
|
||||
host.innerHTML = historyChartMarkup('customHistoryChart', tr('history.customChart'), tr('history.customChartHint'));
|
||||
const series = app.customChartSeries.map((key, index) => customSeriesDefinition(key, index)).filter(Boolean);
|
||||
const rows = [...app.historyData.devices, ...app.historyData.zones, ...app.historyData.sensors];
|
||||
drawLineChart($('#customHistoryChart'), series, rows, { height: 390 }); renderLegend($('#customHistoryChartLegend'), series);
|
||||
}
|
||||
|
||||
function renderHistoryPage() {
|
||||
renderHistorySummary();
|
||||
renderCustomBuilder();
|
||||
if(app.historyTab==='overview') renderOverviewHistory();
|
||||
else if(app.historyTab==='zones') renderZoneHistory();
|
||||
else if(app.historyTab==='devices') renderDeviceHistory();
|
||||
else if(app.historyTab==='sensors') renderSensorHistory();
|
||||
if (app.historyTab === 'overview') renderOverviewHistory();
|
||||
else if (app.historyTab === 'zones') renderZoneHistory();
|
||||
else if (app.historyTab === 'devices') renderDeviceHistory();
|
||||
else if (app.historyTab === 'sensors') renderSensorHistory();
|
||||
else renderCustomHistory();
|
||||
}
|
||||
|
||||
@@ -164,34 +164,34 @@ function drawCurrentChartIfVisible() {
|
||||
}
|
||||
|
||||
async function handleHistoryAction(button) {
|
||||
const action=button.dataset.historyAction;
|
||||
if(action==='add-series'){
|
||||
const value=$('#customSeriesSelect')?.value;
|
||||
if(value && !app.customChartSeries.includes(value)) app.customChartSeries.push(value);
|
||||
const action = button.dataset.historyAction;
|
||||
if (action === 'add-series') {
|
||||
const value = $('#customSeriesSelect')?.value;
|
||||
if (value && !app.customChartSeries.includes(value)) app.customChartSeries.push(value);
|
||||
renderCustomHistory(); updateBrowserUrl(currentHistoryPath(), true); return;
|
||||
}
|
||||
if(action==='remove-series'){
|
||||
app.customChartSeries.splice(Number(button.dataset.index),1);
|
||||
if (action === 'remove-series') {
|
||||
app.customChartSeries.splice(Number(button.dataset.index), 1);
|
||||
renderCustomHistory(); updateBrowserUrl(currentHistoryPath(), true); return;
|
||||
}
|
||||
if(action==='save-chart'){
|
||||
if(!app.customChartSeries.length) return toast(tr('history.noCustomSeries'), true);
|
||||
const name=$('#customChartName')?.value.trim() || tr('history.customChart');
|
||||
const item={id:`chart-${Date.now()}`,name,series:[...app.customChartSeries],hours:$('#historyHours')?.value||'24'};
|
||||
if (action === 'save-chart') {
|
||||
if (!app.customChartSeries.length) return toast(tr('history.noCustomSeries'), true);
|
||||
const name = $('#customChartName')?.value.trim() || tr('history.customChart');
|
||||
const item = { id: `chart-${Date.now()}`, name, series: [...app.customChartSeries], hours: $('#historyHours')?.value || '24' };
|
||||
app.savedCharts.unshift(item); persistSavedCharts(); renderCustomHistory(); toast(tr('history.chartSaved')); return;
|
||||
}
|
||||
if(action==='load-chart'){
|
||||
const item=app.savedCharts.find(entry=>entry.id===button.dataset.id); if(!item) return;
|
||||
app.customChartSeries=[...item.series]; if($('#historyHours')&&item.hours) $('#historyHours').value=String(item.hours);
|
||||
if (action === 'load-chart') {
|
||||
const item = app.savedCharts.find(entry => entry.id === button.dataset.id); if (!item) return;
|
||||
app.customChartSeries = [...item.series]; if ($('#historyHours') && item.hours) $('#historyHours').value = String(item.hours);
|
||||
renderCustomHistory(); updateBrowserUrl(currentHistoryPath()); return;
|
||||
}
|
||||
if(action==='delete-chart'){
|
||||
app.savedCharts=app.savedCharts.filter(entry=>entry.id!==button.dataset.id); persistSavedCharts(); renderCustomHistory(); return;
|
||||
if (action === 'delete-chart') {
|
||||
app.savedCharts = app.savedCharts.filter(entry => entry.id !== button.dataset.id); persistSavedCharts(); renderCustomHistory(); return;
|
||||
}
|
||||
if(action==='copy-chart-link'){
|
||||
if(!app.customChartSeries.length) return toast(tr('history.noCustomSeries'), true);
|
||||
const path=currentHistoryPath(); updateBrowserUrl(path, true); const link=`${location.origin}${APP_BASE}${path}`;
|
||||
try { await navigator.clipboard.writeText(link); } catch (_) { const area=document.createElement('textarea'); area.value=link; document.body.appendChild(area); area.select(); document.execCommand('copy'); area.remove(); }
|
||||
if (action === 'copy-chart-link') {
|
||||
if (!app.customChartSeries.length) return toast(tr('history.noCustomSeries'), true);
|
||||
const path = currentHistoryPath(); updateBrowserUrl(path, true); const link = `${location.origin}${APP_BASE}${path}`;
|
||||
try { await navigator.clipboard.writeText(link); } catch (_) { const area = document.createElement('textarea'); area.value = link; document.body.appendChild(area); area.select(); document.execCommand('copy'); area.remove(); }
|
||||
toast(tr('history.linkCopied')); return;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user