RHASSPY/Vertiefung: Unterschied zwischen den Versionen

Aus FHEMWiki
("must match"-Prinzip ergänzt)
(Hinweis auf words-Page; Formatierung)
Zeile 16: Zeile 16:


== Grundlegende Strukturierungsmerkmale ==
== Grundlegende Strukturierungsmerkmale ==
Für die Sprachsteuerung ist es wichtig, dass die Geräte möglichst eingängig und innerhalb nachvollziehbarer Strukturen angesprochen werden können. Alle verwendeten Begriffe, insbesondere die eigentlichen Geräte-''Namen'' sollten daher folgende Bedingungen erfüllen:  
{{Randnotiz|RNTyp=Info|RNText=Es ist zu empfehlen, hin und wieder auch zu überprüfen, ob alle Wörter auch für Rhasspy ''hörbar'' sind. Dazu ist die [https://rhasspy.readthedocs.io/en/latest/usage/#words-page Words-Seite] in der Rhasspy-Konfiguration aufzurufen, mit derem Hilfe man bisher unbekannte Wörter phonetisch beschreiben kann.}}Für die Sprachsteuerung ist es wichtig, dass die Geräte möglichst eingängig und innerhalb nachvollziehbarer Strukturen angesprochen werden können. Alle verwendeten Begriffe, insbesondere die eigentlichen Geräte-''Namen'' sollten daher folgende Bedingungen erfüllen:  
Sie sollten  
Sie sollten  
- sprechbar
* '''sprechbar'''
- hinreichend eindeutig und
* hinreichend '''eindeutig''' und
- eingängig
* '''eingängig'''
sein. Doppelbelegungen sind zwar möglich, es sollte aber für die Software auf der jeweiligen Stufe möglich sein, ein eindeutiges Ergebnis zu ermitteln. Dies geschieht in der Regel über den Kontext, in dem etwas gesagt wird - ganz wie im richtigen Leben macht es einen Unterschied, ob die Anweisung "Mach das Licht aus" im ''Wohnzimmer'' oder im ''Esszimmer'' erfolgt. Die Orts-Information ergibt sich hier z.B. aus dem Ort, an dem sich der Empfänger der Anweisung befindet.
sein. Doppelbelegungen sind zwar zulässig, es sollte aber für die Software auf der jeweiligen Stufe möglich sein, ein eindeutiges Ergebnis zu ermitteln. Dies geschieht in der Regel über den Kontext, in dem etwas gesagt wird - ganz wie im richtigen Leben macht es einen Unterschied, ob die Anweisung "Mach das Licht aus" im ''Wohnzimmer'' oder im ''Esszimmer'' erfolgt. Die Orts-Information ergibt sich hier z.B. aus dem Ort, an dem sich der Empfänger der Anweisung befindet.


=== Optionale Elemente in sentences.ini ===
=== Optionale Elemente in sentences.ini ===
Zeile 50: Zeile 50:


Damit innerhalb des RHASSPY-Codes Verwechslungsgefahren minimiert werden, werden  
Damit innerhalb des RHASSPY-Codes Verwechslungsgefahren minimiert werden, werden  
- alle intern verwendeten Bezeichnungen '''klein gemacht''', also insbesondere alle Namen, Gruppen- und Raumnamen werden nach devicemap in Kleinschreibung übernommen (und auch so an Rhasspy übermittelt),
* alle intern verwendeten Bezeichnungen '''klein gemacht''', also insbesondere alle Namen, Gruppen- und Raumnamen werden nach devicemap in Kleinschreibung übernommen (und auch so an Rhasspy übermittelt),
- '''englische Schlüsselwörter''' für typische Kommandos verwendet (also z.B. ''on'' und ''off'')
* '''englische Schlüsselwörter''' für typische Kommandos verwendet (also z.B. ''on'' und ''off'')
- '''Farbwerte''' und ähnliches '''nummerisch''' übergeben
* '''Farbwerte''' und ähnliches '''nummerisch''' übergeben
Tauchen irgendwo Großbuchstaben auf, handelt es sich in der Regel um ein Schlüsselwort wie ''Value'' oder ''Room'', dem dann ein Wert zugewiesen ist.
Tauchen irgendwo Großbuchstaben auf, handelt es sich in der Regel um ein Schlüsselwort wie ''Value'' oder ''Room'', dem dann ein Wert zugewiesen ist.


=== "must match"-Prinzip ===
=== "must match"-Prinzip ===
Eine Besonderheit der in den Standardeinstellungen bei Rhasspy verwendeten ''speech-to-text-engine'' besteht darin, dass diese '''zwingend ein Ergebnis''' liefern muss. Daher sollten folgende Aspekte bei der Gestaltung der ''sentences.ini'' berücksichtigt werden:
Eine Besonderheit der in den Standardeinstellungen bei Rhasspy verwendeten ''speech-to-text-engine'' besteht darin, dass diese '''zwingend ein Ergebnis''' liefern muss. Daher sollten folgende Aspekte bei der Gestaltung der ''sentences.ini'' berücksichtigt werden:
- alle nicht zwingend erforderlichen Angaben sollten in optionaler Form notiert werden
* alle nicht zwingend erforderlichen Angaben sollten in optionaler Form notiert werden
- Stille wird auf den kürzesten verfügbaren Satz gemappt. Dieser kürzeste Satz sollte '''unbedingt dem Intent ''CancelAction'' zugeordnet''' sein!
* Stille wird auf den kürzesten verfügbaren Satz gemappt. Dieser kürzeste Satz sollte '''unbedingt dem Intent ''CancelAction'' zugeordnet''' sein!


=== Namen ===
=== Namen ===
Zeile 83: Zeile 83:


=== Gruppen vs. Räume ===
=== Gruppen vs. Räume ===
Worin besteht nun der Unterschied zwischen Gruppen und Räumen? Räume sind eher dazu gedacht, Geräte örtlich zu kennzeichnen, Gruppen sind dagegen eher funktional, und die Wirkung eines Gruppenbefehls ist in RHASSPY in der Regel (s.u.) begrenzt durch den Raum, der jeweils mit angesprochen wird. Ist in dem von Rhasspy an RHASSPY übergebenen Datensatz keine ausdrückliche ''{Room}''-Information enthalten, wird  RHASSPY daher in der Regel zunächst versuchen, dies aus der in der ''siteId'' enthaltenen Ortsinformation abzuleiten, und dann erst die Suche außerhalb des Raums fortsetzen, wenn dies nicht möglich ist. Natürlich ist es möglich, auch in der Gruppenbezeichnung eine Ortsinformation mitzugeben, indem (zusätzliche) Gruppen gesetzt werden wie "lichter beim sofa", oder die Ortsinfo "beim sofa" als zusätzlichen Raum anzugeben (was aber dann dazu führt, dass "lichter beim sofa im wohnzimmer" uU. nicht zum gewünschten Ergebnis führt.   
Worin besteht nun der Unterschied zwischen Gruppen und Räumen? Räume sind eher dazu gedacht, Geräte örtlich zu kennzeichnen, Gruppen sind dagegen eher funktional, und die Wirkung eines Gruppenbefehls ist in RHASSPY in der Regel (s.u.) begrenzt durch den Raum, der jeweils mit angesprochen wird. Ist in dem von Rhasspy an RHASSPY übergebenen Datensatz keine ausdrückliche ''{Room}''-Information enthalten, wird  RHASSPY daher in der Regel zunächst versuchen, dies aus der in der ''siteId'' enthaltenen Ortsinformation abzuleiten, und dann erst die Suche außerhalb des Raums fortsetzen, wenn dies nicht möglich ist. Natürlich ist es zulässig, auch in der Gruppenbezeichnung eine Ortsinformation mitzugeben, indem (zusätzliche) Gruppen gesetzt werden wie "lichter beim sofa", oder die Ortsinfo "beim sofa" als zusätzlichen Raum anzugeben (was aber dann dazu führt, dass "lichter beim sofa im wohnzimmer" uU. nicht zum gewünschten Ergebnis führt.   


=== Gruppen, Räume und Funktionalitäten als ''slots'' ===
=== Gruppen, Räume und Funktionalitäten als ''slots'' ===
Die sich aus diesen ganzen ''Meta-Informationen'' ergebenden "sprechbaren" Informationsanteilen führt RHASSPY für Rhasspy zu einer Vielzahl von ''slots'' zusammen, mit deren Hilfe dann die Kombinationsmöglichkeiten für die eigentliche Spracherkennung soweit eingeschränkt werden kann, dass im Optimalfall dann nur (aus Sicht von RHASSPY) gültige Datensätze zurückkommen, die dann auch ausgeführt werden können. Es empfiehlt sich daher, sich etwas intensiver mit diesen ''slots'' zu befassen, die sich ergeben, sobald man die ersten paar Geräte in der ''devicemap'' sauber strukturiert hat.  
Die sich aus diesen ganzen ''Meta-Informationen'' ergebenden "sprechbaren" Informationsanteilen führt RHASSPY für Rhasspy zu einer Vielzahl von ''slots'' zusammen, mit deren Hilfe dann die Kombinationsmöglichkeiten für die eigentliche Spracherkennung soweit eingeschränkt werden kann, dass im Optimalfall dann nur (aus Sicht von RHASSPY) gültige Datensätze zurückkommen, die dann auch ausgeführt werden können. Es empfiehlt sich daher, sich etwas intensiver mit diesen ''slots'' zu befassen, die sich ergeben, sobald man die ersten paar Geräte in der ''devicemap'' sauber strukturiert hat. Sind in den ''slots'' Informationen enthalten, die so nicht erwartet wurden, kann man diese in der Regel über die ''devicemap'' im rhasspy-list zurückverfolgen und muss dann eben die entsprechenden Attribute anpassen bzw. ggf. den ''ignoreKeywords''-Filter in ''rhasspyTweaks'' anpassen.


== Gruppen-Intents ==
== Gruppen-Intents ==

Version vom 7. Dezember 2021, 09:12 Uhr


Clock - Under Construction.svg An dieser Seite wird momentan noch gearbeitet.


Erste Schritte

Diese Seite knüpft an die Schnellstart-Anleitung an, welche die ersten grundlegenden Schritte einfach erklärt. Es wird dabei davon ausgegangen, dass das Feature "useGenericAttrs" aktiv ist.

Info green.pngFalls bereits eine andere Sprachsteuerungslösung installiert ist, kann es sein, dass RHASSPY bereits sehr viele Geräte in der devicemap enthält. Wem dies zu unübersichtlich ist, kann den devspec-Parameter in der DEF z.B. so anpassen, um z.B. zunächst nur Geräte im Wohnzimmer zu erfassen: devspec=room=Wohnzimmer:FILTER=genericDeviceType=.+,room=Esszimmer:FILTER=genericDeviceType=.+. Dies kann später ohne weiteres wieder erweitert werden.

Ziel dieser Vertiefung ist zu zeigen, wie man auf einfachem Weg Sprachbefehle umsetzen kann wie - "schalte alle Lichter beim Sofa an" - "schließe die Rollläden" (gemeint: im Wohnzimmer) - "mach überall die Lichter aus"

Es empfiehlt sich für diese Vertiefung, einige wenige, jeweils gleichartige Geräte in einem oder besser zwei Räumen zu betrachten, also z.B. vier Leuchten (genericDeviceType light) und einige Rollläden oder Jalousien (blind). Im Folgenden wird unterstellt, dass je zwei light und blind-Geräte im Wohn- und Esszimmer vorhanden sind.

Die devicemap

Im Schnellstart im Abschnitt Ein Gerät mit Rhasspy verbinden wurde bereits darauf hingewiesen, dass mit Hilfe des Befehls list rhasspy in Erfahrung gebracht werden kann, welche "Eigenschaften" die automatische Erkennung für die einzelnen Geräte erkannt hat. Wenn also im Folgenden Änderungen vorgenommen werden, empfiehlt es sich dann jeweils nach der Änderung ein set rhasspy update devicemap_only durchzuführen und dann die Auswirkungen im list bei rhasspy zu betrachten. Sind alle Änderungen dann soweit ok, können diese mit set rhasspy update devicemap an Rhasspy übergeben und das Training veranlasst werden.

Grundlegende Strukturierungsmerkmale

Info green.pngEs ist zu empfehlen, hin und wieder auch zu überprüfen, ob alle Wörter auch für Rhasspy hörbar sind. Dazu ist die Words-Seite in der Rhasspy-Konfiguration aufzurufen, mit derem Hilfe man bisher unbekannte Wörter phonetisch beschreiben kann.

Für die Sprachsteuerung ist es wichtig, dass die Geräte möglichst eingängig und innerhalb nachvollziehbarer Strukturen angesprochen werden können. Alle verwendeten Begriffe, insbesondere die eigentlichen Geräte-Namen sollten daher folgende Bedingungen erfüllen: Sie sollten

  • sprechbar
  • hinreichend eindeutig und
  • eingängig

sein. Doppelbelegungen sind zwar zulässig, es sollte aber für die Software auf der jeweiligen Stufe möglich sein, ein eindeutiges Ergebnis zu ermitteln. Dies geschieht in der Regel über den Kontext, in dem etwas gesagt wird - ganz wie im richtigen Leben macht es einen Unterschied, ob die Anweisung "Mach das Licht aus" im Wohnzimmer oder im Esszimmer erfolgt. Die Orts-Information ergibt sich hier z.B. aus dem Ort, an dem sich der Empfänger der Anweisung befindet.

Optionale Elemente in sentences.ini

Im Schnellstart wurde folgender Intent für das Ein- und Ausschalten vorgestellt:

[de.fhem:SetOnOff]
schalte das $de.fhem.Device-SetOnOff{Device} ( an{Value:on} | aus{Value:off})

Demgegenüber enthält der Abschnitt SetOnOff zu den einzelnen Intens eine deutlich erweiterte Variante:

[de.fhem:SetOnOff]
(schalte|schalt|mache|mach|stelle|stell|starte) [den|die|das] $de.fhem.Device-SetOnOff{Device} [[(im|in dem|auf dem|in der|auf der)] $de.fhem.Room{Room}] (an|ein){Value:on}
(schalte|schalt|mache|mach|stelle|stell) [den|die|das] $de.fhem.Device-SetOnOff{Device} [[(im|in dem|auf dem|in der|auf der)] $de.fhem.Room{Room}] aus{Value:off}
(fahre|fahr) [den|die|das] $de.fhem.Device-blind{Device} [[(im|in dem|auf dem|in der|auf der)] $de.fhem.Room{Room}] ((hoch|auf){Value:on}|(zu|runter){Value:off})

Spätestens jetzt ist zu empfehlen, das Informationsvideo zu den Optionen anzusehen, die sentences.ini innerhalb Rhasspy bietet! Hier ist bereits zu erkennen, dass es sinnvoll ist - optionale Zusatzinformationen (sprechend) ergänzen zu können (oder diese eben auszulassen) - bestimmte Wortkombinationen auf die Geräte-Typen zu beschränken, für die das sprachlich überhaupt paßt.

"Öffnen" oder "schließen" kann man ein Licht in der Regel nicht, einen Rollladen oder eine Tür sehr wohl. Unterstellt, es sind keine automtatischen Türöffner vorhanden, könnte vielleicht folgende Ergänzung sinnvoll sein:

 (öffne{Value:on}|schliesse{Value:off})[den|die|das] $de.fhem.Device-blind{Device} [[(im|in dem|auf dem|in der|auf der)] $de.fhem.Room{Room}]

Werden alle drei Datenelemente (Value, Device und Room) übergeben, sollte es für RHASSPY möglich sein, ein FHEM-Device zu ermitteln, auf das die beiden wesentlichen Informationen (Device und Room) paßt. Fehlt Room, bleibt immer noch die Möglichkeit, dass RHASSPY die fehlende Information ergänzen kann - insbesondere, indem ausgewertet wird, wo die Information herkam.

Damit innerhalb des RHASSPY-Codes Verwechslungsgefahren minimiert werden, werden

  • alle intern verwendeten Bezeichnungen klein gemacht, also insbesondere alle Namen, Gruppen- und Raumnamen werden nach devicemap in Kleinschreibung übernommen (und auch so an Rhasspy übermittelt),
  • englische Schlüsselwörter für typische Kommandos verwendet (also z.B. on und off)
  • Farbwerte und ähnliches nummerisch übergeben

Tauchen irgendwo Großbuchstaben auf, handelt es sich in der Regel um ein Schlüsselwort wie Value oder Room, dem dann ein Wert zugewiesen ist.

"must match"-Prinzip

Eine Besonderheit der in den Standardeinstellungen bei Rhasspy verwendeten speech-to-text-engine besteht darin, dass diese zwingend ein Ergebnis liefern muss. Daher sollten folgende Aspekte bei der Gestaltung der sentences.ini berücksichtigt werden:

  • alle nicht zwingend erforderlichen Angaben sollten in optionaler Form notiert werden
  • Stille wird auf den kürzesten verfügbaren Satz gemappt. Dieser kürzeste Satz sollte unbedingt dem Intent CancelAction zugeordnet sein!

Namen

Helper für eine Lampe im RHASSPY Modul: Unter dem technischen Namen ist der alias aufgelistet, darunter ggf. die weiteren Namen, unter denen das Gerät ansprechbar ist

Dem Umstand, dass die üblichen FHEM-Gerätenamen in der Regel nicht sprechbar sind, kann man dadurch begegnen, dass man einfach sprechbare Namen vergibt. Wie im wiklichen Leben ist es dabei denkbar, dass etwas unter mehreren Bezeichnungen bekannt ist. Im nebenstehenden rhasspy-list für das Gerät "Office" ist zu erkennen, dass dieses einen alias namens licht hat. Diese Information könnte entweder aus dem gleichnamigen (allgemeinen) Attribut kommen, es könnte aber auch die Folge davon sein, dass z.B. ein alexaName o.ä. vergeben ist, oder diese Bezeichnung im Attribut rhasspyName zu finden ist. Der Schlüssel alias wird immer nur eine (Haupt-) Bezeichnung enthalten, der weitere Schlüssel names kann eine im Prinzip unbeschränkter Werte enthalten.

Die Namen müssen nicht eindeutig sein, jedenfalls dann nicht, wenn sich aus dem Kontext ableiten läßt, welches Gerät gemeint ist. Es spricht also nichts dagegen, jeweils einen alias rollladen zu vergeben, wenn sich in einem Raum jeweils nur ein einziger befindet. Entsprechendes gilt für Lichter, wobei es durchaus zulässig ist, z.B. die Hauptbeleuchtung mit licht zu bezeichnen. Wird das Attribut rhasspyName verwendet, um diese Werte zu erzeugen, wird der erste angegebene Name als alias verwendet.

Exkurs: Die allgemeine Logik der rhasspy-Attribute

Es empfiehlt sich, bei einem Device nachzuvollziehen, wie ggf. vorhandene alias-, siriName- (etc.) und rhasspyName-Attribute zusammenwirken. RHASSPY wird dabei immer die eigenen Attribute bevorzugen, falls diese gesetzt sind, mit der Wirkung, dass andere, automatisch ermittelten Werte vollständig verdrängt werden. Dies gilt genauso z.B. für rhasspyMapping. Dieses Prinzip ist an vielen Stellen in RHASSPY anzutreffen. So gibt es teils Möglichkeiten, die man entweder allgemein in rhasspyTweaks einstellen kann, oder speziell für einzelne Devices in rhasspySpecials. Auch in diesen Fällen wird die spezifischere Angabe am Device die generelle Vorgabe (an rhasspy) verdrängen.

Gruppen

Um eine Anweisung wie das oben genannte "schließe die Rollläden" umzusetzen, muss RHASSPY wissen, dass es (uU.) mehrere Geräte gibt, die unter einer Sammelbezeichnung angesprochen werden können. Ist also ein group-Attribut vergeben, wird diese Angabe dann auch im rhasspy-list zu finden sein. Um die automatische Gruppen-Zuordnung zu verbessern, stehen zwei Schlüssel für das Attribut rhasspyTweaks zur Verfügung:

attr rhasspy rhasspyTweaks ignoreKeywords=rooms=MQTT.*|alexa|homebridge|googleassistant|Steuerung.* groups=Türen.und.Fenster|Schalter\
gdt2groups= blind=rollläden,rollladen thermostat=heizkörper light=lichter,leuchten

Der erste ignoreKeywords bewirkt (u.a.), dass bestimmte, im allgemeinen Attribut group vorzufindende Gruppennamen nicht ausgewertet werden, der zweite gdt2groups führt dazu, dass für die dirt genannten genericDeviceType bestimmte Gruppennamen automatisch vergeben werden. Auch hier kann diese generelle Vorgabe durch das Setzen des speziellen Attributs rhasspyGroup überschrieben werden, so dass dann z.B. eine Leinwand (gDT blind) dann auch wieder aus den Gruppen rollläden,rollladen genommen werden kann und z.B. der Gruppe mediengeräte zugeordnet werden könnte.

Räume

Das Vorgenannte gilt für Räume (allgemeines Attribut room vs- rhasspyRooms) entsprechend, wobei der erste in rhasspyRooms angegebene Raum dann ggf. wieder eine Sonderfunktion hat und daher dem Hauptraum entsprechen sollte, dem man dieses Gerät örtlich hauptsächlich zuordnet.

Gruppen vs. Räume

Worin besteht nun der Unterschied zwischen Gruppen und Räumen? Räume sind eher dazu gedacht, Geräte örtlich zu kennzeichnen, Gruppen sind dagegen eher funktional, und die Wirkung eines Gruppenbefehls ist in RHASSPY in der Regel (s.u.) begrenzt durch den Raum, der jeweils mit angesprochen wird. Ist in dem von Rhasspy an RHASSPY übergebenen Datensatz keine ausdrückliche {Room}-Information enthalten, wird RHASSPY daher in der Regel zunächst versuchen, dies aus der in der siteId enthaltenen Ortsinformation abzuleiten, und dann erst die Suche außerhalb des Raums fortsetzen, wenn dies nicht möglich ist. Natürlich ist es zulässig, auch in der Gruppenbezeichnung eine Ortsinformation mitzugeben, indem (zusätzliche) Gruppen gesetzt werden wie "lichter beim sofa", oder die Ortsinfo "beim sofa" als zusätzlichen Raum anzugeben (was aber dann dazu führt, dass "lichter beim sofa im wohnzimmer" uU. nicht zum gewünschten Ergebnis führt.

Gruppen, Räume und Funktionalitäten als slots

Die sich aus diesen ganzen Meta-Informationen ergebenden "sprechbaren" Informationsanteilen führt RHASSPY für Rhasspy zu einer Vielzahl von slots zusammen, mit deren Hilfe dann die Kombinationsmöglichkeiten für die eigentliche Spracherkennung soweit eingeschränkt werden kann, dass im Optimalfall dann nur (aus Sicht von RHASSPY) gültige Datensätze zurückkommen, die dann auch ausgeführt werden können. Es empfiehlt sich daher, sich etwas intensiver mit diesen slots zu befassen, die sich ergeben, sobald man die ersten paar Geräte in der devicemap sauber strukturiert hat. Sind in den slots Informationen enthalten, die so nicht erwartet wurden, kann man diese in der Regel über die devicemap im rhasspy-list zurückverfolgen und muss dann eben die entsprechenden Attribute anpassen bzw. ggf. den ignoreKeywords-Filter in rhasspyTweaks anpassen.

Gruppen-Intents

Beispiel SetOnOffGroup

Ist die devicemap also soweit vorbereitet, dass sprechbare Namen vergeben sind und Gruppen- und Raumstrukturen (sprechbar) beschrieben sind, können wir den ersten Gruppen-Intent anlegen:

[de.fhem:SetOnOffGroup]
(schalte|schalt|mache|mach|stelle|stell|starte) ([überall{Room:global} [die]] | alle | sämtliche ) $de.fhem.Group-SetOnOff{Group} [[(im|in dem|auf dem|in der|auf der)] $de.fhem.Room {Room}] (an|ein){Value:on}
(schalte|schalt|mache|mach|stelle|stell) ([überall{Room:global} [die]] | alle | sämtliche ) $de.fhem.Group-SetOnOff{Group} [[(im|in dem|auf dem|in der|auf der)] $de.fhem.Room{Room}] aus{Value:off}
(fahre|fahr|mach|mache) ([überall{Room:global} [die]] | alle | sämtliche ) $de.fhem.Group-blind{Group} [[(im|in dem|auf dem|in der|auf der)] $de.fhem.Room{Room}] ((hoch|auf){Value:on}|(zu|runter){Value:off})

Die Unterscheidung zu dem einfachen SetOnOff-Intent besteht dabei sprachlich zum einen in den Gruppennamen, und zum anderen in der zusätzlich erforderlichen Angabe, ob überall oder alle bzw. sämtliche Geräte angesprochen werden sollen. global ist dabei ein spezieller Raumname, der schlicht alle passenden Geräte umfasst.

Timing-Aspekte

tbd

Andere Gruppenfunktionen in FHEM

tbd structure, Hardware-Strukturen

Nummerische Werte

SetNumerich

Farben

Links