Condition as string
Actions as string
PredicateHandler
Rule as tokens
ActionHandler
Error message if not valid
Time the rule was last executed
The Rule Manager
The Rule Manager holds a collection of rules. Rules can be added to this collection. When a rule is added the rule is parsed by the Rule Manager and for each predicate a Predicate Provider will be searched. Predicate Provider that should be considered can be added to the Rule Manager.
If all predicates of the added rule can be handled by an Predicate Provider for each action of the actions of the rule a Action Handler is searched. Action Handler can be added to the Rule Manager, too.
Array of the added rules If a rule was successfully added, the rule has the form:
id: 'some-id'
name: 'some name'
string: 'if its 10pm and light is on then turn the light off'
conditionToken: 'its 10pm and light is on'
predicates: [
{ id: 'some-id0'
provider: the corresponding provider },
{ id: 'some-id1'
provider: the corresponding provider }
]
tokens: ['predicate', '(', 0, ')', 'and',
'predicate', '(', 1, ')' ]
action: 'turn the light off'
active: false or true
If the rule had an error:
id: id
string: 'if bla then blub'
error: 'Could not find a provider that decides bla'
active: false
Array of ActionHandlers: see actions.coffee
Array of predicateProviders: see actions.coffee
_parseRuleString()
This function parses a rule given by a string and returns a rule object. A rule string is for example 'if its 10pm and light is on then turn the light off' it get parsed to the follwoing rule object:
id: 'some-id'
string: 'if its 10pm and light is on then turn the light off'
conditionToken: 'its 10pm and light is on'
predicates: [
{ id: 'some-id0'
provider: the corresponding provider },
{ id: 'some-id1'
provider: the corresponding provider }
]
tokens: ['predicate', '(', 0, ')', 'and',
'predicate', '(', 1, ')' ]
action: 'turn the light off'
active: false or true
The function returns a promise!
Allways return a promise
First take the string apart, so that
parts = ["", "its 10pm and light is on", "turn the light off"].
Check for the right parts count. Note the empty string at the beginning.
Then extraxt the condition and actions from the rule
rule.conditionToken = "its 10pm and light is on"
rule.actions = "turn the light off"
Split the condition in a token stream. For example:
"12:30 and temperature > 10"
becomes
['12:30', 'and', 'temperature > 30 C']
Then we replace all predicates with tokens of the following form
tokens = ['predicate', '(', 0, ')', 'and', 'predicate', '(', 1, ')']
and remember the predicates:
predicates = [ {token: '12:30'}, {token: 'temperature > 10'}]
For each token
Try to match " and ", " or ", ...
trigger keyword?
find a prdicate provider for that can parse and decide the predicate:
get part of nextInput that is related to the found provider
Parse the for-Suffix:
For each token
actions.push { token: token handler:
Try to macth after as prefix: after 10 seconds log "42"
find a prdicate provider for that can parse and decide the predicate:
get part of nextInput that is related to the found provider
try to match after as suffix: log "42" after 10 seconds
try to parse "for 10 seconds"
_addPredicateChangeListener()
Register for every predicate the callback function that should be called when the predicate becomes true.
For all predicate providers
let us be notified when the predicate state changes.
This function should be called by a provider if a predicate becomes true.
if not active, then nothing to do
Then mark the given predicate as true
and check if the rule is now true.
if the rule is now true, then execute its action
_cancelPredicateproviderNotify()
Cancels for every predicate the callback that should be called when the predicate becomes true.
Then cancel the notifier for all predicates
Then cancel the notifier for all predicates
First parse the rule.
If we have parse error we don't need to continue here
If the rules was successful parsed add it to the rule array.
Check if the condition of the rule is allready true.
If the confition is true then execute the action.
If there was an error pasring the rule, but the rule is forced to be added, then add the rule with an error.
removeRule()
Removes a rule, from the Rule Manager.
First get the rule from the rule array.
Then get cancel all notifies
and delete the rule from the array
and emit the event.
First try to parse the updated ruleString.
Set the properties for the new rule
If the rule was successfully parsed then update the rule
and cancel the notifier for the old predicates.
We do that to keep the old rule object and not use the new one
and register the new ones:
and emit the event.
Then check if the condition of the rule is now true.
If the condition is true then exectue the action.
_evaluateConditionOfRule()
This function returnes a promise thatwill be fulfilled with true if the condition of the
rule is true. This function ignores all the "for"-suffixes of predicates.
The knownPredicates
is an object containing a value for
each predicate for that the state is already known.
_doesRuleCondtionHold()
The same as _evaluateConditionOfRule but does not ignore the for-suffixes.
First evaluate the condition and
if the condition is false then the condition con not hold, because it is already false so return false.
Some predicates could have a 'for'-Suffix like 'for 10 seconds' then the predicates must at least hold for 10 seconds to be true, so we have to wait 10 seconds to decide if the rule is realy true
Create a deferred that will be resolve with the return value when the decision can be made.
We will collect all predicates that have a for suffix and are not yet decideable in an awaiting list.
Whenever an awaiting predicate gets resolved then we will revalidate the rule condition.
If it is true
then resolve the return value as true
and cancel all awaitings.
Else check if we have awaiting predicates. If we have no awaiting predicates
then we can resolve the return value as false
If it has a for suffix and its an event something gone wrong, because an event can't hold (its just one time)
Fill the awaiting list: Check for each predicate,
The time since last change
Time to wait till condition becomes true, if not change occures
Mark that we are awaiting the result
and as long as we are awaiting the result, the predicate is false.
When the time passes
the predicate remains true and no value is awaited anymore.
Let us be notified when it becomes false.
If it changes to false
then the predicate is false
and clear the timeout.
and we can cancel the notify
If we have not found awaiting predicates
then resolve the return value to true.
Cancel all awatting changeHandler
_executeRuleActionsAndLogResult()
Executes the actions of the string using executeAction
and logs the result to
the env.logger.
executeAction()
Executes the actions in the given actionString
cancel scheule for pending executes
schedule new action
wrap into an fcall to convert throwen erros to a rejected promise
wrap into an fcall to convert throwen erros to a rejected promise
sort in config order
Rule System
This file handles the parsing and executing of rules.
What's a rule
A rule is a string that has the format: "if this then that". The this part will be called the condition of the rule and the that the actions of the rule.
Examples:
The condition and predicates
The condition of a rule consists of one or more predicates. The predicates can be combined with "and", "or" and can be grouped by parentheses ('[' and ']'). A predicate is either true or false at a given time. There are special predicates, called event-predicates, that represent events. These predicate are just true in the moment a special event happen.
Each predicate is handled by an Predicate Provider. Take a look at the predicates file for more details.
for-suffix
A predicate can have a "for" as a suffix like in "music is playing for 5 seconds" or "tv is on for 2 hours". If the predicate has a for-suffix then the rule action is only triggered, when the predicate stays true the given time. Predicates that represent one time events like "10pm" can't have a for-suffix because the condition can never hold.
The actions
The actions of a rule can consists of one or more actions. Each action describes a command that should be executed when the confition of the rule is true. Take a look at the actions.coffee for more details.