diff --git a/eraser-base/src/main/jastadd/Item.jrag b/eraser-base/src/main/jastadd/Item.jrag
index 87d45fa2e45bd59de85bbdf8a04dac716b5ae1b0..e6378febc6811e38d19984d70e7e28c24fe27efa 100644
--- a/eraser-base/src/main/jastadd/Item.jrag
+++ b/eraser-base/src/main/jastadd/Item.jrag
@@ -290,7 +290,6 @@ aspect ItemHandling {
    *   <li>Send the new state via MQTT</li>
    *   <li>Send the new state to Influx DB</li>
    *   <li>Notify the attached {@link ItemObserver}, if any</li>
-   *   <li>Update state of controlled items</li>
    * </ul>
    * @param shouldSendState whether to send the new state (currently affects MQTT and Influx)
    */
@@ -306,9 +305,6 @@ aspect ItemHandling {
     if (hasItemObserver()) {
       getItemObserver().apply();
     }
-    for (Item controlled : getControllingList()) {
-      doUpdateFor(controlled);
-    }
   }
 
   //--- sendState ---
@@ -388,22 +384,49 @@ aspect ItemHandling {
   syn DateTimeItem Item.asDateTimeItem() = (DateTimeItem) this;
   syn DateTimeItem DateTimeItem.asDateTimeItem() = this;
 
-  //--- doUpdateFor ---
-  protected abstract void Item.doUpdateFor(Item controlling);
-  protected void ItemWithBooleanState.doUpdateFor(Item controlling) {
-    controlling.setStateFromBoolean(this.getState());
+
+  //--- State Copy ---
+  public void Item.addControlling(Item controlledItem) { 
+    getRoot().addRule(createControllerRule(this,controlledItem));
+  }
+
+  public void Item.addControlledBy(Item controllerItem) { 
+    getRoot().addRule(createControllerRule(controllerItem,this));
+  }
+
+  
+  private Rule Item.createControllerRule(Item controllerItem, Item controlledItem) {
+    Rule rule = new Rule();
+
+    ItemStateChangeCondition condition = new ItemStateChangeCondition();
+    condition.setItem(controllerItem);
+    rule.addCondition(condition);
+
+    SetStateFromTriggeringItemAction action = new SetStateFromTriggeringItemAction();
+    action.setAffectedItem(controlledItem);
+    rule.addAction(action);
+    
+    return rule;
+    
+  }
+  
+
+  //--- copyStateTo ---
+  protected abstract void Item.copyStateTo(Item stateReceiver);
+  protected void ItemWithBooleanState.copyStateTo(Item stateReceiver) {
+    stateReceiver.setStateFromBoolean(this.getState());
   }
-  protected void ItemWithStringState.doUpdateFor(Item controlling) {
-    controlling.setStateFromString(this.getState());
+  protected void ItemWithStringState.copyStateTo(Item stateReceiver) {
+    stateReceiver.setStateFromString(this.getState());
   }
-  protected void ItemWithDoubleState.doUpdateFor(Item controlling) {
-    controlling.setStateFromDouble(this.getState());
+  protected void ItemWithDoubleState.copyStateTo(Item stateReceiver) {
+    stateReceiver.setStateFromDouble(this.getState());
   }
-  protected void ColorItem.doUpdateFor(Item controlling) {
-    controlling.setStateFromColor(this.getState());
+  protected void ColorItem.copyStateTo(Item stateReceiver) {
+    stateReceiver.setStateFromColor(this.getState());
   }
-  protected void DateTimeItem.doUpdateFor(Item controlling) {
-    controlling.setStateFromInstant(this.getState());
+  protected void DateTimeItem.copyStateTo(Item stateReceiver) {
+    stateReceiver.setStateFromInstant(this.getState());
   }
 
   private void ColorItem.setBrightness(int value) {
@@ -415,18 +438,12 @@ aspect ItemHandling {
   public void ItemUpdateColor.apply() {
     getItem().setStateFromColor(getNewHSB());
     getItem().freeze();
-    for (Item controller : getItem().getControlledByList()) {
-      controller.setStateFromColor(getNewHSB());
-    }
     getItem().unfreeze();
   }
   //--- ItemUpdate.apply ---
   public void ItemUpdateDouble.apply() {
     getItem().setStateFromDouble(getNewValue());
     getItem().freeze();
-    for (Item controller : getItem().getControlledByList()) {
-      controller.setStateFromDouble(getNewValue());
-    }
     getItem().unfreeze();
   }
 
diff --git a/eraser-base/src/main/jastadd/Printing.jrag b/eraser-base/src/main/jastadd/Printing.jrag
index fec6c4abfb0a9761e4970d3881adeead29a9464a..0b454039a9b84c02c68f2cef4593c0a334b99b37 100644
--- a/eraser-base/src/main/jastadd/Printing.jrag
+++ b/eraser-base/src/main/jastadd/Printing.jrag
@@ -49,7 +49,7 @@ aspect Printing {
         .build();
   }
 
-//ITEM_TYPE Item: id="" label="" state="" category="" topic="" controls=["ITEM_ID", "ITEM_ID"];
+//ITEM_TYPE Item: id="" label="" state="" category="" topic="";
   syn String Item.prettyPrint() {
     return new MemberPrinter(prettyPrintType())
         .addRequired("id", getID())
@@ -57,7 +57,6 @@ aspect Printing {
         .addRequired("state", getStateAsString())
         .addOptional("category", hasCategory(), () -> getCategory().getName())
         .addOptional("topic", hasTopic(), () -> getTopic().getTopicString())
-        .addIds("controls", getControllingList())
         .addNodes("metaData", getNumMetaData(), getMetaDataList(),
                   md -> "\"" + md.getKey() + "\":\"" + md.getValue() + "\"",
                   MemberPrinter.ListBracketType.CURLY)
@@ -86,7 +85,6 @@ aspect Printing {
         .addNonDefault("label", getLabel())
         .addOptional("category", hasCategory(), () -> getCategory().getName())
         .addOptional("topic", hasTopic(), () -> getTopic().getTopicString())
-        .addIds("controls", getControllingList())
         .addNodes("metaData", getNumMetaData(), getMetaDataList(),
                   md -> "\"" + md.getKey() + "\":\"" + md.getValue() + "\"",
                   MemberPrinter.ListBracketType.CURLY)
diff --git a/eraser-base/src/main/jastadd/Rules.jrag b/eraser-base/src/main/jastadd/Rules.jrag
index e1e0db7c7248016dd6db7bea0d6579994548cee9..464b3d3d2f132797e712573805158a507c694c6a 100644
--- a/eraser-base/src/main/jastadd/Rules.jrag
+++ b/eraser-base/src/main/jastadd/Rules.jrag
@@ -79,6 +79,7 @@ aspect Rules {
   syn boolean Condition.holdsFor(Item item);
   eq ItemStateCheckCondition.holdsFor(Item item) = getItemStateCheck().holdsFor(item);
   eq ExpressionCondition.holdsFor(Item item) = getLogicalExpression().eval();
+  eq ItemStateChangeCondition.holdsFor(Item item) = getItem() == item;
 
   // --- Action.applyFor ---
   public abstract void Action.applyFor(Item item);
@@ -101,7 +102,7 @@ aspect Rules {
     getAffectedItem().setStateFromString(getNewStateProvider().get());
   }
   public void SetStateFromTriggeringItemAction.applyFor(Item item) {
-    item.doUpdateFor(getAffectedItem());
+    item.copyStateTo(getAffectedItem());
   }
   public void SetStateFromItemsAction.applyFor(Item item) {
     getAffectedItem().setStateFromString(getCombinator().apply(getSourceItems()));
diff --git a/eraser-base/src/main/jastadd/Rules.relast b/eraser-base/src/main/jastadd/Rules.relast
index 3385858454a006cda3c052dbc41028684cb17d23..25a0fd2b41f3f1051e03d06ace4f41b6907030e6 100644
--- a/eraser-base/src/main/jastadd/Rules.relast
+++ b/eraser-base/src/main/jastadd/Rules.relast
@@ -2,6 +2,7 @@
 Rule ::= Condition* Action* ;
 abstract Condition ;
 ItemStateCheckCondition : Condition ::= ItemStateCheck ;
+ItemStateChangeCondition : Condition ::= Item;
 ExpressionCondition : Condition ::= LogicalExpression ;
 abstract Action ;
 NoopAction : Action ;
diff --git a/eraser-base/src/main/jastadd/eraser.flex b/eraser-base/src/main/jastadd/eraser.flex
index bc9b3e8d9a41c9bd4cc51d1879f081b32538f103..5f70414fa4005ad1baf1b0623459ce7c5f2f69f2 100644
--- a/eraser-base/src/main/jastadd/eraser.flex
+++ b/eraser-base/src/main/jastadd/eraser.flex
@@ -77,7 +77,6 @@ Comment = "//" [^\n\r]+
 "channels"     { return sym(Terminals.CHANNELS); }
 "channelTypes" { return sym(Terminals.CHANNEL_TYPES); }
 "context"      { return sym(Terminals.CONTEXT); }
-"controls"     { return sym(Terminals.CONTROLS); }
 "dbName"       { return sym(Terminals.DB_NAME); }
 "default"      { return sym(Terminals.DEFAULT); }
 "description"  { return sym(Terminals.DESCRIPTION); }
diff --git a/eraser-base/src/main/jastadd/eraser.parser b/eraser-base/src/main/jastadd/eraser.parser
index 5c275fa8c16d8a694608581b08640ae8a9db5451..68967a5655bf12cab2e2127db29e1f0ac5ebd808 100644
--- a/eraser-base/src/main/jastadd/eraser.parser
+++ b/eraser-base/src/main/jastadd/eraser.parser
@@ -125,15 +125,13 @@ Item item =
   |  ITEM COLON item_body.ib SEMICOLON                {: return eph.retype(new DefaultItem(), ib); :}
   ;
 
-// ITEM_TYPE Item: id="" label="" state="" category="" topic="" controls=["ITEM_ID"] metaData={"key":"value"} ;
+// ITEM_TYPE Item: id="" label="" state="" category="" topic="" 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); :}
-  |  CONTROLS EQUALS string_list.controlling item_body.i
-    {: return eph.setControlling(i, controlling); :}
   |  META_DATA EQUALS string_map.md item_body.i
     {: return eph.setMetaData(i, md); :}
   |                                     {: return eph.createItem(); :}
diff --git a/eraser-base/src/main/jastadd/shem.relast b/eraser-base/src/main/jastadd/shem.relast
index 34ad05feba959e8f1b39449ec56ba96e3dc7a14f..ca1878ac6d2722a3e2a1671e066b19c5175c7be6 100644
--- a/eraser-base/src/main/jastadd/shem.relast
+++ b/eraser-base/src/main/jastadd/shem.relast
@@ -27,7 +27,6 @@ ParameterDefaultValue ::= <Value:String> ;
 
 abstract Item : LabelledModelElement ::= <_fetched_data:boolean> MetaData:ItemMetaData* [ItemObserver] ;
 rel Item.Category? -> ItemCategory ;
-rel Item.Controlling* <-> Item.ControlledBy* ;
 
 abstract ItemWithBooleanState : Item ::= <_state:boolean> ;
 abstract ItemWithStringState : Item ::= <_state:String> ;
diff --git a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/Application.java b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/Application.java
index c646b0e5fe74e09998894767e12f249158f6fbd4..d8a76db65406f94d78f1197ab2fbf8fde10e61f1 100644
--- a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/Application.java
+++ b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/Application.java
@@ -243,7 +243,6 @@ public class Application {
         item.getTopic() != null ? item.getTopic().getTopicString() : null,
         item.isFrozen(),
         item.isSendState(),
-        item.getControllingList().stream().map(Item::getID).collect(Collectors.toList()),
         wrapMetaData(item.getMetaDataList()));
   }
 
diff --git a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleItem.java b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleItem.java
index 2eac624367215c547ef4cfe0058fdcce337062fa..cd18057f917fedb077a237c2618b2e6e9d0c26d0 100644
--- a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleItem.java
+++ b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleItem.java
@@ -17,6 +17,5 @@ public class SimpleItem {
   public final String topic;
   public final boolean frozen;
   public final boolean sendState;
-  public final List<String> controlling;
   public final Map<String, String> metaData;
 }