diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1fd92e48fc22eb5ac473e1464e79e3ae52c0515f..8fbc29ce31343092a641feffdde3f65a089c7dfa 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -119,7 +119,7 @@ pages:
     - cd pages && mkdocs build
   only:
     - dev
-    - master
+    - main
   artifacts:
     paths:
       - "public"
diff --git a/README.md b/README.md
index be3f3b866e5a966523c8fa8f54b086a1ae181049..af4d2c9d8bd6f40b3984c3502c200a0d6115a711 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 There are the following subprojects in this repository:
 
 - Base
-	- **eraser-base**: The core part of the framework containing the [grammar](/../../blob/master/eraser-base/src/main/jastadd/main.relast), various attributes, the [parser](/../../blob/master/eraser-base/src/main/jastadd/eraser.parser) for model specification files, and Java classes for the [openHAB communication](/../../tree/master/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2) via [MQTT](/../../blob/master/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/mqtt/MQTTUpdater.java)
+	- **eraser-base**: The core part of the framework containing the [grammar](/../../blob/main/eraser-base/src/main/jastadd/main.relast), various attributes, the [parser](/../../blob/main/eraser-base/src/main/jastadd/eraser.parser) for model specification files, and Java classes for the [openHAB communication](/../../tree/main/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2) via [MQTT](/../../blob/main/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/mqtt/MQTTUpdater.java)
 - Utility
 	- **commons.color**: Utilities for converting color spaces (XYZ, RGB, HSB)
 	- **eraser.rest** and **eraser.spark**: REST-API to communicate with the framework
@@ -25,15 +25,6 @@ There are the following subprojects in this repository:
 
 This project uses Gradle as the build tool. For detailed information, see [setup guidelines](/../../wikis/setup)
 
-## Running the demo
-
-There is a demo project, as explained [above](#overview) to showcase the framework.
-To start it, run `./gradlew :skywriter-hue-integration:run`
-
-This demo is intended to run with a pre-configured openHAB instance and has some hard-coded settings.
-It will first try to connect to an MQTT broker (with a hard-coded IP) to listen on changes of item states.
-There is one rule implemented, namely to listen on changes of the item `skywriter_xyz` expecting MQTT messages of the form `XVALUE,YVALUE,ZVALUE`, and to change to state of another item `iris1_item` representing the HSB value of a Hue Iris.
-
 ## Trivia
 
 The name *Eraser* is a small pun on the term *eRACR*, which is an extension of *RACR* for event recognition. It was chosen to ease the search for a good repository image as it is based on a [film](https://en.wikipedia.org/wiki/Eraser_(film)).
diff --git a/eraser.starter/build.gradle b/eraser.starter/build.gradle
index 739e399f957a3b55b859384c45a078104e51a0bb..26a0364217deda509bf80593c9f10f976d84e696 100644
--- a/eraser.starter/build.gradle
+++ b/eraser.starter/build.gradle
@@ -19,6 +19,31 @@ dependencies {
 
 }
 
+def versionFile = 'src/main/resources/eraser.properties'
+def oldProps = new Properties()
+
+try {
+    file(versionFile).withInputStream { stream -> oldProps.load(stream) }
+    version = oldProps['version']
+} catch (e) {
+    // this happens, if either the properties file is not present, or cannot be read from
+    throw new GradleException("File ${versionFile} not found or unreadable. Aborting.", e)
+}
+
+task printVersion() {
+    doLast {
+        println(version)
+    }
+}
+
+task newVersion() {
+    doFirst {
+        def props = new Properties()
+        props['version'] = value
+        props.store(file(versionFile).newWriter(), null)
+    }
+}
+
 application.mainClass = 'de.tudresden.inf.st.eraser.starter.EraserStarter'
 
 import io.github.httpbuilderng.http.HttpTask
diff --git a/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/EraserStarter.java b/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/EraserStarter.java
index ce6e008bc1f02e5e7c35aeb4452f96f04fccdfcb..83815a11615b3b2c1c8f851772c9a84208e4f316 100644
--- a/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/EraserStarter.java
+++ b/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/EraserStarter.java
@@ -24,8 +24,11 @@ import org.apache.logging.log4j.Logger;
 
 import java.io.File;
 import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
@@ -52,8 +55,12 @@ public class EraserStarter {
 
   private static final Logger logger = LogManager.getLogger(EraserStarter.class);
 
-  @SuppressWarnings("ResultOfMethodCallIgnored")
   public static void main(String[] args) {
+    EraserStarter starter = new EraserStarter();
+    starter.run(args);
+  }
+
+  public void run(String[] args) {
     ArgumentParser parser = ArgumentParsers.newFor("eraser").build()
         .defaultHelp(true)
         .description("Starts the knowledge-base of OpenLicht");
@@ -79,7 +86,7 @@ public class EraserStarter {
       System.exit(1);
       return;
     }
-    logger.info("Starting ERASER");
+    logger.info("Starting ERASER " + readVersion());
     boolean startRest = settings.rest.use;
 
     Root root;
@@ -102,7 +109,7 @@ public class EraserStarter {
       default:
         try {
           root = ParserUtils.load(settings.load.realURL());
-          model = root.getSmartHomeEntityModel();
+          root.getSmartHomeEntityModel();
         } catch (IOException | Parser.Exception e) {
           logger.error("Problems parsing the given file {}", settings.load.file);
           logger.catching(e);
@@ -194,6 +201,7 @@ public class EraserStarter {
       logger.info("No REST server this time");
       System.out.println("Hit [Enter] to exit");
       try {
+        //noinspection ResultOfMethodCallIgnored
         System.in.read();
       } catch (IOException e) {
         e.printStackTrace();
@@ -222,7 +230,7 @@ public class EraserStarter {
     logger.info("I'm done here.");
   }
 
-  private static MachineLearningHandlerFactory createFactory(MachineLearningHandlerFactory.MachineLearningHandlerFactoryTarget target, Setting.MLContainer config, Root root) {
+  private MachineLearningHandlerFactory createFactory(MachineLearningHandlerFactory.MachineLearningHandlerFactoryTarget target, Setting.MLContainer config, Root root) {
     MachineLearningHandlerFactory factory;
     String niceTargetName = target.toString().toLowerCase().replace("_", " ");
     if (config.dummy || config.factory == null) {
@@ -232,10 +240,11 @@ public class EraserStarter {
       try {
         Class<? extends MachineLearningHandlerFactory> clazz = Class.forName(config.factory)
             .asSubclass(MachineLearningHandlerFactory.class);
-        factory = clazz.newInstance();
+        factory = clazz.getDeclaredConstructor().newInstance();
         factory.setKnowledgeBaseRoot(root);
         factory.initializeFor(target, config.realURL());
-      } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IOException e) {
+      } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IOException |
+          NoSuchMethodException | InvocationTargetException e) {
         logger.error("Could not instantiate machine learning factory for {} with class '{}'.",
             niceTargetName, config);
         logger.catching(e);
@@ -250,4 +259,14 @@ public class EraserStarter {
     }
     return factory;
   }
+
+  private String readVersion() {
+    try {
+      ResourceBundle resources = ResourceBundle.getBundle("eraser");
+      return resources.getString("version");
+    } catch (MissingResourceException e) {
+      return "version ?";
+    }
+  }
+
 }
diff --git a/eraser.starter/src/main/resources/eraser.properties b/eraser.starter/src/main/resources/eraser.properties
new file mode 100644
index 0000000000000000000000000000000000000000..8066d3403a20424c785a5cb8c8df599d7a449db8
--- /dev/null
+++ b/eraser.starter/src/main/resources/eraser.properties
@@ -0,0 +1,2 @@
+#Sun Jun 20 11:43:25 CEST 2021
+version=1.0.0
diff --git a/pages/.gitignore b/pages/.gitignore
index a95071c4b0fd05bccf528339a15b2e93458bc694..14338f39f89e2a45e3473a6597e640aa9458f9a8 100644
--- a/pages/.gitignore
+++ b/pages/.gitignore
@@ -1 +1,2 @@
 /docs/ragdoc/
+__pycache__
diff --git a/pages/custom_theme/footer.html b/pages/custom_theme/footer.html
index e76f0a2b54f97d9b338c9ca8cf36646da77e35a1..2d6c164695b3a1ed19711d3497caf142a8416ebb 100644
--- a/pages/custom_theme/footer.html
+++ b/pages/custom_theme/footer.html
@@ -4,8 +4,8 @@
 {% endif %}
 <hr>
 Built with <a href="https://www.mkdocs.org/">MkDocs</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
-{% if page and page.meta and page.meta.revision_date %}
-<small><br><i>Last updated {{ page.meta.revision_date.strftime('%B %d, %Y at %H:%M') }}</i></small>
+{% if page and page.meta and page.meta.git_revision_date_localized %}
+<small><br><i>Last updated {{ page.meta.git_revision_date_localized }}</i></small>
 {% endif %}
 </p>
 {% endblock %}
diff --git a/pages/docs/DSL.md b/pages/docs/DSL.md
index c58ec900fdab199ba904b915758c409fc1e1569c..7ac5c9f9b6c5dde3768c40a68e804be9fa5c6d50 100644
--- a/pages/docs/DSL.md
+++ b/pages/docs/DSL.md
@@ -1,11 +1,11 @@
 # The eraser-DSL
 
-Find a general description in the [Model description](Model-description) page, and the parser ([dev](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/main/jastadd/eraser.parser), [master](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/master/eraser-base/src/main/jastadd/eraser.parser)) and printing ([dev](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/main/jastadd/Printing.jrag), [master](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/master/eraser-base/src/main/jastadd/Printing.jrag)).
+Find a general description in the [Model description](Model-description) page, and the parser ([dev](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/main/jastadd/eraser.parser), [main](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/main/eraser-base/src/main/jastadd/eraser.parser)) and printing ([dev](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/main/jastadd/Printing.jrag), [main](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/main/eraser-base/src/main/jastadd/Printing.jrag)).
 
 The DSL is line-based, so on each line, one model element will be defined.
 All shown specifications for one element are optional and can be reordered, but at least `id` must be present to identify the element.
 References to other elements are done by using their IDs as strings, and can be forward references.
-The following elements can be described (in the master branch if not otherwise noted):
+The following elements can be described (in the main branch if not otherwise noted):
 
 ## ChannelType
 
@@ -15,7 +15,7 @@ ChannelType: id="" label="" description="" itemType="" category="" readyOnly;
 
 - The item type has to be one of the [item names defined for Eclipse Smart Home](https://www.eclipse.org/smarthome/documentation/concepts/items.html)
 - The category can either be one of the [categories defined for Eclipse Smart Home](https://www.eclipse.org/smarthome/documentation/concepts/categories.html#channel-categories), or a custom one.
-- Both item type and category are defined as enums ([dev](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/main/jastadd/enums.jadd), [master](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/master/eraser-base/src/main/jastadd/enums.jadd))
+- Both item type and category are defined as enums ([dev](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/main/jastadd/enums.jadd), [main](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/main/eraser-base/src/main/jastadd/enums.jadd))
 - The flag `readOnly` is currently without any effect
 
 ## Channel
diff --git a/pages/docs/MachineLearning.md b/pages/docs/MachineLearning.md
index de5ab0652dca58141d4f976bd4f1f46c76480a89..ff7f416cc127077db0553211655f215341e4252b 100644
--- a/pages/docs/MachineLearning.md
+++ b/pages/docs/MachineLearning.md
@@ -1,6 +1,6 @@
 # Machine Learning Basics
 
-The relevant parts of the grammar ([dev](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/main/jastadd/main.relast) | [master](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/main/jastadd/main.relast)) are the following (updated on Feburary, 11th 2019):
+The relevant parts of the grammar ([dev](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/main/jastadd/main.relast) | [main](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/main/jastadd/main.relast)) are the following (updated on Feburary, 11th 2019):
 
 ## General information machine learning
 
diff --git a/pages/docs/Model-description.md b/pages/docs/Model-description.md
index f3edf840caf5ccccc2df01cab11c2b8e8ff21ae1..58d867236afe194910e58916e10a92a074637f3f 100644
--- a/pages/docs/Model-description.md
+++ b/pages/docs/Model-description.md
@@ -1,14 +1,14 @@
 # Model description
 
-The latest version of the model: [master](https://git-st.inf.tu-dresden.de/rschoene/eraser/blob/master/eraser-base/src/main/jastadd/main.relast) | [dev](https://git-st.inf.tu-dresden.de/rschoene/eraser/blob/dev/eraser-base/src/main/jastadd/main.relast)
+The latest version of the model: [main](https://git-st.inf.tu-dresden.de/rschoene/eraser/blob/main/eraser-base/src/main/jastadd/main.relast) | [dev](https://git-st.inf.tu-dresden.de/rschoene/eraser/blob/dev/eraser-base/src/main/jastadd/main.relast)
 
 Below, all parts of the model are described in more detail. Each part resides in a separate grammar file.
 
 ## Main
 
-Root ::= OpenHAB2Model User* MqttRoot InfluxRoot MachineLearningRoot Rule* Location* ;
+`Root ::= SmartHomeEntityModel User* MqttRoot InfluxRoot MachineLearningRoot Rule* Location* ;`
 
-- `OpenHAB2Model`: Content from openHAB
+- `SmartHomeEntityModel`: Smart home entities
 - `User*`: Users in the system (work in progress)
 - `MqttRoot`: Everything needed for MQTT (communication with openHAB)
 - `InfluxRoot`: Everything needed for Influx (time series database for item state history)
@@ -16,16 +16,16 @@ Root ::= OpenHAB2Model User* MqttRoot InfluxRoot MachineLearningRoot Rule* Locat
 - `Rule*`: Self-made ECA rules
 - `Location*`: Locations associated with users, activities, preferences (work in progress)
 
-## openHAB
+## Smart Home Entity Model
 
-> OpenHAB2Model ::= Thing* Group* ThingType* ChannelType* ChannelCategory* ItemCategory* /ActivityItem:Item/
+`SmartHomeEntityModel ::= Thing* Group* ThingType* Parameter* ChannelType* Channel* ItemCategory* /ActivityItem:Item/ FrequencySetting* ;`
 
 The structure is mostly extracted from openHAB. There are some abstract superclasses to share common token, such as `ID`, `label` or `description`. Some differences to openHAB include:
 
-- Links are not modelled explicitely, but instead the items are directly referenced within channels
+- Links are not modelled explicitly, but instead the items are directly referenced within channels
 - Item type was reified instead of a token describing the type. This has the advantage, that the state can be used in a more type-safe way. On the downside, more code is required in some instances, such as for item history, and there is no easy `getState` method.
 
-Explainations for the other types:
+Explanations for the other types:
 
 - **Things** represent physical entities, such as a Hue lamp, a complex (or simple) sensor, a monitor, a webservice. They are typed and described by ThingTypes.
 - **ThingTypes** describe the blueprint for a Thing, like a class does for objects in OO languages. They also have **ChannelTypes** describing possible capabilities.
@@ -46,13 +46,22 @@ Basically, here are just some tokens containing information for connection to In
 
 Handling of machine learning models currently undergoes a change.
 
-There is a distinction between internal and external models, both implement a encoder interface to get data into those models, and decoder interface to extract data from the models.
+There is a distinction between internal and external models, both implement an encoder interface to get data into those models, and decoder interface to extract data from the models.
 There are two internal models which are supported: decision trees and neural networks.
 
-Decision tree are modelled straight-forward, there are inner nodes and leafs. Inner nodes reference an item and check a certain condition for this item to decide whether to follow the left or the right path of the inner node. The classification is equal to the leaf node at the end of the path.
+Decision tree are modelled straight-forward, there are inner nodes and leaves. Inner nodes reference an item and check a certain condition for this item to decide whether to follow the left or the right path of the inner node. The classification is equal to the leaf node at the end of the path.
 
 Neural networks are also straight-forward, but probably inefficient for bigger networks in the current implementation. Every neuron is a non-terminal which has ingoing and outgoing connections (using bidirectional relations), input neurons reference an item and propagate its state, hidden and output neurons have an activation function, bias neurons always return a constant value. The output layer contains all output neurons and has a combinator function to calculate the classification result.
 
 ## Rules
 
 Simple ECA rules can be modelled. A rule has a list of conditions and a list of actions. An event can be triggered by some items (via their ItemObserver), i.e., whenever an item changes its state to a different value. Conditions use a logical expression, which can contain simple mathematical expressions, comparisons, and simple logical operations. Actions are organized currently by their purpose, but are wll be simplified to reference only affected items and a number expression to calculate the new value for those items.
+
+### StateSyncGroup
+
+#### What does it do?
+Item states can be synchronized through StateSyncGroups. Once an item belongs to such a group, all of its state changes will be applied and then sent to other items in that group.
+The behavior of the other items in the group following the order to change the state is up to the item type. This is how state transfers between different item types can be managed. For example if a ColorItem (currently at `1%` brightness) and a SwitchItem (currently at value `false`) are in a StateSyncGroup and the SwitchItem is changing to `true` then the ColorItem won't change to 100%. Instead, it keeps its old value (`1%`).
+
+#### How does it work?
+The StateSyncGroup as an entity does only exist while parsing. After resolving all references, it is rewritten (using JastAdd rewrites). As a result, an ECA rule emerges that has conditions and actions whose functionality correspond to the explained behavior in the previous paragraph.
diff --git a/pages/docs/index.md b/pages/docs/index.md
index a341cbda6a3c9c72aa825cc86ff4856bf86a412d..afb6bbd8ac2d638ef8cb057419b499d4433a7fa2 100644
--- a/pages/docs/index.md
+++ b/pages/docs/index.md
@@ -1,6 +1,6 @@
 # Documentation of Eraser
 
-First, setup eraser following the README in the main repository: [dev](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/README.md) | [master](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/master/README.md)
+First, setup eraser following the README in the main repository: [dev](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/README.md) | [main](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/main/README.md)
 To configure the system, read the [configuration page](config).
 
 For an overview of the model, check out the [Model description](Model-description).
diff --git a/pages/main.py b/pages/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..c3da017406f772426a95f1754057951b8d66ff5b
--- /dev/null
+++ b/pages/main.py
@@ -0,0 +1,25 @@
+eraserVersionFileName = '../eraser.starter/src/main/resources/eraser.properties'
+
+
+def get_version():
+    with open(eraserVersionFileName) as eraserVersionFile:
+        versionFileContent = eraserVersionFile.read()
+    return versionFileContent[versionFileContent.rindex('version=') + 8:].strip()
+
+
+def define_env(env):
+    """
+    This is the hook for defining variables, macros and filters
+
+    - variables: the dictionary that contains the environment variables
+    - macro: a decorator function, to declare a macro.
+    """
+    env.conf['site_name'] = 'Documentation of Eraser ' + get_version()
+
+    @env.macro
+    def eraser_version():
+        return get_version()
+
+
+if __name__ == '__main__':
+    print(get_version())
diff --git a/pages/mkdocs.yml b/pages/mkdocs.yml
index e4410b59ba5f3b4eb406b9cfbd0d9bfd8e8e5197..e8b9ac7fe4dd3d3cc52be9581baf02305f5c429f 100644
--- a/pages/mkdocs.yml
+++ b/pages/mkdocs.yml
@@ -17,8 +17,11 @@ theme:
   custom_dir: custom_theme/
 plugins:
   - search
-  - git-revision-date:
-      as_datetime: True
+  - git-revision-date-localized:
+      type: datetime
+      timezone: Europe/Berlin
+      locale: en
+      fallback_to_build_date: True
   - macros
 repo_url: https://git-st.inf.tu-dresden.de/OpenLicht/eraser
 site_dir: ../public