Zahlencodes als Klartext
Manche Geraete schicken eine Zahl und meinen einen Zustand - der
go-eCharger etwa 2 fuer "Charging". Welche Zahl welchen Namen hat, steht im
value_template der Home-Assistant-Discovery als Werttabelle:
{{ ['Unknown','Idle','Charging','WaitCar','Complete','Error'][value_json|int] }}
Das ist kein Pfad in die Nutzlast, sondern eine Uebersetzung, und sie wurde
bisher verworfen. Jetzt landet sie in possible_values - in derselben
Schreibweise, die WLED fuer seine Effektliste schon benutzt, naemlich einer
Liste aus {Wert: Bezeichnung}. Damit versteht der Editor sie ohne
Zusatzarbeit und macht eine Auswahlliste daraus. Ein Versatz im Ausdruck
wandert in die Schluessel: aus [value_json|int-3] wird {"3":"Default"}.
Der Runner uebersetzt beim Lesen, eine Bedingung vergleicht also den
Klartext. Steht die Zahl nicht in der Tabelle, bleibt sie stehen - ein
erfundener Name waere schlimmer als ein roher Wert.
Dabei ist ein Folgefehler aufgefallen: Auswahlwerte in dieser Schreibweise
kamen im Editor als "[object Object]" an. Die alte addOptions kannte die
Form, der neue Modell-Aufbau nicht - aufgefallen ist es nie, weil WLED beim
Umbau abgeschaltet war. Der Katalog liefert Auswahlwerte jetzt einheitlich
als {value, label}, und zwar seitenrichtig:
Messwert angezeigt Charging, gespeichert Charging
(der Runner hat schon uebersetzt)
Parameter angezeigt Blink, gespeichert 1
(das Geraet will die Zahl)
Die Uebersicht loest die Bezeichnung ebenfalls auf: "Effekt (Blink)" statt
"Effekt (1)".
Nachgemessen: alle fuenf Werttabellen auf dem Broker richtig erkannt,
einschliesslich des Versatzes, kein Fehltreffer unter den 35 uebrigen
Vorlagen. Live liefert der go-eCharger jetzt Idle, Neutral, Auto, Eco und
None statt 1, 0, 0, 4, 0. Ein weiterer Discovery-Lauf aendert keine Zeile.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -84,9 +84,11 @@ function wertFeld(spec, wert) {
|
||||
optionListe(feld, [{ value: "true", text: "JA" }, { value: "false", text: "NEIN" }],
|
||||
wert === "" ? "true" : wert);
|
||||
} else if (spec.input === "select") {
|
||||
// Die Auswahl kommt fertig aus dem Katalog: bei Messwerten steht der
|
||||
// Klartext auch als Wert, bei Geraeteparametern die Zahl dahinter.
|
||||
feld = document.createElement("select");
|
||||
feld.className = "form-select";
|
||||
optionListe(feld, spec.options.map(o => ({ value: o, text: o })), wert);
|
||||
optionListe(feld, spec.options.map(o => ({ value: o.value, text: o.label })), wert);
|
||||
} else {
|
||||
feld = document.createElement("input");
|
||||
feld.className = "form-control";
|
||||
@@ -104,7 +106,7 @@ function wertFeld(spec, wert) {
|
||||
/** Standardwert, wenn eine Zeile auf einen anderen Messwert umgestellt wird. */
|
||||
function standardWert(spec) {
|
||||
if (spec.input === "bool") return "true";
|
||||
if (spec.input === "select") return spec.options.length ? spec.options[0] : "";
|
||||
if (spec.input === "select") return spec.options.length ? spec.options[0].value : "";
|
||||
if (spec.input === "time") return "00:00";
|
||||
if (spec.input === "date") return new Date().toISOString().split("T")[0];
|
||||
if (spec.input === "number") return "0";
|
||||
|
||||
@@ -140,11 +140,34 @@ auch WLED benutzt: `[4]`, `ssid`, `seg[0].col[0]`. Ohne Pfad gilt die ganze
|
||||
Nutzlast.
|
||||
|
||||
Gelesen wird nur der einfache Fall aus dem Template: ein Zugriff auf
|
||||
`value_json` und was danach an Punkten und Klammern folgt. Werttabellen wie
|
||||
`{{ ['Idle','Charging'][value_json|int] }}` sind keine Pfade, sondern eine
|
||||
Übersetzung von Zahl nach Text — dort bleibt es beim Rohwert. Das betrifft
|
||||
hier acht Messwerte (Ladezustand, Fehlercode und Ähnliches am go-eCharger);
|
||||
sie sind als Zahl vergleichbar, nur nicht als Klartext.
|
||||
`value_json` und was danach an Punkten und Klammern folgt.
|
||||
|
||||
### Werttabellen
|
||||
|
||||
Manche Geräte schicken eine Zahl und meinen einen Zustand:
|
||||
|
||||
```
|
||||
{{ ['Unknown','Idle','Charging','WaitCar','Complete','Error'][value_json|int] }}
|
||||
{{ ['Default','Eco','NextTrip'][value_json|int-3] }}
|
||||
```
|
||||
|
||||
Das ist kein Pfad, sondern eine Übersetzung von Zahl nach Text. Sie landet in
|
||||
`possible_values` — in der Schreibweise, die WLED für seine Effektliste schon
|
||||
benutzt: eine Liste aus `{Wert: Bezeichnung}`. Ein Versatz im Ausdruck wandert
|
||||
dabei in die Schlüssel, aus `[value_json|int-3]` wird also `{"3":"Default"}`.
|
||||
|
||||
Der Runner übersetzt beim Lesen: aus der gesendeten `2` wird `Charging`. Eine
|
||||
Bedingung vergleicht damit genau den Klartext, den der Editor zur Auswahl
|
||||
stellt. Steht die Zahl nicht in der Tabelle, bleibt sie stehen — ein
|
||||
erfundener Name wäre schlimmer als ein roher Wert.
|
||||
|
||||
Auf beiden Seiten des Editors steckt dieselbe Tabelle, aber der gespeicherte
|
||||
Wert ist ein anderer:
|
||||
|
||||
| | angezeigt | gespeichert |
|
||||
|---|---|---|
|
||||
| **Messwert** (Bedingung) | `Charging` | `Charging` — der Runner hat schon übersetzt |
|
||||
| **Parameter** (Aktion) | `Blink` | `1` — das Gerät will die Zahl |
|
||||
|
||||
Bei WLED trägt die Kommando-Vorlage alles: `{"seg":[{"col":[[%red%,%green%,%blue%]]}]}`
|
||||
wird mit den Parameterwerten gefüllt und am Stück geschickt. Deshalb haben die
|
||||
|
||||
@@ -44,6 +44,7 @@ Tabellen siehe homeMesh_automations.sql, Konfiguration siehe config.ini.example.
|
||||
|
||||
import argparse
|
||||
import configparser
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
@@ -146,13 +147,24 @@ class Regelwerk:
|
||||
states = {}
|
||||
with db.cursor() as c:
|
||||
c.execute("""SELECT s.id, s.state_name, s.url AS state_url, s.value_path,
|
||||
s.current_value,
|
||||
s.current_value, s.possible_values,
|
||||
a.url AS actor_url, a.name AS actor_name, t.type
|
||||
FROM actor_states s
|
||||
JOIN actors a ON a.id = s.actor_id
|
||||
LEFT JOIN state_types t ON s.state_type = t.id""")
|
||||
for row in c.fetchall():
|
||||
row["type"] = row["type"] or "string"
|
||||
# Werttabelle als flaches {gesendeter Wert: Bezeichnung}. In
|
||||
# der Datenbank steht sie als Liste aus Ein-Schluessel-
|
||||
# Objekten, weil der Editor sie so schon versteht.
|
||||
row["wertetabelle"] = {}
|
||||
try:
|
||||
for eintrag in json.loads(row["possible_values"] or "[]"):
|
||||
if isinstance(eintrag, dict):
|
||||
for wert, name in eintrag.items():
|
||||
row["wertetabelle"][str(wert)] = name
|
||||
except ValueError:
|
||||
pass
|
||||
states[row["id"]] = row
|
||||
|
||||
kommandos = {}
|
||||
@@ -418,7 +430,8 @@ class Runner:
|
||||
# Jeder Transport bekommt die Messwerte, fuer die er zustaendig ist.
|
||||
for transport in self.transporte:
|
||||
passende = [{"id": s["id"], "actor_url": s["actor_url"],
|
||||
"state_url": s["state_url"], "value_path": s["value_path"]}
|
||||
"state_url": s["state_url"], "value_path": s["value_path"],
|
||||
"wertetabelle": s["wertetabelle"]}
|
||||
for s in self.regelwerk.states.values()
|
||||
if transport.passt(s["actor_url"])]
|
||||
transport.zustaende_anmelden(passende)
|
||||
|
||||
@@ -67,6 +67,29 @@ def wert_aus_pfad(daten, pfad):
|
||||
return daten
|
||||
|
||||
|
||||
def uebersetze(wert, tabelle):
|
||||
"""
|
||||
Aus der gesendeten Zahl den Zustandsnamen machen: aus "2" wird "Charging".
|
||||
|
||||
Manche Geraete schicken einen Zahlencode und meinen einen Zustand. Welche
|
||||
Zahl welchen Namen hat, steht in possible_values - derselben Spalte, aus
|
||||
der auch der Editor seine Auswahlliste baut. Eine Bedingung vergleicht
|
||||
damit genau den Klartext, den man dort ausgewaehlt hat.
|
||||
|
||||
Steht die Zahl nicht in der Tabelle, bleibt sie stehen: ein erfundener
|
||||
Name waere schlimmer als ein roher Wert.
|
||||
"""
|
||||
if not tabelle:
|
||||
return wert
|
||||
if wert in tabelle:
|
||||
return tabelle[wert]
|
||||
try: # "2.0" und "2" meinen dieselbe Stufe
|
||||
ganz = str(int(float(wert)))
|
||||
except (TypeError, ValueError):
|
||||
return wert
|
||||
return tabelle.get(ganz, wert)
|
||||
|
||||
|
||||
class Transport:
|
||||
"""Gemeinsame Form. Wer nichts zu lesen hat, erbt die leeren Methoden."""
|
||||
|
||||
@@ -101,7 +124,7 @@ class MQTTTransport(Transport):
|
||||
def __init__(self, client, dry_run=False):
|
||||
self.client = client
|
||||
self.dry_run = dry_run
|
||||
self.topics = {} # topic -> [(state_id, value_path), ...]
|
||||
self.topics = {} # topic -> [(state_id, value_path, wertetabelle), ...]
|
||||
self.neu = {} # state_id -> wert
|
||||
|
||||
def zustaende_anmelden(self, states):
|
||||
@@ -109,7 +132,8 @@ class MQTTTransport(Transport):
|
||||
for s in states:
|
||||
if not s["state_url"]:
|
||||
continue
|
||||
self.topics.setdefault(s["state_url"], []).append((s["id"], s.get("value_path")))
|
||||
self.topics.setdefault(s["state_url"], []).append(
|
||||
(s["id"], s.get("value_path"), s.get("wertetabelle") or {}))
|
||||
for topic in self.topics:
|
||||
self.client.subscribe(topic)
|
||||
mehrfach = sum(1 for e in self.topics.values() if len(e) > 1)
|
||||
@@ -126,13 +150,13 @@ class MQTTTransport(Transport):
|
||||
daten = json.loads(text)
|
||||
except ValueError:
|
||||
daten = None # kein JSON - dann gilt der Rohtext
|
||||
for state_id, pfad in eintraege:
|
||||
wert = self._wert(text, daten, pfad)
|
||||
for state_id, pfad, tabelle in eintraege:
|
||||
wert = self._wert(text, daten, pfad, tabelle)
|
||||
if wert is not None:
|
||||
self.neu[state_id] = wert
|
||||
|
||||
@staticmethod
|
||||
def _wert(text, daten, pfad):
|
||||
def _wert(text, daten, pfad, tabelle=None):
|
||||
"""
|
||||
Aus der Nutzlast den Wert eines einzelnen Messwerts machen.
|
||||
|
||||
@@ -141,19 +165,24 @@ class MQTTTransport(Transport):
|
||||
Home-Assistant-Discovery. Ohne Pfad gilt die ganze Nutzlast, und
|
||||
JSON-Skalare werden ausgepackt: manche Geraete schicken 21.4 mit
|
||||
Anfuehrungszeichen, andere true statt ON.
|
||||
|
||||
Steht eine Werttabelle dabei, wird aus der gesendeten Zahl der
|
||||
Zustandsname: aus 2 wird "Charging".
|
||||
"""
|
||||
if daten is None:
|
||||
return text
|
||||
return uebersetze(text, tabelle)
|
||||
try:
|
||||
wert = wert_aus_pfad(daten, pfad)
|
||||
except (KeyError, IndexError, TypeError):
|
||||
logger.debug("Pfad %s nicht in der Nutzlast: %s", pfad, text[:80])
|
||||
return None
|
||||
if isinstance(wert, bool):
|
||||
return "true" if wert else "false"
|
||||
if isinstance(wert, (int, float, str)):
|
||||
return str(wert)
|
||||
return json.dumps(wert, ensure_ascii=False)
|
||||
wert = "true" if wert else "false"
|
||||
elif isinstance(wert, (int, float, str)):
|
||||
wert = str(wert)
|
||||
else:
|
||||
return json.dumps(wert, ensure_ascii=False)
|
||||
return uebersetze(wert, tabelle)
|
||||
|
||||
def zustaende_lesen(self):
|
||||
werte, self.neu = self.neu, {}
|
||||
|
||||
+69
-13
@@ -100,6 +100,43 @@ function lockoutChoices()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Auswahlwerte aus possible_values in eine einheitliche Form bringen.
|
||||
*
|
||||
* In der Datenbank stehen zwei Schreibweisen. Eine schlichte Liste
|
||||
* (["open","close"]) und eine Liste aus Ein-Schluessel-Objekten
|
||||
* ([{"0":"Unknown"},{"1":"Idle"}]) - letztere ueberall dort, wo das Geraet
|
||||
* eine Zahl schickt und einen Zustand meint.
|
||||
*
|
||||
* Was davon der gespeicherte Wert ist, haengt an der Seite:
|
||||
*
|
||||
* Messwert: der Runner uebersetzt die Zahl schon beim Lesen in den
|
||||
* Klartext, eine Bedingung vergleicht also "Charging".
|
||||
* Parameter: das Geraet will die Zahl - gespeichert wird der Schluessel,
|
||||
* angezeigt der Klartext.
|
||||
*/
|
||||
function optionList($json, $klartextAlsWert)
|
||||
{
|
||||
$roh = json_decode($json, true);
|
||||
if (!is_array($roh)) {
|
||||
return [];
|
||||
}
|
||||
$liste = [];
|
||||
foreach ($roh as $eintrag) {
|
||||
if (is_array($eintrag)) {
|
||||
foreach ($eintrag as $wert => $bezeichnung) {
|
||||
$liste[] = [
|
||||
"value" => $klartextAlsWert ? strval($bezeichnung) : strval($wert),
|
||||
"label" => strval($bezeichnung),
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$liste[] = ["value" => strval($eintrag), "label" => strval($eintrag)];
|
||||
}
|
||||
}
|
||||
return $liste;
|
||||
}
|
||||
|
||||
/** Wie das Wertfeld im Editor aussieht. */
|
||||
function inputForType($type, $hasOptions)
|
||||
{
|
||||
@@ -187,10 +224,7 @@ function deviceCatalog()
|
||||
if (!isset($devices[$id])) {
|
||||
continue;
|
||||
}
|
||||
$options = json_decode($row["possible_values"], true);
|
||||
if (!is_array($options)) {
|
||||
$options = [];
|
||||
}
|
||||
$options = optionList($row["possible_values"], true);
|
||||
$type = $row["type"] ?: "string";
|
||||
$devices[$id]["states"][] = [
|
||||
"id" => intval($row["id"]),
|
||||
@@ -198,7 +232,7 @@ function deviceCatalog()
|
||||
"type" => $type,
|
||||
"input" => inputForType($type, count($options) > 0),
|
||||
"operators" => operatorChoices($type),
|
||||
"options" => array_values($options),
|
||||
"options" => $options,
|
||||
"unit" => $row["unit"],
|
||||
"value" => $row["current_value"],
|
||||
"url" => $row["url"],
|
||||
@@ -232,10 +266,7 @@ function deviceCatalog()
|
||||
if (!isset($commands[$cmdId])) {
|
||||
continue;
|
||||
}
|
||||
$options = json_decode($row["possible_values"], true);
|
||||
if (!is_array($options)) {
|
||||
$options = [];
|
||||
}
|
||||
$options = optionList($row["possible_values"], false);
|
||||
$type = $row["type"] ?: "string";
|
||||
// Das passende Kommando im Geraet suchen. Wenige Kommandos je
|
||||
// Geraet, deshalb reicht die lineare Suche.
|
||||
@@ -246,7 +277,7 @@ function deviceCatalog()
|
||||
"name" => $row["parameter_name"],
|
||||
"type" => $type,
|
||||
"input" => inputForType($type, count($options) > 0),
|
||||
"options" => array_values($options),
|
||||
"options" => $options,
|
||||
"min" => $row["min_value"] === null ? null : floatval($row["min_value"]),
|
||||
"max" => $row["max_value"] === null ? null : floatval($row["max_value"]),
|
||||
"url" => $row["url"],
|
||||
@@ -564,6 +595,7 @@ function listAutomations($floor)
|
||||
|
||||
$namesState = stateNames();
|
||||
$namesCommand = commandNames();
|
||||
$labelsParam = parameterLabels();
|
||||
|
||||
$list = [];
|
||||
foreach ($ids as $id) {
|
||||
@@ -572,7 +604,7 @@ function listAutomations($floor)
|
||||
continue;
|
||||
}
|
||||
$auto["conditionText"] = describeConditions($auto["conditions"], $namesState);
|
||||
$auto["actionText"] = describeActions($auto["actions"], $namesCommand);
|
||||
$auto["actionText"] = describeActions($auto["actions"], $namesCommand, $labelsParam);
|
||||
$list[] = $auto;
|
||||
}
|
||||
return $list;
|
||||
@@ -597,6 +629,25 @@ function stateNames()
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* id => [gespeicherter Wert => Bezeichnung] fuer alle Kommando-Parameter,
|
||||
* die eine Auswahl haben. Gespeichert wird bei Parametern die Zahl, die das
|
||||
* Geraet erwartet - in der Uebersicht soll aber "Blink" stehen und nicht "1".
|
||||
*/
|
||||
function parameterLabels()
|
||||
{
|
||||
$db = meshDb();
|
||||
$res = $db->query("SELECT id, possible_values FROM command_parameters
|
||||
WHERE possible_values <> '' AND possible_values <> '[]'");
|
||||
$labels = [];
|
||||
while ($row = $res->fetch_assoc()) {
|
||||
foreach (optionList($row["possible_values"], false) as $option) {
|
||||
$labels[intval($row["id"])][$option["value"]] = $option["label"];
|
||||
}
|
||||
}
|
||||
return $labels;
|
||||
}
|
||||
|
||||
/** id => "Geraet: Kommando" fuer alle Kommandos. */
|
||||
function commandNames()
|
||||
{
|
||||
@@ -642,12 +693,17 @@ function describeConditions($conditions, $names)
|
||||
return implode(" oder ", $parts);
|
||||
}
|
||||
|
||||
function describeActions($actions, $names)
|
||||
function describeActions($actions, $names, $labels = [])
|
||||
{
|
||||
$parts = [];
|
||||
foreach ($actions as $a) {
|
||||
$text = $names[$a["command_id"]] ?? ("Kommando " . $a["command_id"]);
|
||||
$values = array_values($a["params"]);
|
||||
$values = [];
|
||||
foreach ($a["params"] as $parameterId => $wert) {
|
||||
// Wo es eine Auswahl gibt, die Bezeichnung nennen: "Blink" sagt
|
||||
// mehr als die 1, die das Geraet tatsaechlich bekommt.
|
||||
$values[] = $labels[intval($parameterId)][$wert] ?? $wert;
|
||||
}
|
||||
if ($values) {
|
||||
$text .= " (" . implode(", ", $values) . ")";
|
||||
}
|
||||
|
||||
@@ -473,14 +473,15 @@ class DatabaseManager:
|
||||
for state in states:
|
||||
state_query = """
|
||||
INSERT INTO actor_states
|
||||
(actor_id, state_name, state_type, current_value, unit, url, value_path)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s)
|
||||
(actor_id, state_name, state_type, current_value, unit, url, value_path, possible_values)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
state_type = VALUES(state_type),
|
||||
current_value = VALUES(current_value),
|
||||
unit = VALUES(unit),
|
||||
url = VALUES(url),
|
||||
value_path = VALUES(value_path)
|
||||
value_path = VALUES(value_path),
|
||||
possible_values = VALUES(possible_values)
|
||||
"""
|
||||
state_name = state.get('name', '')
|
||||
state_type = self.PARAMETER_TYPES_MAPPING.get(state.get('type', 0), 4)
|
||||
@@ -488,8 +489,9 @@ class DatabaseManager:
|
||||
unit = state.get('unit')
|
||||
state_url = state.get('url')
|
||||
value_path = state.get('value_path')
|
||||
possible_vals = json.dumps(state.get('values')) if 'values' in state else ""
|
||||
|
||||
cursor.execute(state_query, (sensor_id, state_name, state_type, current_value, unit, state_url, value_path))
|
||||
cursor.execute(state_query, (sensor_id, state_name, state_type, current_value, unit, state_url, value_path, possible_vals))
|
||||
|
||||
self.connection.commit()
|
||||
cursor.close()
|
||||
|
||||
@@ -549,6 +549,40 @@ class MQTTDeviceConverter:
|
||||
pfad += teil if teil.startswith("[") else (("." + teil) if pfad else teil)
|
||||
return pfad[:120]
|
||||
|
||||
@staticmethod
|
||||
def _value_map(config: Dict) -> Optional[List[Dict]]:
|
||||
"""
|
||||
Liest eine Werttabelle aus dem value_template.
|
||||
|
||||
Manche Geraete schicken eine Zahl und meinen einen Zustand:
|
||||
|
||||
{{ ['Unknown','Idle','Charging','WaitCar','Complete','Error'][value_json|int ]}}
|
||||
{{ ['Default','Eco','NextTrip'][value_json|int-3]}}
|
||||
|
||||
Das ist kein Pfad in die Nutzlast, sondern eine Uebersetzung von Zahl
|
||||
nach Text. Zurueck kommt sie in der Schreibweise, die WLED fuer seine
|
||||
Effektliste schon benutzt - eine Liste aus {Wert: Bezeichnung} -,
|
||||
denn die versteht der Editor bereits und macht eine Auswahlliste
|
||||
daraus.
|
||||
|
||||
Der Versatz im Ausdruck wandert dabei in die Schluessel: bei
|
||||
"value_json|int-3" gehoert der erste Eintrag zur gesendeten 3.
|
||||
"""
|
||||
vorlage = config.get('value_template') or config.get('val_tpl')
|
||||
if not vorlage:
|
||||
return None
|
||||
treffer = re.search(
|
||||
r"\[\s*((?:'[^']*'|\"[^\"]*\")(?:\s*,\s*(?:'[^']*'|\"[^\"]*\"))*)\s*\]"
|
||||
r"\s*\[\s*value_json\s*\|\s*int\s*([+-]\s*\d+)?\s*\]",
|
||||
vorlage)
|
||||
if not treffer:
|
||||
return None
|
||||
bezeichnungen = [t[1:-1] for t in
|
||||
re.findall(r"'[^']*'|\"[^\"]*\"", treffer.group(1))]
|
||||
versatz = int(treffer.group(2).replace(' ', '')) if treffer.group(2) else 0
|
||||
# index = wert + versatz -> wert = index - versatz
|
||||
return [{str(i - versatz): name} for i, name in enumerate(bezeichnungen)]
|
||||
|
||||
@staticmethod
|
||||
def _entity_to_states(component: str, object_id: str, config: Dict) -> List[Dict]:
|
||||
"""
|
||||
@@ -585,6 +619,9 @@ class MQTTDeviceConverter:
|
||||
'url': state_topic,
|
||||
'value_path': MQTTDeviceConverter._value_path(config)
|
||||
}
|
||||
wertetabelle = MQTTDeviceConverter._value_map(config)
|
||||
if wertetabelle:
|
||||
state_entry['values'] = wertetabelle
|
||||
|
||||
# Unit hinzufügen - verschiedene mögliche Feldnamen
|
||||
unit = (
|
||||
@@ -610,6 +647,11 @@ class MQTTDeviceConverter:
|
||||
elif component == 'sensor':
|
||||
# Bei Sensor den Typ aus value_template ableiten oder number annehmen
|
||||
state_entry['type'] = 'number' # Default für Sensoren
|
||||
|
||||
# Eine Werttabelle schlaegt den geratenen Typ: was beim Geraet als
|
||||
# Zahl ankommt, ist fuer den Benutzer ein Zustandsname.
|
||||
if 'values' in state_entry:
|
||||
state_entry['type'] = 'string'
|
||||
|
||||
states.append(state_entry)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user