Skip to content

Commit

Permalink
v4.0.30-beta
Browse files Browse the repository at this point in the history
  • Loading branch information
jemu75 committed Mar 21, 2024
1 parent 7e37264 commit f1ec7d1
Show file tree
Hide file tree
Showing 22 changed files with 286 additions and 90 deletions.
46 changes: 41 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,26 @@ In den allgemeinen Einstellungen für Panels wird festgelegt, mit welchen FHEM *

Wenn für ein Panel keine [Vorlage](#vorlagen) verwendet werden soll, oder bestimmte Einstellungen der Vorlage für das Panel angepasst werden sollen, kann die **erweitere Konfiguration** aktiviert werden.

### Element deviceskeys
Die *devicekeys* werden in [Vorlagen](#vorlagen) definiert. Ein *devicekey* ist eine Variable, welche in einer Vorlage (Template) an Stelle eines konkreten FHEM-Devicenamen verwendet wird. Bei der späteren Verwendung der Vorlage, wird in der Panelkonfiguration jeder *devicekey* einem konkreten FHEM-Device zugeordnet.

Somit können [Vorlagen](#vorlagen) für beliebig viele Panels verwendet werden. Die Defintion für einen Button, der einen bestimmten Befehl an FHEM sendet wird dann beispielsweise in Form von `set switch on` statt `set <fhem_device_name> on` in der betreffenden Definition hinterlegt. Die Variable `switch` wird deshalb als *devicekey* definiert und später in der Panelkonfigurtation mit dem tatsächlichen FHEM-Device `<fhem_device_name>` verknüpft.

Für jede Vorlage muss mindestens ein *devicekey* festgelegt werden.

|Parameter|Beschreibung|
|---|---|
|key|eindeutiger Schlüssel, der für Element-Definitionen an Stelle von festen FHEM-Devicenamen verwendet werden soll|
|description|kurze Beschreibung des jeweiligen FHEM Devicetypen|



### Element devices
Liste der FHEM-Devices, die mit dem Panel verknüpft sind. Es muss mindestens ein FHEM-Device mit einem Panel verknüpft werden. (siehe auch [neues Panel erstellen](#neues-panel-erstellen))

|Parameter|Beschreibung|
|---|---|
|key|eindeutiger Schlüssel, der für Element-Definitionen benötigt wird.|
|key|eindeutiger Schlüssel, der für Element-Definitionen benötigt wird|
|device|Name des FHEM Device, mit dem das Panel verknüpft ist|
### Element template
Optional kann eine [Vorlage](#vorlagen) ausgewählt werden, die für die Darstellung des Panels verwendet werden soll. Elemente die im Panel (unter **erweiterte Konfiguration**) definiert wurden, behalten ihre Gültigkeit auch wenn eine Vorlage verwendet wird. In diesem Fall werden die betreffenden Definitionen aus der Vorlage ignoriert.
Expand Down Expand Up @@ -510,23 +524,45 @@ Zeigt ein Chart. Im Gegensatz zu normalen Definitionen, werden zur Anzeige der D

Für die Darstellung von Charts kommt [Apache ECharts](https://echarts.apache.org) zum Einsatz. Diese bietet eine sehr große Vielfalt an Charts und Anpassungsmöglichkeiten.

Grundsätzlich können alle Charttypen, die [Apache ECharts](https://echarts.apache.org) bereitstellt, verwendet werden. Die Konfiguration von [Apache ECharts](https://echarts.apache.org) erfolgt als JSON-Definition. Die Konfigurationsmöglichkeiten der jeweiligen Charts sind in der Dokumentation von [Apache ECharts](https://echarts.apache.org) beschrieben. Die jeweilige JSON-Konfiguration muss in **FHEMApp** in [chart-options](#level-element-chart-options) bzw. in [chart-options2](#level-element-chart-options) übernommen werden. Zu beachten ist, dass keine Java-Script Funktionen innerhalb der JSON-Konfiguration an **FHEMApp** übergeben werden können. Wird keine JSON-Konfiguration in **FHEMApp** hinterlegt, so werden die Daten aus FHEM in Form eines einfachen Line-Chart dargestellt.

Es gibt 3 Möglichkeiten, Daten aus FHEM an ein Chart zu übergeben.
1. einzelner Zahlenwert aus einem *reading* z.B. `22.4 C`
2. ein Array mit Zahlenwerten aus einem *reading* z.B. `[12, 13.5, 0.7, 4, ...]`
3. Werte aus einem FHEM DBLog bzw. LogFile


Parameter|Default|Beschreibung|
|---|---|---|
|reading||siehe Parameter [reading](#konfiguration-der-elemente)|
|value||siehe Parameter [value](#konfiguration-der-elemente)|
|data||kann sowohl eine FHEM-Anweisung `get` für FileLog-Devices bzw. DBLog-Devices enthalten oder eine Reihe von Werten, welche durch ein Leerzeichen oder Komma voneinander getrennt sind.|
|data||kan einen einzelnen Zahlenwert, oder ein Array mit Zahlenwerten oder eine FHEM-Anweisung `get` für FileLog-Devices bzw. DBLog-Devices enthalten|
|name||Name der Datenreihe|
|digits|0|Anzahl der Nachkommastellen, die im Diagramm angezeigt werden.|
|suffix||Einheit für die Zahlenwerte (z.B. % oder °C)|
|type|line|Charttyp der Datenreihe (z.B. line, bar, scatter, gauge und weitere siehe auch [ECharts Beispiele](https://echarts.apache.org/examples/en/index.html))|

Bei Verwendung der FHEM-Anweisung `get` können statt fester Datumsangaben im ISO-Format auch Offsetwerte verwendet werden. z.B. `get log1 - - -14 0 4\:level` Dabei entspricht `-14` dem aktuellen Datum - 14 Tage und `0` dem aktuellen Datum. Da in der FHEM-Anweisung `get` auch Doppelpunkte zum Einsatz kommen ist die Schreibweise `\:` innerhalb der Anweisung zu beachten. (siehe auch [Ersetzungen](#ersetzungen)) Das Ergebnis der `get` Anweisung muss immer zwei Spalten zurückgeben. Die linke Spalte muss den Zeitstempel und die rechte Spalte den zugehörigen Zahlenwert enthalten.

Die zurückgegebenen Daten werden an das EChart Object-Modell an folgende Stellen übergeben:
Die Daten aus der Definition werden an das EChart Object-Modell an folgende Stellen übergeben:
```
{
series: [{ data: <data>, name: <name>, type: <type> },...],
yAxis: [{ axisLabel: fn(<digits>, <suffix>) },...],
series: [{
data: <data>,
name: <name>,
type: <type>,
detail: {
formatter: fn(<digits>, <suffix>)
},
tooltip: {
formatter: fn(<digits>, <suffix>)
}
}],
yAxis: [{
axisLabel: {
formatter: fn(<digits>, <suffix>)
}
}],
legend: { data: [<name>,...] }
}
```
Expand Down
8 changes: 8 additions & 0 deletions public/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# v4.0.30-beta (20.03.2024)
## Core
- bugfix for euro-sign inside from definitions
## Component Chart
- Optimization for displaying formatted values
## Settings
- Wizard for devices definitions in panels
- new element devicekeys in templates
# v4.0.29-beta (17.03.2024)
## Core
- bugfix for change imageGradient in case of changing theme
Expand Down
33 changes: 16 additions & 17 deletions src/components/PanelMainChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,10 @@
}
}
}
} else if(/^\[.*\]/.test(def.data)) {
data = fhem.stringToJson(def.data)
} else {
xAxisType = 'category'
logData = /,/.test(def.data) ? def.data : def.data.replace(/ /g,',')
if(!/^\[.*\]$/.test(loadData)) logData = '[' + logData + ']'
data = fhem.stringToJson(logData)
//on effect on gauge
//for(const [ idx, value] of Object.entries(data)) data[idx] = parseFloat(value).toFixed(def.digits)
data = [fhem.replacer('%n(' + def.digits + ')', def.data, true)]
}
res.push({ xAxisType: xAxisType, type: def.type, name: def.name, digits: def.digits, suffix: def.suffix, data })
Expand All @@ -124,11 +118,11 @@
series: [],
yAxis: [],
xAxis: {}
},
axisLabel,
},
options = JSON.parse(JSON.stringify(fhem.getEl(props.el, ['options']) || {})),
options2 = JSON.parse(JSON.stringify(fhem.getEl(props.el, ['options2']) || {})),
opts = Object.assign(preset, fhem.app.panelMaximized && Object.keys(options2).length > 0 ? options2 : options )
opts = Object.assign(preset, fhem.app.panelMaximized && Object.keys(options2).length > 0 ? options2 : options ),
label
chart.value.fromMenu = false
chart.value.toMenu = false
Expand All @@ -137,9 +131,9 @@
data = await loadData()
for(const [ idx, serie ] of Object.entries(data)) {
axisLabel = {
label = {
formatter: (val) => {
return val.toLocaleString(i18n.locale.value, { minimumFractionDigits: serie.digits, maximumFractionDigits: serie.digits }) + serie.suffix
return val.toLocaleString(i18n.locale.value, { minimumFractionDigits: serie.digits, maximumFractionDigits: serie.digits }) + serie.suffix
}
}
Expand All @@ -148,12 +142,17 @@
if(!opts.legend.data) opts.legend.data = []
if(!opts.xAxis.type) opts.xAxis.type = serie.xAxisType
if(!opts.yAxis[idx].type) opts.yAxis[idx].type = 'value'
if(!opts.yAxis[idx].axisLabel) opts.yAxis[idx].axisLabel = {}
if(!opts.yAxis[idx].axisLabel.formatter) opts.yAxis[idx].axisLabel.formatter = label.formatter
if(!opts.legend.data[idx]) opts.legend.data[idx] = serie.name
if(!opts.series[idx].name) opts.series[idx].name = serie.name
if(!opts.series[idx].type) opts.series[idx].type = serie.type
if(!opts.series[idx].data) opts.series[idx].data = serie.data
if(!opts.yAxis[idx].type) opts.yAxis[idx].type = 'value'
if(!opts.yAxis[idx].axisLabel) opts.yAxis[idx].axisLabel = axisLabel
if(!opts.legend.data[idx]) opts.legend.data[idx] = serie.name
if(!opts.series[idx].detail) opts.series[idx].detail = {}
if(!opts.series[idx].detail.formatter) opts.series[idx].detail.formatter = label.formatter
if(!opts.series[idx].tooltip) opts.series[idx].tooltip = {}
if(!opts.series[idx].tooltip.valueFormatter) opts.series[idx].tooltip.valueFormatter = label.formatter
}
fhem.log(7, 'Chartdata chart.loaded.', opts)
Expand Down
8 changes: 4 additions & 4 deletions src/components/SettingsProps.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@
if(props.type === 'panels') {
res = [
{ key: 'name', title: i18n.t(preLang + 'title'), sortable: true, align: 'start' },
{ key: 'devices', title: 'Devices', sortable: true, align: 'start' },
{ key: 'advanced', title: i18n.t(preLang + 'extended'), sortable: true },
{ key: 'devices', title: 'Devices', sortable: true, align: 'start', filterable: false },
{ key: 'advanced', title: i18n.t(preLang + 'extended'), sortable: true, filterable: false },
{ key: 'actions', title: '', sortable: false, align: 'end' }
]
} else {
res = [
{ key: 'name', title: i18n.t(preLang + 'title'), sortable: true, align: 'start' },
{ key: 'panels', title: i18n.t('_app.settings.panels.title', 2), sortable: true, align: 'end' },
{ key: 'panels', title: i18n.t('_app.settings.panels.title', 2), sortable: true, align: 'end', filterable: false },
{ key: 'actions', title: '', sortable: false, align: 'end' }
]
}
Expand Down Expand Up @@ -81,7 +81,7 @@
}
}
if(Object.keys(el.status).length > 0) advanced.push('status')
if(el.main.length > 1 || Object.keys(el.main[0].level).length > 0) advanced.push('main')
if(el.main && JSON.stringify(el.main) !== '[{"level":{}}]') advanced.push('main')
if(Object.keys(el.info).length > 0) advanced.push('info')
if(el.panel.devices) devices = el.panel.devices.join(', ')
Expand Down
131 changes: 128 additions & 3 deletions src/components/SettingsPropsItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
section: String,
prop: String,
propDef: String,
propHelp: String
propHelp: String,
propAssist: String
})
//type = panels|templates
Expand All @@ -25,6 +26,17 @@
const newDef = ref()
const assist = ref({
devices: {
show: false,
keys: [],
fhemDevices: [],
key: null,
device: null,
idx: -1
}
})
const disable = computed(() => {
let res = fhem.app.config[props.type][props.typeIdx][props.section][props.prop]
return res && res.length > 1 ? false : true
Expand Down Expand Up @@ -60,6 +72,73 @@
delete fhem.app.config[props.type][props.typeIdx][props.section][props.prop]
}
}
function getDevicesKeys() {
let template,
templateIdx,
res = []
template = fhem.app.config[props.type][props.typeIdx].template
if(template) {
templateIdx = fhem.app.config.templates.map((e) => e.name).indexOf(template)
if(templateIdx !== -1) {
res
for(const key of fhem.app.config.templates[templateIdx].panel.devicekeys || []) {
res.push(key.split(':')[0])
}
}
}
return res
}
async function getFhemDevices() {
let res = await fhem.request('json', 'jsonlist2 .* Name alias'),
alias,
list = []
if(res && res.Results.length > 0) {
for(const dev of res.Results) {
alias = dev.Attributes.alias ? ' (' + dev.Attributes.alias + ')' : ''
list.push({ title: dev.Name + alias, value: dev.Name})
}
}
return list
}
async function openAssist(idx) {
let def
if(props.propAssist === 'devices') {
assist.value.devices.keys = getDevicesKeys()
assist.value.devices.fhemDevices = await getFhemDevices()
if(idx !== -1) {
def = fhem.app.config[props.type][props.typeIdx][props.section][props.prop][idx]
assist.value.devices.key = def.split(':')[0],
assist.value.devices.device = def.split(':')[1]
} else {
assist.value.devices.key = null,
assist.value.devices.device = null
}
assist.value.devices.idx = idx
assist.value.devices.show = true
}
}
function confirmAssist() {
if(assist.value.devices.idx !== -1) {
fhem.app.config[props.type][props.typeIdx][props.section][props.prop][assist.value.devices.idx] = assist.value.devices.key + ':' + assist.value.devices.device
} else {
newDef.value = assist.value.devices.key + ':' + assist.value.devices.device
addDef()
}
assist.value.devices.show = false
}
</script>

<template>
Expand All @@ -76,16 +155,18 @@
<v-icon icon="mdi-drag-vertical" class="dd_zone pr-5" :disabled="disable"></v-icon>
<v-col>
<v-text-field
v-model="fhem.app.config[props.type][props.typeIdx][props.section][props.prop][index]"
density="compact"
variant="outlined"
persistent-placeholder
hide-details
:placeholder="props.propDef"
:label="'Definition ' + (index + 1)"
v-model="fhem.app.config[props.type][props.typeIdx][props.section][props.prop][index]">
:prepend-inner-icon="props.propAssist ? 'mdi-auto-fix' : ''"
@click:prepend-inner="openAssist(index)">
</v-text-field>
</v-col>
<v-btn variant="plain" density="compact" icon="mdi-delete" @click="deleteDef(index)" class="pl-5"></v-btn>
<v-btn variant="plain" density="compact" icon="mdi-delete" @click="deleteDef(index)" class="pl-5"></v-btn>
</v-row>
</template>
</draggable>
Expand All @@ -101,12 +182,56 @@
hide-details
:placeholder="props.propDef"
label="new Definition"
:prepend-inner-icon="props.propAssist ? 'mdi-auto-fix' : ''"
:append-inner-icon="props.propHelp ? 'mdi-help-circle' : ''"
clearable
@click:prepend-inner="openAssist(-1)"
@click:append-inner="fhem.help(propHelp)">
</v-text-field>
</v-col>
<v-btn variant="plain" icon="mdi-plus" :disabled="!newDef" @click="addDef()" class="mr-5"></v-btn>
</v-row>
</v-form>

<v-dialog v-model="assist.devices.show" max-width="650px">
<v-card>
<v-sheet color="primary">
<v-card-title>{{ $t('_app.settings.assist.title') }}</v-card-title>
</v-sheet>

<v-card-text>
<v-row class="align-center">
<v-col cols="12" md="4">
<v-select
v-model="assist.devices.key"
:items="assist.devices.keys"
:label="props.propDef.split(':')[0]"
hide-details
density="compact"
variant="outlined">

</v-select>
</v-col>
<v-col cols="12" md="8">
<v-autocomplete
v-model="assist.devices.device"
:items="assist.devices.fhemDevices"
:label="props.propDef.split(':')[1]"
hide-details
clearable
density="compact"
variant="outlined">

</v-autocomplete>
</v-col>
</v-row>
</v-card-text>

<v-card-actions>
<v-spacer></v-spacer>
<v-btn @click="confirmAssist()" :disabled="!assist.devices.key || !assist.devices.device">{{ $t('_app.settings.assist.ok') }}</v-btn>
<v-btn @click="assist.devices.show = false">{{ $t('_app.settings.assist.cancel') }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
6 changes: 4 additions & 2 deletions src/components/SettingsPropsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
const listItemDefs = {
panel: [
{ type: 'defs', show: ['panels', 'extended.panels'], required: true, prop: 'devices', def: 'key:fhem_device', help: 'element-devices' },
{ type: 'defs', show: ['panels', 'extended.panels'], required: true, prop: 'devices', def: 'key:fhem_device', help: 'element-devices', assist: 'devices' },
{ type: 'template', show: ['panels', 'extended.panels'], required: false, prop: 'template', help: 'element-template' },
{ type: 'text', show: ['templates'], required: false, prop: 'author', def: 'text' },
{ type: 'text', show: ['templates'], required: false, prop: 'date', def: 'text' },
{ type: 'defs', show: ['templates'], required: true, prop: 'devicekeys', def: 'key:description', help: 'element-devicekeys' },
{ type: 'defs', show: ['extended.panels', 'templates'], required: false, prop: 'navigation', def: 'reading:value:route', help: 'element-navigation' },
{ type: 'defs', show: ['extended.panels', 'templates'], required: false, prop: 'expandable', def: 'reading:value:expandable:expanded:maximizable', help: 'element-expandable' },
{ type: 'defs', show: ['extended.panels', 'templates'], required: false, prop: 'sortby', def: 'reading:value:sortkey', help: 'element-sortkey' },
Expand Down Expand Up @@ -102,7 +103,8 @@
:section="props.section"
:prop="def.prop"
:propDef="def.def"
:propHelp="def.help">
:propHelp="def.help"
:propAssist="def.assist">
</SettingsPropsItem>

<v-autocomplete v-if="def.type === 'template'"
Expand Down
Loading

0 comments on commit f1ec7d1

Please sign in to comment.