Provide a trigger for thing Online/Offline status in rule. #3001
Changes from 9 commits
2b3036e
0e6ccfe
9d184d0
330bea9
3b21b2c
12908bf
d99a8da
f9614b7
48b3d50
40158b6
1a55639
db3e23d
5eeb0e2
25b04e9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/** | ||
* Copyright (c) 2014-2016 by the respective copyright holders. | ||
* All rights reserved. This program and the accompanying materials | ||
* are made available under the terms of the Eclipse Public License v1.0 | ||
* which accompanies this distribution, and is available at | ||
* http://www.eclipse.org/legal/epl-v10.html | ||
*/ | ||
package org.eclipse.smarthome.core.library.types; | ||
|
||
import org.eclipse.smarthome.core.library.internal.StateConverterUtil; | ||
import org.eclipse.smarthome.core.types.Convertible; | ||
import org.eclipse.smarthome.core.types.PrimitiveType; | ||
import org.eclipse.smarthome.core.types.State; | ||
|
||
public enum OnlineOfflineType implements PrimitiveType, State, Convertible { | ||
ONLINE, | ||
OFFLINE; | ||
|
||
@Override | ||
public String format(String pattern) { | ||
return String.format(pattern, this.toString()); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return toFullString(); | ||
} | ||
|
||
@Override | ||
public String toFullString() { | ||
return super.toString(); | ||
} | ||
|
||
@Override | ||
public State as(Class<? extends State> target) { | ||
if (target == DecimalType.class) { | ||
return this == ONLINE ? new DecimalType(1) : DecimalType.ZERO; | ||
} else { | ||
return StateConverterUtil.defaultConversion(this, target); | ||
} | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,7 @@ Import-Package: org.eclipse.smarthome.core.common.registry, | |
org.quartz.spi, | ||
org.quartz.utils, | ||
org.slf4j | ||
Require-Bundle: org.eclipse.smarthome.model.rule | ||
Require-Bundle: org.eclipse.smarthome.model.rule, | ||
org.eclipse.smarthome.core | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please use Import-Package instead |
||
Export-Package: org.eclipse.smarthome.model.rule.runtime | ||
Bundle-ActivationPolicy: lazy |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ | |
|
||
import org.eclipse.emf.ecore.util.EcoreUtil; | ||
import org.eclipse.smarthome.core.items.Item; | ||
import org.eclipse.smarthome.core.library.types.OnlineOfflineType; | ||
import org.eclipse.smarthome.core.types.Command; | ||
import org.eclipse.smarthome.core.types.State; | ||
import org.eclipse.smarthome.core.types.Type; | ||
|
@@ -34,6 +35,8 @@ | |
import org.eclipse.smarthome.model.rule.rules.RuleModel; | ||
import org.eclipse.smarthome.model.rule.rules.SystemOnShutdownTrigger; | ||
import org.eclipse.smarthome.model.rule.rules.SystemOnStartupTrigger; | ||
import org.eclipse.smarthome.model.rule.rules.ThingStateChangedEventTrigger; | ||
import org.eclipse.smarthome.model.rule.rules.ThingStateUpdateEventTrigger; | ||
import org.eclipse.smarthome.model.rule.rules.TimerTrigger; | ||
import org.eclipse.smarthome.model.rule.rules.UpdateEventTrigger; | ||
import org.quartz.CronScheduleBuilder; | ||
|
@@ -74,13 +77,17 @@ public enum TriggerTypes { | |
TRIGGER, // fires whenever a trigger is emitted on a channel | ||
STARTUP, // fires when the rule engine bundle starts and once as soon as all required items are available | ||
SHUTDOWN, // fires when the rule engine bundle is stopped | ||
TIMER // fires at a given time | ||
TIMER, // fires at a given time | ||
THINGUPDATE, // fires whenever the thing state is updated. | ||
THINGCHANGE, // fires if the thing state is changed by the update | ||
} | ||
|
||
// lookup maps for different triggering conditions | ||
private Map<String, Set<Rule>> updateEventTriggeredRules = Maps.newHashMap(); | ||
private Map<String, Set<Rule>> changedEventTriggeredRules = Maps.newHashMap(); | ||
private Map<String, Set<Rule>> commandEventTriggeredRules = Maps.newHashMap(); | ||
private Map<String, Set<Rule>> thingUpdateEventTriggeredRules = Maps.newHashMap(); | ||
private Map<String, Set<Rule>> thingChangedEventTriggeredRules = Maps.newHashMap(); | ||
// Maps from channelName -> Rules | ||
private Map<String, Set<Rule>> triggerEventTriggeredRules = Maps.newHashMap(); | ||
private Set<Rule> systemStartupTriggeredRules = new CopyOnWriteArraySet<>(); | ||
|
@@ -133,6 +140,11 @@ public Iterable<Rule> getRules(TriggerTypes type) { | |
case TRIGGER: | ||
result = Iterables.concat(triggerEventTriggeredRules.values()); | ||
break; | ||
case THINGUPDATE: | ||
result = Iterables.concat(thingUpdateEventTriggeredRules.values()); | ||
break; | ||
case THINGCHANGE: | ||
result = Iterables.concat(thingChangedEventTriggeredRules.values()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume a break is missing here! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Many thanks. I'll create a new PR. |
||
default: | ||
result = Sets.newHashSet(); | ||
} | ||
|
@@ -221,18 +233,30 @@ public Iterable<Rule> getRules(TriggerTypes triggerType, String channel, String | |
return result; | ||
} | ||
|
||
private Iterable<Rule> getAllRules(TriggerTypes type, String itemName) { | ||
public Iterable<Rule> getRules(TriggerTypes triggerType, String thingUid, State state) { | ||
return internalGetThingRules(triggerType, thingUid, null, state); | ||
} | ||
|
||
public Iterable<Rule> getRules(TriggerTypes triggerType, String thingUid, State oldState, State newState) { | ||
return internalGetThingRules(triggerType, thingUid, oldState, newState); | ||
} | ||
|
||
private Iterable<Rule> getAllRules(TriggerTypes type, String name) { | ||
switch (type) { | ||
case STARTUP: | ||
return systemStartupTriggeredRules; | ||
case SHUTDOWN: | ||
return systemShutdownTriggeredRules; | ||
case UPDATE: | ||
return updateEventTriggeredRules.get(itemName); | ||
return updateEventTriggeredRules.get(name); | ||
case CHANGE: | ||
return changedEventTriggeredRules.get(itemName); | ||
return changedEventTriggeredRules.get(name); | ||
case COMMAND: | ||
return commandEventTriggeredRules.get(itemName); | ||
return commandEventTriggeredRules.get(name); | ||
case THINGUPDATE: | ||
return thingUpdateEventTriggeredRules.get(name); | ||
case THINGCHANGE: | ||
return thingChangedEventTriggeredRules.get(name); | ||
default: | ||
return Sets.newHashSet(); | ||
} | ||
|
@@ -331,6 +355,74 @@ private Iterable<Rule> internalGetRules(TriggerTypes triggerType, Item item, Typ | |
return result; | ||
} | ||
|
||
private Iterable<Rule> internalGetThingRules(TriggerTypes triggerType, String thingUid, Type oldType, | ||
Type newType) { | ||
List<Rule> result = Lists.newArrayList(); | ||
Iterable<Rule> rules = getAllRules(triggerType, thingUid); | ||
if (rules == null) { | ||
rules = Lists.newArrayList(); | ||
} | ||
List<Class<? extends State>> thingStateTypes = new ArrayList<Class<? extends State>>(); | ||
thingStateTypes.add(OnlineOfflineType.class); | ||
|
||
switch (triggerType) { | ||
case THINGUPDATE: | ||
if (newType instanceof State) { | ||
State state = (State) newType; | ||
for (Rule rule : rules) { | ||
for (EventTrigger t : rule.getEventtrigger()) { | ||
if (t instanceof ThingStateUpdateEventTrigger) { | ||
ThingStateUpdateEventTrigger tt = (ThingStateUpdateEventTrigger) t; | ||
if (tt.getThing().equals(thingUid)) { | ||
if (tt.getState() != null) { | ||
State triggerState = TypeParser.parseState(thingStateTypes, tt.getState()); | ||
if (!state.equals(triggerState)) { | ||
continue; | ||
} | ||
} | ||
result.add(rule); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
break; | ||
case THINGCHANGE: | ||
if (newType instanceof State && oldType instanceof State) { | ||
State newState = (State) newType; | ||
State oldState = (State) oldType; | ||
for (Rule rule : rules) { | ||
for (EventTrigger t : rule.getEventtrigger()) { | ||
if (t instanceof ThingStateChangedEventTrigger) { | ||
ThingStateChangedEventTrigger ct = (ThingStateChangedEventTrigger) t; | ||
if (ct.getThing().equals(thingUid)) { | ||
if (ct.getOldState() != null) { | ||
State triggerOldState = TypeParser.parseState(thingStateTypes, | ||
ct.getOldState()); | ||
if (!oldState.equals(triggerOldState)) { | ||
continue; | ||
} | ||
} | ||
if (ct.getNewState() != null) { | ||
State triggerNewState = TypeParser.parseState(thingStateTypes, | ||
ct.getNewState()); | ||
if (!newState.equals(triggerNewState)) { | ||
continue; | ||
} | ||
} | ||
result.add(rule); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
break; | ||
default: | ||
break; | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* Removes all rules with a given trigger type from the mapping tables. | ||
* | ||
|
@@ -362,6 +454,12 @@ public void clear(TriggerTypes type) { | |
} | ||
timerEventTriggeredRules.clear(); | ||
break; | ||
case THINGUPDATE: | ||
thingUpdateEventTriggeredRules.clear(); | ||
break; | ||
case THINGCHANGE: | ||
thingChangedEventTriggeredRules.clear(); | ||
break; | ||
} | ||
} | ||
|
||
|
@@ -376,6 +474,8 @@ public void clearAll() { | |
clear(COMMAND); | ||
clear(TIMER); | ||
clear(TRIGGER); | ||
clear(THINGUPDATE); | ||
clear(THINGCHANGE); | ||
} | ||
|
||
/** | ||
|
@@ -430,6 +530,22 @@ public synchronized void addRule(Rule rule) { | |
triggerEventTriggeredRules.put(eeTrigger.getChannel(), rules); | ||
} | ||
rules.add(rule); | ||
} else if (t instanceof ThingStateUpdateEventTrigger) { | ||
ThingStateUpdateEventTrigger tsuTrigger = (ThingStateUpdateEventTrigger) t; | ||
Set<Rule> rules = thingUpdateEventTriggeredRules.get(tsuTrigger); | ||
if (rules == null) { | ||
rules = new HashSet<Rule>(); | ||
thingUpdateEventTriggeredRules.put(tsuTrigger.getThing(), rules); | ||
} | ||
rules.add(rule); | ||
} else if (t instanceof ThingStateChangedEventTrigger) { | ||
ThingStateChangedEventTrigger tscTrigger = (ThingStateChangedEventTrigger) t; | ||
Set<Rule> rules = thingChangedEventTriggeredRules.get(tscTrigger.getThing()); | ||
if (rules == null) { | ||
rules = new HashSet<Rule>(); | ||
thingChangedEventTriggeredRules.put(tscTrigger.getThing(), rules); | ||
} | ||
rules.add(rule); | ||
} | ||
} | ||
} | ||
|
@@ -470,6 +586,15 @@ public void removeRule(TriggerTypes type, Rule rule) { | |
timerEventTriggeredRules.remove(rule); | ||
removeTimerRule(rule); | ||
break; | ||
case THINGUPDATE: | ||
for (Set<Rule> rules : thingUpdateEventTriggeredRules.values()) { | ||
rules.remove(rule); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing break There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll create a new PR. |
||
case THINGCHANGE: | ||
for (Set<Rule> rules : thingChangedEventTriggeredRules.values()) { | ||
rules.remove(rule); | ||
} | ||
break; | ||
} | ||
} | ||
|
||
|
@@ -497,6 +622,8 @@ public void removeRuleModel(RuleModel ruleModel) { | |
removeRules(STARTUP, Collections.singletonList(systemStartupTriggeredRules), ruleModel); | ||
removeRules(SHUTDOWN, Collections.singletonList(systemShutdownTriggeredRules), ruleModel); | ||
removeRules(TIMER, Collections.singletonList(timerEventTriggeredRules), ruleModel); | ||
removeRules(THINGUPDATE, thingUpdateEventTriggeredRules.values(), ruleModel); | ||
removeRules(THINGCHANGE, thingChangedEventTriggeredRules.values(), ruleModel); | ||
} | ||
|
||
private void removeRules(TriggerTypes type, Collection<? extends Collection<Rule>> ruleSets, RuleModel model) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!-- | ||
|
||
Copyright (c) 2014-2016 by the respective copyright holders. | ||
All rights reserved. This program and the accompanying materials | ||
are made available under the terms of the Eclipse Public License v1.0 | ||
which accompanies this distribution, and is available at | ||
http://www.eclipse.org/legal/epl-v10.html | ||
|
||
--> | ||
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.eclipse.smarthome.model.rule.jvmmodel.rulesthingrefresher"> | ||
<implementation class="org.eclipse.smarthome.model.rule.jvmmodel.RulesThingRefresher"/> | ||
<reference bind="setModelRepository" cardinality="1..1" interface="org.eclipse.smarthome.model.core.ModelRepository" name="ModelRepository" policy="dynamic" unbind="unsetModelRepository"/> | ||
<reference bind="setThingRegistry" cardinality="0..1" interface="org.eclipse.smarthome.core.thing.ThingRegistry" name="ThingRegistry" policy="dynamic" unbind="unsetThingRegistry"/> | ||
<reference bind="addActionService" cardinality="0..n" interface="org.eclipse.smarthome.model.script.engine.action.ActionService" name="ActionService" policy="dynamic" unbind="removeActionService"/> | ||
</scr:component> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ONLINE/OFFLINE must not be introduced as a new state - it is a "status" which only applies for Things, not for Items.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see the comment in Rules.xtext about ValidState and ThingStatusInfo