Zustand-Templates
Struktur Templates
Neben der vom Plugin bereitgestellten Möglichkeit, Templates zu definieren (siehe weiter unten),
bietet sich ab smarthomeNG 1.6 das struct
Attribut an. Zum einen können in der Datei etc/struct.yaml
eigene Vorlagen definiert werden, zum anderen stellt das Plugin einige Vorlagen fix fertig bereit. Sie
können wie folgt eingebunden werden:
#items/item.yaml
beispiel:
trigger:
type: bool
enforce_updates: True
raffstore1:
automatik:
struct:
- stateengine.general
- stateengine.state_lock
- stateengine.state_suspend
- stateengine_default_raffstore #beispielsweise eine in etc/struct.yaml angelegte Vorlage
manuell:
# Weitere Attribute werden bereits über das Template stateengine.state_suspend geladen
eval_trigger:
- beispiel.raffstore1.aufab
- beispiel.raffstore1.step
- beispiel.raffstore1.hoehe
- beispiel.raffstore1.lamelle
se_manual_exclude:
- KNX:y.y.y:1/2/3 # konkrete Gruppenadresse eines konkreten KNX Geräts
- Init:*
rules:
eval_trigger:
- ..lock
- ..supsend
- .. release
- beispiel.trigger
additional_state1:
type: foo
Unter SmarthomeNG Version 1.7.0 werden die eval_trigger Angaben aus den einzelnen Struct-Vorgaben nicht kumuliert. Es ist daher wichtig, die eval_trigger Liste nochmals manuell im endgültigen Item anzulegen. Bei neueren Versionen kann man sich das erneute Listen der Trigger wie lock, suspend und release sparen, da diese bereits im struct des Plugins gelistet sind.
Die Vorlagen beinhalten folgende Strukturen:
general
Die general
Vorlage enthält die Items, die generell für einen Zustandsautomaten
angelegt werden sollten. Das „rules“ Item ist das Regelwerk-Item mit aktiviertem
se_plugin. Dieser Codeblock wird zwingend von jedem Zustandsautomaten benötigt.
#stateengine.general
state_id:
# The id/path of the actual state is assigned to this item by the stateengine
type: str
visu_acl: r
cache: True
state_name:
# The name of the actual state is assigned to this item by the stateengine
type: str
visu_acl: r
cache: True
conditionset_id:
remark: The id/path of the actual condition set is assigned to this item by the stateengine
type: str
visu_acl: r
cache: True
conditionset_name:
remark: The name of the actual condition set is assigned to this item by the stateengine
type: str
visu_acl: r
cache: True
rules:
name: Regeln und Item Verweise für den Zustandsautomaten
type: bool
se_plugin: active
eval: True
# se_startup_delay: 30
# se_repeat_actions: true
# se_suspend_time: 7200
se_laststate_item_id: ..state_id
se_laststate_item_name: ..state_name
se_lastconditionset_item_id: ..conditionset_id
se_lastconditionset_item_name: ..conditionset_name
lock
Die state_lock
Vorlage beinhaltet zum einen den Lock Zustand mit dem Namen „gesperrt“,
zum anderen ein Item mit dem Namen lock
. Wird dieses auf „1/True“ gesetzt, wird der
Zustand eingenommen. Der Zustand sollte immer als erster Zustand eingebunden werden.
#stateengine.state_lock
lock:
type: bool
knx_dpt: 1
visu_acl: rw
cache: 'on'
rules:
se_item_lock: ..lock
eval_trigger:
- ..lock
lock:
name: gesperrt
on_leave:
se_action_lock:
- 'function: set'
- 'to: False'
enter:
se_value_lock: True
suspend
Die state_suspend
Vorlage dient dem Abfragen von manuellen Tätigkeiten, wie
z.B. Schalten eines Lichts oder Fahren einer Jalousie mittels Taster oder Visu.
In diesem Fall soll die automatiche Evaluierung für eine gewisse Zeit pausieren.
Beim manuell
Item muss unter Umständen der Eintrag se_manual_exclude
in der eigenen
Baumstruktur überschrieben und durch einen Eintrag (z.B. beim Einsatz von KNX Aktoren) - KNX:physikalische Adresse:Gruppenadresse
ergänzt werden. Außerdem muss ein eval_trigger manuell deklariert werden. Hier sollten alle
Items gelistet sein, die für ein vorübergehendes Aussetzen der Automatisierung sorgen sollen
(z.B. Schalt- und Dimm-Items)
Das Item settings.suspendduration
ermöglicht es, die Dauer der Pausierung bequem
über eine Visu oder das Backend zu ändern. Das darunter angesiedelte duration_format
konvertiert die angegebene Dauer in Minuten in das „Duration Format“ mit
Tage-, Stunden- und Minutenangabe, für 5 Minuten also z.B. 0d 0h 5i. Dies ist genau wie das
suspend_start.unix_timestamp
Item für das smartvisu Widget clock.countdown notwendig.
Setzt man das Item settings.suspend_active
auf False, wird der Pause-Zustand
deaktiviert und manuelle Betätigungen werden
beim nächsten Durchlauf eventuell durch andere Zustände überschrieben.
#stateengine.state_suspend
state_suspend:
name: Zustandsvorlage für manuelles Aussetzen
suspend:
type: bool
knx_dpt: 1
visu_acl: rw
cache: True
visu:
type: bool
knx_dpt: 1
visu_acl: rw
cache: True
suspend_end:
type: str
visu_acl: ro
eval: "'' if not any(char.isdigit() for char in sh..self.date_time()) else sh..self.date_time().split(' ')[1].split('.')[0]"
eval_trigger: .date_time
crontab: init
date_time:
type: str
visu_acl: ro
cache: True
unix_timestamp:
type: num
visu_acl: ro
eval: "0 if not any(char.isdigit() for char in sh...date_time()) else sh.tools.dt2ts(shtime.datetime_transform(sh...date_time())) * 1000"
eval_trigger: ..date_time
crontab: init
suspend_start:
type: str
visu_acl: ro
eval: "'' if not any(char.isdigit() for char in sh..self.date_time()) else sh..self.date_time().split(' ')[1].split('.')[0]"
eval_trigger: .date_time
crontab: init
date_time:
type: str
visu_acl: ro
cache: True
unix_timestamp:
remark: Can be used for the clock.countdown widget
type: num
visu_acl: ro
eval: "0 if not any(char.isdigit() for char in sh...date_time()) else sh.tools.dt2ts(shtime.datetime_transform(sh...date_time())) * 1000"
eval_trigger: ..date_time
crontab: init
manuell:
type: bool
name: manuell
se_manual_invert: True
remark: Adapt the se_manual_exclude the way you need it
#se_manual_include: KNX:* Force manual mode based on source
se_manual_exclude:
- database:*
- init:*
retrigger:
remark: Item to retrigger the rule set evaluation
type: bool
visu_acl: rw
enforce_updates: True
on_update: ..rules = True
settings:
remark: Use these settings for your condition values
type: foo
eval: (sh..suspendduration(sh..suspendduration(), "Init", "Start"), sh..suspendvariant.suspendduration0(sh..suspendduration(), "Init", "Start"), sh..suspendvariant.suspendduration1(sh..suspendvariant.suspendduration1(), "Init", "Start"), sh..suspendvariant.suspendduration2(sh..suspendvariant.suspendduration2(), "Init", "Start"))
crontab: init = True
suspendduration:
remark: duration of suspend mode in minutes (gets converted automatically)
type: num
visu_acl: rw
cache: True
initial_value: 60
on_change: .seconds = value * 60 if not sh..self.property.last_change_by == "On_Change:{}".format(sh..seconds.property.path) else None
on_update: .seconds = value * 60 if "Init" in sh..self.property.last_update_by else None
duration_format:
remark: Can be used for the clock.countdown widget
type: str
cache: True
visu_acl: ro
eval: "'{}d {}h {}i {}s'.format(int(sh...seconds()//86400), int((sh...seconds()%86400)//3600), int((sh...seconds()%3600)//60), round((sh...seconds()%3600)%60))"
eval_trigger:
- ..seconds
- ..
seconds:
remark: duration of suspend mode in seconds (gets converted automatically)
type: num
visu_acl: rw
cache: True
on_change: .. = value / 60 if not sh..self.property.last_change_by in [ "On_Change:{}".format(sh....property.path), "On_Update:{}".format(sh....property.path)] else None
suspend_active:
remark: Use this to (de)activate suspend mode in general
type: bool
visu_acl: rw
cache: True
initial_value: True
settings_edited:
type: bool
name: settings editiert
eval_trigger: ...settings.*
eval: not sh..self()
on_update: ...retrigger = True if sh..self.property.prev_update_age > 0.1 else None
rules:
se_item_suspend: ..suspend
se_item_suspend_visu: ..suspend.visu
se_item_suspend_end: ..suspend_end.date_time
se_item_suspend_start: ..suspend_start.date_time
se_item_suspend_active: ..settings.suspend_active
se_suspend_time: ..settings.suspendduration
eval_trigger:
- ..manuell
suspend:
name: ausgesetzt
on_enter:
se_action_suspend_visu:
- 'function: set'
- 'to: True'
- 'order: 2'
on_enter_or_stay:
se_action_suspend:
- 'function: special'
- 'value: suspend:..suspend, ..manuell'
- 'repeat: True'
- 'order: 1'
se_action_suspend_end:
- 'function: set'
- "to: eval:se_eval.insert_suspend_time('..suspend', suspend_text='%Y-%m-%d %H:%M:%S.%f%z')"
- 'repeat: True'
- 'order: 3'
se_action_suspend_start:
- 'function: set'
- "to: eval:str(shtime.now())"
- 'repeat: True'
- 'conditionset: enter_manuell'
- 'order: 4'
se_action_retrigger:
- 'function: special'
- 'value: retrigger:..retrigger'
- 'delay: var:item.suspend_remaining'
- 'repeat: True'
- 'order: 5'
on_leave:
se_action_suspend:
- 'function: set'
- 'to: False'
- 'order: 2'
se_action_suspend_visu:
- 'function: set'
- 'to: False'
- 'order: 3'
se_action_suspend_end:
- 'function: set'
- 'to: '
- 'order: 4'
se_action_suspend_start:
- 'function: set'
- 'to: '
- 'order: 5'
- 'delay: 1'
enter_manuell:
se_value_trigger_source: eval:se_eval.get_relative_itemproperty('..manuell', 'path')
se_value_suspend_active: True
enter_stay:
se_value_laststate: var:current.state_id
se_agemax_suspend: var:item.suspend_time
se_value_suspend: True
se_value_suspend_active: True
release
Die state_release
Vorlage ist nicht unbedingt nötig, kann aber dazu genutzt werden,
schnell den Sperr- oder Pause-Zustand zu verlassen und die erneute Evaluierung
der Zustände anzuleiern.
#stateengine.state_release
release: #triggers the release
type: bool
knx_dpt: 1
visu_acl: rw
enforce_updates: True
rules:
se_item_lock: ..lock
se_item_suspend: ..suspend
se_item_retrigger: ..rules
se_item_release: ..release
se_item_suspend_end: ..suspend_end
eval_trigger:
- ..release
release:
name: release
on_enter_or_stay:
se_action_suspend:
- 'function: set'
- 'to: False'
- 'order: 1'
se_action_lock:
- 'function: set'
- 'to: False'
- 'order: 2'
se_action_release:
- 'function: set'
- 'to: False'
- 'order: 3'
se_action_suspend_end:
- 'function: set'
- 'to: '
- 'order: 4'
se_action_retrigger:
- 'function: set'
- 'to: True'
- 'order: 5'
- 'repeat: True'
- 'delay: 1'
enter:
se_value_release: True
Pluginspezifische Templates
Es ist neben der oben beschriebene Variante möglich, Vorgabezustände in
der Item-Konfiguration über se_use
zu definieren
und diese später für konkrete Regelwerke durch Plugin-interne Attribute zu nutzen.
Dabei können im konkreten Zustand auch Einstellungen des Vorgabezustands
überschrieben werden. Alternativ ist es möglich, die struct Vorlagen aus
SmarthomeNG >= 1.6 zu nutzen bzw. selbst welche zu erstellen.
Vorgabezustände werden als Item an beliebiger Stelle innerhalb der
Item-Struktur definiert. Es ist sinnvoll, die Vorgabezustände
unter einem gemeinsamen Item namens default
zusammenzufassen. Innerhalb der
Vorgabezustand-Items stehen die gleichen Möglichkeiten wie in
normalen Zustands-Items zur Verfügung. Das dem
Vorgabezustands-Item übergeordnete Item darf nicht das Attribut
se_plugin: active
haben, da diese Items nur Vorlagen und keine
tatsächlichen State Machines darstellen. Im Item über dem
Vorgabezustands-Item können jedoch Items über
se_item_<Bedingungsname|Aktionsname>
angegeben werden. Diese
stehen in den Vorgabezuständen und in den von den Vorgabezuständen
abgeleiteten Zuständen zur Verfügung und müssen so nicht jedes Mal
neu definiert werden.
Im konkreten Zustands-Item kann das Vorgabezustand-Item über das Attribut
se_use:
- struct:stateengine.state_suspend.rules.suspend
- item:<string item mit Verweis auf Vorgabezustand>
- eval:<Ausdruck zum dynamischen Einbinden von Vorgabezuständen>
- <(relative) Itemangabe zum Vorgabezustand> #z.B. .suspend
eingebunden werden. Die Vorgabezustand-Items können als Liste angegben
und geschachtelt werden; das heißt ein Vorgabezustand kann also selbst wiederum
über se_use
von einem weiteren Vorgabezustand abgeleitet
werden. Um unnötige Komplexität und Zirkelbezüge zu vermeiden, ist
die maximale Tiefe jedoch auf 5 Ebenen begrenzt. Jede in einem se_use
angegebene
Definition kann durch eine höher geordnete Angabe mit dem gleichen Namen überschrieben
werden.
Beispiel
Die Konfiguration…
wetter:
helligkeit:
type: num
initial_value: 400
temperatur:
type: num
initial_value: 15
beispiel:
define_use:
type: str
initial_value: 'beispiel.default.Mittags'
default:
se_eval_height: se_eval.get_relative_item('...hoehe')
se_item_helligkeit: wetter.helligkeit
se_item_temperatur: wetter.temperatur
Nacht:
enter:
se_min_helligkeit: 300
se_min_temperatur: 0
se_max_helligkeit: 4000
se_set_height: value:100
se_set_lamella: 0
Morgens:
enter:
se_min_helligkeit: 900
se_max_temperatur: 12
se_set_height: value:90
se_set_lamella: 25
Mittags:
se_set_lamella: 55
enter:
se_min_helligkeit: 5900
se_min_temperatur: 18
raffstore1:
lamelle:
type: num
hoehe:
type: num
automatik:
struct: stateengine.general
lock:
type: bool
rules:
se_item_lamella: ...lamelle
se_item_helligkeit: wetter.helligkeit
Nacht:
se_use: beispiel.default.Nacht
se_set_lamella: 10
enter_additional:
se_min_helligkeit: 20
enter:
se_min_helligkeit: 500
se_min_temperatur: 0
Morgens:
name: morgens
se_use:
- beispiel.default.Morgens
- .Nacht
- struct:stateengine.state_lock.rules.lock
- item:beispiel.define_use
führt zu folgendem Ergebnis:
State beispiel.raffstore1.automatik.rules.Nacht:
State Name: Nacht
Updating Web Interface...
Finished Web Interface Update
State configuration extended by se_use: beispiel.default.Nacht
Condition sets to enter state:
Condition Set 'enter':
Condition 'helligkeit':
item: helligkeit (wetter.helligkeit)
min: 500
max: 4000
negate: False
Condition 'temperatur':
item: temperatur (wetter.temperatur)
min: 0
negate: False
Condition Set 'enter_additional':
Condition 'helligkeit':
item: helligkeit (wetter.helligkeit)
max: 200
negate: False
Actions to perform on enter or stay:
Action 'height':
name: height
item from eval: se_eval.get_relative_item('...hoehe')
value: 100
Action 'lamella':
name: lamella
item from eval: beispiel.raffstore1.lamelle
value: 10
State beispiel.raffstore1.automatik.rules.Morgens:
State Name: morgens
Updating Web Interface...
Finished Web Interface Update
State configuration extended by se_use: [Item: beispiel.default.Morgens, Item: beispiel.raffstore1.automatik.rules.Nacht, Item: beispiel.default.Nacht, SeStructMain stateengine.state_lock.rules.lock, Item: beispiel.default.Mittags]
Condition sets to enter state:
Condition Set 'enter':
Condition 'lock':
item: lock (beispiel.raffstore1.automatik.lock)
value: True
negate: False
Condition 'temperatur':
item: temperatur (wetter.temperatur)
min: 18
max: 12
negate: False
Condition 'helligkeit':
item: helligkeit (wetter.helligkeit)
min: 5900
max: 12000
negate: False
Condition Set 'enter_additional':
Condition 'helligkeit':
item: helligkeit (wetter.helligkeit)
max: 200
negate: False
Actions to perform on enter or stay:
Action 'height':
name: height
item from eval: se_eval.get_relative_item('...hoehe')
value: 100
Action 'lamella':
name: lamella
item from eval: beispiel.raffstore1.lamelle
value: 55
Bemerkung
Folgende Besonderheiten sind bei der Konfiguration zu beachten:
Beim Laden via se_use werden relative Itemdeklarationen NICHT relativ zum StateEngine Item (rules) gesucht, sondern relativ zu dem Item, wo das Attribut
se_item_..
steht. Daher muss hier unbedingtse_eval_..
wie z.B.se_eval_height: se_eval.get_relative_item('...hoehe')
genutzt werden.Auf gleicher Ebene zum StateEngine Item (rules) müssen mittels
struct: stateengine.general
die Standarditems und Attribute des Plugins eingebunden werden, damit eine korrekte Funktion garantiert werden kann.Über
se_use
eingebundene Zustandsvorlagen suchen zwar im darüber gelegenen Item nach den entsprechenden se_item/eval Deklarationen, binden aber keine darüber liegenden Items ein. Daher muss z.B. beim Referenzieren des Lock-Zustands aus dem Plugin Struct das lock Item manuell angelegt werden. Beim Nutzen vonstruct: stateengine.state_lock
wäre das nicht notwendig, weshalb sich eine Referenzierung auf die Plugin Templates viase_use
nur bedingt eignet.