diff --git a/eraser-base/src/main/jastadd/Item.jrag b/eraser-base/src/main/jastadd/Item.jrag
index ece1fd875c26b2d6f4bc94aa712f9bfe56a8884b..94ebdce1bc189ccbfa73b767ccacd9488936fc5c 100644
--- a/eraser-base/src/main/jastadd/Item.jrag
+++ b/eraser-base/src/main/jastadd/Item.jrag
@@ -21,6 +21,8 @@ aspect ItemHandling {
   eq ItemWithDoubleState.getStateAsString() = Double.toString(getState());
   eq ItemWithStringState.getStateAsString() = getState();
 
+  syn LastChanged Item.getLastChanged() = new LastChanged();
+
   //--- getStateAsDouble ---
   syn double Item.getStateAsDouble();
   // TupleHSB and String work like default
@@ -285,7 +287,7 @@ aspect ItemHandling {
 
   //--- stateUpdated ---
   /**
-   * Called, whenever the state of an item is updated. Does various things including:
+   * Called, whenever the state of an item is updated. Does various things (except it has no parent) including:
    * <ul>
    *   <li>Send the new state via MQTT</li>
    *   <li>Send the new state to Influx DB</li>
@@ -294,6 +296,7 @@ aspect ItemHandling {
    * @param shouldSendState whether to send the new state (currently affects MQTT and Influx)
    */
   protected void Item.stateUpdated(boolean shouldSendState) {
+    if (getParent() == null) { return; }
     if (shouldSendState) {
       try {
         // sendState() refined in MQTT and Influx aspect
@@ -302,9 +305,13 @@ aspect ItemHandling {
         logger.catching(e);
       }
     }
-    getItemObserver().apply();
+    if (this.getLastChanged().checkStateProcessingTime(relevantFrequencySetting())) {
+      this.getLastChanged().afterStateChangeProcessed();
+      getItemObserver().apply();
+    }
   }
 
+
   //--- sendState ---
   protected void Item.sendState() throws Exception {
     for (MachineLearningModel model : getRelevantInMachineLearningModels()) {
@@ -459,6 +466,11 @@ aspect ItemHandling {
 
   }
 
+  //--- onReceiveStateChange ---
+  protected void Item.onReceiveStateChange() {
+      //Instant lastChange = this.
+  }
+
   
 
 
@@ -506,4 +518,6 @@ aspect ItemHandling {
   syn String ItemUpdate.getNewStateAsString();
   eq ItemUpdateColor.getNewStateAsString() = getNewHSB().toString();
   eq ItemUpdateDouble.getNewStateAsString() = Double.toString(getNewValue());
+
+  
 }
diff --git a/eraser-base/src/main/jastadd/LastChanged.jrag b/eraser-base/src/main/jastadd/LastChanged.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..e79bfe7c8c394154f78307ca629ff64ddfd4eb2f
--- /dev/null
+++ b/eraser-base/src/main/jastadd/LastChanged.jrag
@@ -0,0 +1,20 @@
+aspect LastChanged {
+    public void LastChanged.afterStateChangeProcessed() {
+        this.setValue(Instant.now());
+    }
+
+    public boolean LastChanged.checkStateProcessingTime(FrequencySetting FrequencySetting) {
+        if (FrequencySetting==null) {
+            return true;
+        }
+        double frequency = FrequencySetting.getEventProcessingFrequency();
+        Instant lastStateChange = this.getValue();
+        if (lastStateChange==null) {
+            return true;
+        }
+
+        return lastStateChange.toEpochMilli() + (1/frequency)*1000 < Instant.now().toEpochMilli();
+
+    }
+
+}
\ No newline at end of file
diff --git a/eraser-base/src/main/jastadd/Navigation.jrag b/eraser-base/src/main/jastadd/Navigation.jrag
index dd4e283a155b68dd432303debd93dd094aee06eb..984e1029e1aa7817396d6676f7772d9bc824d1ba 100644
--- a/eraser-base/src/main/jastadd/Navigation.jrag
+++ b/eraser-base/src/main/jastadd/Navigation.jrag
@@ -11,6 +11,48 @@ aspect Navigation {
     return result;
   }
 
+  //--- enclosingGroup ---
+  inh Group Group.enclosingGroup();
+  inh Group Item.enclosingGroup();
+  eq Group.getItem().enclosingGroup() = this;
+  eq Group.getGroup().enclosingGroup() = this;
+  eq SmartHomeEntityModel.getGroup().enclosingGroup() = null;
+  eq SmartHomeEntityModel.getActivityItem().enclosingGroup() = null;
+
+  //--- relevantFrequencySetting ---
+  syn FrequencySetting Group.relevantFrequencySetting() {
+    // first, use value defined on group itself, if any
+    if (this.hasFrequencySetting()) {
+      return this.getFrequencySetting();
+    }
+
+    // recursively use enclosing group and use value from there, if any
+    Group parent = enclosingGroup();
+    if (parent != null) {
+      return parent.relevantFrequencySetting();
+    }
+
+    // if top-level group without FrequencySetting
+    return null;
+
+  }
+  syn FrequencySetting Item.relevantFrequencySetting() {
+    // first, use value defined on item itself, if any
+    if (this.hasFrequencySetting()) {
+      return this.getFrequencySetting();
+    }
+
+    // use enclosing group and use value from there, if any
+    Group parent = enclosingGroup();
+    if (parent != null) {
+      return parent.relevantFrequencySetting();
+    }
+
+    // if top-level item without FrequencySetting
+    return null;
+  }
+
+  //--- addItems ---
   private void SmartHomeEntityModel.addItems(java.util.List<Item> result, JastAddList<Group> groups) {
     groups.forEach(group -> group.getItemList().forEach(item -> result.add(item)));
   }
@@ -56,4 +98,5 @@ aspect Navigation {
   eq Root.getRule().getRoot() = this;
   eq Root.getUser().getRoot() = this;
   eq Root.getLocation().getRoot() = this;
+  eq Root.getFrequencySetting().getRoot() = this;
 }
diff --git a/eraser-base/src/main/jastadd/Resolving.jrag b/eraser-base/src/main/jastadd/Resolving.jrag
index e1d793c1587afcd16b232197a2e87b21c9d6f08b..2d3a99f76bb63228bf41c315c9c9a86a768653cd 100644
--- a/eraser-base/src/main/jastadd/Resolving.jrag
+++ b/eraser-base/src/main/jastadd/Resolving.jrag
@@ -98,9 +98,22 @@ aspect Resolving {
     return java.util.Optional.empty();
   }
 
+  syn java.util.Optional<FrequencySetting> Root.resolveFrequencySetting(String performanceId) {
+    for (FrequencySetting performance : getFrequencySettingList()) {
+      if (performance.getLabel().equals(performanceId)) {
+        return java.util.Optional.of(performance);
+      }
+    }
+    return java.util.Optional.empty();
+  }
+
   // implementing resolving for relations
   refine RefResolverStubs eq StateSyncGroup.resolveTargetItemByToken(String id, int position) {
     return getRoot().getSmartHomeEntityModel().resolveItem(id).orElseThrow(() -> new RuntimeException("Item '" + id + "' not found!"));
   }
 
+  refine RefResolverStubs eq ASTNode.globallyResolveFrequencySettingByToken(String id) {
+    return getRoot().resolveFrequencySetting(id).orElseThrow(() -> new RuntimeException("FrequencySetting '" + id + "' not found!"));
+  }
+
 }
diff --git a/eraser-base/src/main/jastadd/Rules.relast b/eraser-base/src/main/jastadd/Rules.relast
index 22176613130d4fd2802379fffb6d406819a2cd04..e6c3ab0138742149407d3a6279a958c2cc551ee4 100644
--- a/eraser-base/src/main/jastadd/Rules.relast
+++ b/eraser-base/src/main/jastadd/Rules.relast
@@ -2,7 +2,8 @@
 Rule ::= Condition* Action* ;
 abstract Condition ;
 ItemStateCheckCondition : Condition ::= ItemStateCheck ;
-ItemStateChangeCondition : Condition ::= Item;
+ItemStateChangeCondition : Condition ;
+rel ItemStateChangeCondition.Item -> Item;
 ExpressionCondition : Condition ::= LogicalExpression ;
 abstract Action ;
 NoopAction : Action ;
@@ -29,5 +30,6 @@ MultiplyDoubleToStateAction : SetStateAction ::= <Multiplier:double> ;
 ItemObserver ::= ;
 rel ItemObserver.TriggeredRule* <-> Rule.Observer* ;
 
+FrequencySetting : LabelledModelElement ::= <EventProcessingFrequency:double> ;
 StateSyncGroup : Rule ;
 rel StateSyncGroup.TargetItem* -> Item;
diff --git a/eraser-base/src/main/jastadd/eraser.flex b/eraser-base/src/main/jastadd/eraser.flex
index 21987f14a9762c3f7c9de8dadd405ad5431ad7b5..621d9d044c0dc2d82695221d28c1da16f6dadd5c 100644
--- a/eraser-base/src/main/jastadd/eraser.flex
+++ b/eraser-base/src/main/jastadd/eraser.flex
@@ -87,6 +87,9 @@ Comment = "//" [^\n\r]+
 "incoming"     { return sym(Terminals.INCOMING); }
 "items"        { return sym(Terminals.ITEMS); }
 "itemType"     { return sym(Terminals.ITEM_TYPE); }
+"FrequencySetting" { return sym(Terminals.FREQUENCY_SETTING); }
+"performance" { return sym(Terminals.PERFORMANCE); }
+"procFreq" { return sym(Terminals.PROCESS_FREQUENCY); }
 "label"        { return sym(Terminals.LABEL); }
 "links"        { return sym(Terminals.LINKS); }
 "metaData"     { return sym(Terminals.META_DATA); }
diff --git a/eraser-base/src/main/jastadd/eraser.parser b/eraser-base/src/main/jastadd/eraser.parser
index 1a055ec64b0166fa0bea7882e6e0921e7d280976..f58525a329bc0e858283e1b5f95ef14297a3f14c 100644
--- a/eraser-base/src/main/jastadd/eraser.parser
+++ b/eraser-base/src/main/jastadd/eraser.parser
@@ -41,6 +41,7 @@ Root goal =
   |  influx_root.ir goal.r              {: r.setInfluxRoot(ir); return r; :}
   |  machine_learning_root.ml goal.r    {: r.setMachineLearningRoot(ml); return r; :}
   |  rule.rule goal.r                   {: r.addRule(rule); return r; :}
+  |  frequency_setting.ip goal.r         {: r.addFrequencySetting(ip); return r; :}
   |  thing.t                            {: return eph.createRoot(t); :}
   |  item.i                             {: return eph.createRoot(); :}
   |  group.g                            {: return eph.createRoot(g); :}
@@ -53,6 +54,7 @@ Root goal =
   |  influx_root.ir                     {: return eph.createRoot(ir); :}
   |  machine_learning_root.ml           {: return eph.createRoot(ml); :}
   |  rule.rule                          {: return eph.createRoot(rule); :}
+  |  frequency_setting.ip                {: return eph.createRoot(ip); :}
   ;
 
 %left RB_ROUND;
@@ -128,28 +130,28 @@ Item item =
   |  ITEM COLON item_body.ib SEMICOLON                {: return eph.retype(new DefaultItem(), ib); :}
   ;
 
-// ITEM_TYPE Item: id="" label="" state="" category="" topic="" metaData={"key":"value"} ;
+// ITEM_TYPE Item: id="" label="" state="" category="" topic="" performance="" metaData={"key":"value"} ;
 Item item_body =
      ID EQUALS TEXT.n item_body.i       {: return eph.setID(i, n); :}
   |  LABEL EQUALS TEXT.n item_body.i    {: i.setLabel(n); return i; :}
   |  STATE EQUALS TEXT.n item_body.i    {: i.setStateFromString(n); return i; :}
   |  TOPIC EQUALS TEXT.n item_body.i    {: return eph.setTopic(i, n); :}
   |  CATEGORY EQUALS TEXT.n item_body.i {: return eph.setCategory(i, n); :}
+  |  PERFORMANCE EQUALS frequency_setting_ref.ip item_body.i           {: i.setFrequencySetting(ip); return i; :}
   |  META_DATA EQUALS string_map.md item_body.i
     {: return eph.setMetaData(i, md); :}
   |                                     {: return eph.createItem(); :}
   ;
 
-Item item_ref =
-     TEXT.n                                 {: return Item.createRef(n); :}
-  ;
+Item item_ref = TEXT.n {: return Item.createRef(n); :};
+FrequencySetting frequency_setting_ref = TEXT.n {: return FrequencySetting.createRef(n); :};
 
 Group group =
      GROUP COLON group_body.gb SEMICOLON    {: return gb; :}
   ;
 
-// Group: id="" groups=["GROUP_ID", "GROUP_ID"] items=["ITEM_ID", "ITEM_ID"] aggregation="";
-// Group: id="" groups=["GROUP_ID", "GROUP_ID"] items=["ITEM_ID", "ITEM_ID"] aggregation="" ("","");
+// Group: id="" groups=["GROUP_ID", "GROUP_ID"] items=["ITEM_ID", "ITEM_ID"] performance="" aggregation="";
+// Group: id="" groups=["GROUP_ID", "GROUP_ID"] items=["ITEM_ID", "ITEM_ID"] performance="" aggregation="" ("","");
 Group group_body =
      ID EQUALS TEXT.n group_body.g                    {: return eph.setID(g, n); :}
   |  LABEL EQUALS TEXT.n group_body.g                 {: g.setLabel(n); return g; :}
@@ -158,6 +160,7 @@ Group group_body =
   |  AGGREGATION EQUALS TEXT.n group_body.g           {: return eph.setSimpleAggregationFunction(g, n); :}
   |  AGGREGATION EQUALS TEXT.n round_string_list.params group_body.g
     {: return eph.setParameterizedAggregationFunction(g, n, params); :}
+  |  PERFORMANCE EQUALS frequency_setting_ref.ip group_body.g           {: g.setFrequencySetting(ip); return g; :}
   |                                                   {: return new Group(); :}
   ;
 
@@ -365,3 +368,14 @@ IntegerKeyMap integer_map_body =
        return result;
     :}
   ;
+
+FrequencySetting frequency_setting =
+    FREQUENCY_SETTING COLON frequency_setting_body.ipb SEMICOLON   {: return ipb; :}
+  ;
+
+// FrequencySetting: id="" procFreq="" persFreq="";
+FrequencySetting frequency_setting_body =
+     ID EQUALS TEXT.n frequency_setting_body.ip                  {: return eph.setID(ip, n); :}
+  |  PROCESS_FREQUENCY EQUALS TEXT.n frequency_setting_body.ip        {: ip.setEventProcessingFrequency(Double.parseDouble(n)); return ip; :}
+  |                                                   {: return new FrequencySetting(); :}
+  ;
diff --git a/eraser-base/src/main/jastadd/main.relast b/eraser-base/src/main/jastadd/main.relast
index b9fd3e6ca271b94dd0a59c1f596c3e4cad241d58..e12d23dc7185d244d079e3aa5d712a4290c8a2f4 100644
--- a/eraser-base/src/main/jastadd/main.relast
+++ b/eraser-base/src/main/jastadd/main.relast
@@ -1,5 +1,5 @@
 // ----------------    Main    ------------------------------
-Root ::= SmartHomeEntityModel User* MqttRoot InfluxRoot MachineLearningRoot Rule* Location* ;
+Root ::= SmartHomeEntityModel User* MqttRoot InfluxRoot MachineLearningRoot Rule* Location* FrequencySetting*;
 
 // ----------------    Users   ------------------------------
 User : LabelledModelElement ;
diff --git a/eraser-base/src/main/jastadd/shem.relast b/eraser-base/src/main/jastadd/shem.relast
index a71a0ede3307b937a77798d9713ddbae197d323e..62cb25506245a5f7d04dcea483cfa11c07d1d179 100644
--- a/eraser-base/src/main/jastadd/shem.relast
+++ b/eraser-base/src/main/jastadd/shem.relast
@@ -1,6 +1,7 @@
 // ----------------    openHAB    ------------------------------
 SmartHomeEntityModel ::= Thing* Group* ThingType* ChannelType* ChannelCategory* ItemCategory* /ActivityItem:Item/ ;
 
+
 abstract ModelElement ::= <ID:String> ;
 abstract LabelledModelElement : ModelElement ::= <Label:String> ;
 abstract DescribableModelElement : LabelledModelElement ::= <Description:String> ;
@@ -25,10 +26,9 @@ rel Channel.LinkedItem* <-> Item.Channel? ;
 Parameter : DescribableModelElement ::= <Type:ParameterValueType> [DefaultValue:ParameterDefaultValue] <Context:String> <Required:boolean> ;
 ParameterDefaultValue ::= <Value:String> ;
 
-abstract Item : LabelledModelElement ::= <_fetched_data:boolean> MetaData:ItemMetaData* /ItemObserver/;
-
-
+abstract Item : LabelledModelElement ::= <_fetched_data:boolean> MetaData:ItemMetaData* /ItemObserver/ /LastChanged/;
 rel Item.Category? -> ItemCategory ;
+rel Item.FrequencySetting? -> FrequencySetting ;
 
 abstract ItemWithBooleanState : Item ::= <_state:boolean> ;
 abstract ItemWithStringState : Item ::= <_state:String> ;
@@ -51,8 +51,14 @@ ItemMetaData ::= <Key:String> <Value:String> ;
 
 ItemCategory ::= <Name:String> ;
 
+LastChanged ::= <Value:Instant> ;
+
+
 Group : LabelledModelElement ::= Group* Item* [AggregationFunction:GroupAggregationFunction] ;
+rel Group.FrequencySetting? -> FrequencySetting ;
+
 abstract GroupAggregationFunction ;
 SimpleGroupAggregationFunction : GroupAggregationFunction ::= <FunctionName:SimpleGroupAggregationFunctionName> ;
 ParameterizedGroupAggregationFunction : GroupAggregationFunction ::= <FunctionName:ParameterizedGroupAggregationFunctionName>
                                                                      <Param1:String> <Param2:String> ;
+
diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/parser/EraserParserHelper.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/parser/EraserParserHelper.java
index 01b91770dc1d4a58dfd4f03033ea1068c9282a98..32be358baf48a7cd2af133d0fe3e1a3dca97aa02 100644
--- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/parser/EraserParserHelper.java
+++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/parser/EraserParserHelper.java
@@ -24,16 +24,18 @@ public class EraserParserHelper {
   private Map<String, Parameter> parameterMap = new HashMap<>();
   private Map<String, Item> itemMap = new HashMap<>();
   private Map<String, Group> groupMap = new HashMap<>();
+  private Map<String, FrequencySetting> FrequencySettingMap = new HashMap<>();
 
   private Map<Thing, String> missingThingTypeMap = new HashMap<>();
   private Map<Channel, String> missingChannelTypeMap = new HashMap<>();
   private Map<Item, String> missingTopicMap = new HashMap<>();
   private Map<Item, String> missingItemCategoryMap = new HashMap<>();
+
   private Map<Designator, String> missingItemForDesignator = new HashMap<>();
 
   private Map<Thing, Iterable<String>> missingChannelListMap = new HashMap<>();
   private Map<Channel, Iterable<String>> missingItemLinkListMap = new HashMap<>();
-  private Map<Item, Iterable<String>> missingControllingListMap = new HashMap<>();
+
   private Map<Group, Iterable<String>> missingSubGroupListMap = new HashMap<>();
   private Map<Group, Iterable<String>> missingItemListMap = new HashMap<>();
   private Map<ThingType, Iterable<String>> missingChannelTypeListMap = new HashMap<>();
@@ -94,19 +96,20 @@ public class EraserParserHelper {
     resolveList(itemMap, missingItemLinkListMap, Channel::addLinkedItem);
     resolveList(groupMap, missingSubGroupListMap, Group::addGroup);
     resolveList(itemMap, missingItemListMap, this::addItemToGroup);
-
     resolveList(channelTypeMap, missingChannelTypeListMap, ThingType::addChannelType);
     resolveList(parameterMap, missingParameterListMap, ThingType::addParameter);
-    resolveList(itemMap, missingControllingListMap, Item::addControlling);
+
 
     createUnknownGroupIfNecessary();
     createChannelCategories();
     createItemCategories();
+
     if (checkUnusedElements) {
       checkUnusedElements();
     }
 
     this.root.treeResolveAll();
+    this.root.doFullTraversal();
   }
 
   private void addItemToGroup(Group group, Item item) {
@@ -226,6 +229,12 @@ public class EraserParserHelper {
     return thing;
   }
 
+  public FrequencySetting setID(FrequencySetting FrequencySetting, String id) {
+    FrequencySetting.setID(id);
+    FrequencySettingMap.put(id,FrequencySetting);
+    return FrequencySetting;
+  }
+
   public ThingType setID(ThingType thingType, String id) {
     thingType.setID(id);
     thingTypeMap.put(id, thingType);
@@ -259,6 +268,7 @@ public class EraserParserHelper {
     return c;
   }
 
+
   public Channel setLinks(Channel c, StringList linkNames) {
     missingItemLinkListMap.put(c, linkNames);
     return c;
@@ -300,6 +310,7 @@ public class EraserParserHelper {
     itemWithCorrectType.setID(prototype.getID());
     itemWithCorrectType.setLabel(prototype.getLabel());
     itemWithCorrectType.setMetaDataList(prototype.getMetaDataList());
+    itemWithCorrectType.setFrequencySetting(prototype.getFrequencySetting());
     if (!(itemWithCorrectType instanceof ActivityItem)) {
       String state = prototype.getStateAsString();
       itemWithCorrectType.disableSendState();
@@ -312,7 +323,6 @@ public class EraserParserHelper {
     }
 
     moveMissingForRetype(itemWithCorrectType, prototype, missingTopicMap);
-    moveMissingForRetype(itemWithCorrectType, prototype, missingControllingListMap);
     moveMissingForRetype(itemWithCorrectType, prototype, missingItemCategoryMap);
 
     itemMap.put(prototype.getID(), itemWithCorrectType);
@@ -470,6 +480,12 @@ public class EraserParserHelper {
     return result;
   }
 
+  public Root createRoot(FrequencySetting FrequencySetting) {
+    Root result = createRoot();
+    result.addFrequencySetting(FrequencySetting);
+    return result;
+  }
+
   //+++ newStuff (to be categorized) +++
   public Designator createDesignator(String itemName) {
     Designator result = new Designator();
diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/RulesTest.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/RulesTest.java
index 633627c6bf85e4ddd363865b01bb62155919ae1e..f4da998f546f33e1ad976b2d9aef08b329185966 100644
--- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/RulesTest.java
+++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/RulesTest.java
@@ -250,6 +250,7 @@ public class RulesTest {
     assertEquals(2, counter.get(item), "Change of item to 7 should not trigger the rule, check2 violated");
   }
 
+
   @Test
   public void testStateSyncGroupRewriteStructure() {
     // init StateSyncGroup
@@ -915,6 +916,35 @@ public class RulesTest {
     assertEquals(2, counter.get(null), "Rule was not executed two times");
   }
 
+  @Test
+  public void testFrequencySetting() {
+
+    TestUtils.ModelAndItem mai = createModelAndItem(0);
+    NumberItem numberItem = mai.item;
+
+    FrequencySetting itemPerformance = new FrequencySetting();
+    itemPerformance.setEventProcessingFrequency(10);
+    numberItem.setFrequencySetting(itemPerformance);
+
+    Rule rule = new Rule();
+    CountingAction counter = new CountingAction();
+    rule.addAction(counter);
+    rule.activateFor(numberItem);
+    numberItem.setState(1);
+    numberItem.setState(2);
+    assertEquals(1, counter.get(numberItem), "Action was triggered although FrequencySetting too small");
+    counter.reset();
+    waitMillis(100);
+    numberItem.setState(3);
+    assertEquals(1, counter.get(numberItem), "Action wasn't triggered although frequency FrequencySetting is small enough");
+    counter.reset();
+    numberItem.setState(4);
+    numberItem.setState(5);
+    assertEquals(0, counter.get(numberItem), "Action was triggered although FrequencySetting too small");
+    counter.reset();
+
+  }
+
   private static void waitMillis(int millis) {
     try {
       Thread.sleep(millis);