diff --git a/README.md b/README.md
index 70f0eed138676e90179fcdb6b41adc6850974f71..a50f7119bb0323be304f19b1e4a4ffec603f5d1a 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 [![Build](https://git-ce.rwth-aachen.de/wzl-mq-ms/forschung-lehre/lava/unified-device-interface/python/badges/master/pipeline.svg)](https://git-ce.rwth-aachen.de/wzl-mq-ms/forschung-lehre/lava/unified-device-interface/python/commits/master)
 
 # Python Unified Device Interface
-Current stable version: 9.3.8
+Current stable version: 10.0.0
 
 ## Installation
 1. Install the WZL-UDI package via pip
@@ -58,6 +58,10 @@ Funded by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation)
 
 ## Recent changes
 
+**10.0.0** - 2024-03-21
+  - the getter method of a measurement must always return a tuple of value and uncertainty quantification, if the uncertainty is not applicable None must be returned for the uncertainty
+  - semantic path for Observation and MeasurementResults can now be resolved
+
 **9.3.8** - 2024-03-19
   - fixed semantic serialization of integer variables
 
diff --git a/setup.py b/setup.py
index da591dcd01a36c2ad1e919a9b8e4c4a187a072a1..2ad2d8163f5678be1ec035657c783ef179202450 100644
--- a/setup.py
+++ b/setup.py
@@ -4,7 +4,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
     long_description = fh.read()
 
 setup(name='wzl-udi',
-      version='9.3.8',
+      version='10.0.0',
       url='https://git-ce.rwth-aachen.de/wzl-mq-public/soil/python',
       project_urls={
           "Bug Tracker": "https://git-ce.rwth-aachen.de/wzl-mq-public/soil/python/-/issues",
diff --git a/src/http/server.py b/src/http/server.py
index f78cf4a6b9d5e22b8414249722767158b6ed6281..bd859843343a4fd0963fa6e2940018297f7756f3 100644
--- a/src/http/server.py
+++ b/src/http/server.py
@@ -216,7 +216,7 @@ class HTTPServer(object):
                     (rdflib.URIRef(Semantics.namespace[profilename]), Namespaces.dcterms.license, Semantics.profile_license))
                 item, status = None, 200
 
-            elif resource_type == ResourceType.metadata or resource_type == ResourceType.data:
+            elif resource_type.is_semantic():
                 semantic_name = request.url.parts[-2] if request.url.parts[-1] == '' else request.url.parts[-1]
 
                 item, resource_type = self.root.resolve_semantic_path(semantic_name)
diff --git a/src/soil/measurement.py b/src/soil/measurement.py
index 741c16681b9ffba7142dd4e60c88521161019a1a..a414e77b08a4b7e32289b2fe097d567b8411c9c9 100644
--- a/src/soil/measurement.py
+++ b/src/soil/measurement.py
@@ -1,5 +1,7 @@
+import asyncio
 import copy
 import datetime
+import inspect
 import warnings
 from typing import Dict, Callable, List
 
@@ -7,10 +9,11 @@ import rdflib
 from deprecated import deprecated
 
 from .datatype import Datatype
-from .variable import Variable
+from .error import ChildNotFoundException
+from .variable import Variable, serialize_time
 from .semantics import Semantics, Namespaces
 from ..utils import root_logger
-from ..utils.constants import HTTP_GET
+from ..utils.constants import HTTP_GET, HTTP_OPTIONS
 from ..utils.error import SerialisationException, DeviceException
 from ..utils.resources import ResourceType
 
@@ -73,6 +76,33 @@ class Measurement(Variable):
             return self._label
         if item == 'label':
             return self._label
+        if item == "value":
+            if method != HTTP_OPTIONS:
+                try:
+                    if inspect.iscoroutinefunction(self.get):
+                        loop = asyncio.get_event_loop()
+                        value, covariance = loop.run_until_complete(asyncio.gather(self.get()))[0]
+                    else:
+                        value, covariance = self.get()
+
+                    if self._datatype == Datatype.TIME:
+                        value = serialize_time(value)
+                        if covariance is not None:
+                            covariance = serialize_time(value)
+                    elif self._datatype == Datatype.ENUM:
+                        value = str(value)
+
+                except Exception as e:
+                    raise DeviceException(
+                        'Could not provide value of Measurement/Parameter {}: {}'.format(self.uuid, str(e)),
+                        predecessor=e)
+
+                Variable.check_all(self._datatype, self._dimension, self._range, value)
+                self._value = value
+                self._covariance = covariance
+                return value
+            else:
+                return self._value
         if item == 'covariance':
             return self._covariance
         # if item == 'uncertainty':
@@ -188,6 +218,7 @@ class Measurement(Variable):
             for subject in subjects:
                 if subject != Semantics.namespace[f'{self._semantic_name}Range']:
                     result.remove((subject, None, None))
+
         elif resource_type == ResourceType.data:
             data_graph = rdflib.Graph()
             data_graph.bind('sosa', Namespaces.sosa)
@@ -195,45 +226,87 @@ class Measurement(Variable):
             data_graph.bind('soil', Namespaces.soil)
             data_graph.bind('qudt', Namespaces.qudt)
             data_graph.bind('unit', Namespaces.unit)
-            observation_subject = Semantics.namespace[f'{self._semantic_name}Observation']
-
-            sensor_triples = list(self._metadata.triples((None, Namespaces.sosa.isObservedBy, None)))
-            assert len(sensor_triples) == 1
-
-            # create observation node
-            data_graph.add((observation_subject, Namespaces.rdf.type, rdflib.URIRef(Namespaces.sosa.Observation)))
-            data_graph.add((observation_subject, Namespaces.schema.name, rdflib.Literal(f'{self._name} Observation')))
-            data_graph.add(
-                (observation_subject, Namespaces.sosa.observedProperty, Semantics.namespace[self._semantic_name]))
-            data_graph.add((observation_subject, Namespaces.sosa.hasResult,
-                            Semantics.namespace[f'{self._semantic_name}Measurement']))
-            data_graph.add((observation_subject, Namespaces.sosa.madeBySensor, sensor_triples[0][2]))
-            data_graph.add((observation_subject, Namespaces.schema.license, Semantics.data_license))
+            data_graph.bind('earl', Namespaces.earl)
+            measurement_subject = Semantics.namespace[f'{self._semantic_name}Measurement']
+            result_subject = Semantics.namespace[f'{self._semantic_name}MeasurementResult']
+            uncertainty_subject = Semantics.namespace[f'{self._semantic_name}MeasurementUncertainty']
 
             # create result node
             unit_triples = list(self._metadata.triples((None, Namespaces.qudt.applicableUnit, None)))
             assert len(unit_triples) == 1
 
-            measurement_subject = Semantics.namespace[f'{self._semantic_name}Measurement']
-            data_graph.add((measurement_subject, Namespaces.rdf.type, rdflib.URIRef(Namespaces.sosa.Result)))
-            data_graph.add((measurement_subject, Namespaces.rdf.type, rdflib.URIRef(Namespaces.soil.Measurement)))
-            data_graph.add((measurement_subject, Namespaces.sosa.isResultOf, observation_subject))
-            data_graph.add((measurement_subject, Namespaces.qudt.unit, unit_triples[0][2]))
-            data_graph.add((measurement_subject, Namespaces.schema.license, Semantics.data_license))
+            data_graph.add((result_subject, Namespaces.rdf.type, rdflib.URIRef(Namespaces.sosa.Result)))
+            data_graph.add((result_subject, Namespaces.sosa.isResultOf, measurement_subject))
+            data_graph.add((result_subject, Namespaces.qudt.unit, unit_triples[0][2]))
+            data_graph.add((result_subject, Namespaces.schema.license, Semantics.data_license))
 
             rdf_value = self.serialize_value(data_graph, self.__getitem__('value', 0))
+            data_graph.add((result_subject, Namespaces.qudt.value, rdf_value))
+            data_graph.add((result_subject, Namespaces.m4i.hasUncertaintyDeclaration, uncertainty_subject))
 
-            data_graph.add((measurement_subject, Namespaces.qudt.value, rdf_value))
-            data_graph.add((measurement_subject, Namespaces.schema.dateCreated,
+            data_graph.add((result_subject, Namespaces.schema.dateCreated,
                             rdflib.Literal(datetime.datetime.now().astimezone())))
 
-            # TODO add uncertainty
+            result = data_graph
+
+        elif resource_type == ResourceType.uncertainty:
+            data_graph = rdflib.Graph()
+            data_graph.bind(Semantics.prefix, Semantics.namespace)
+            uncertainty_subject = Semantics.namespace[f'{self._semantic_name}MeasurementUncertainty']
+            covariance = self.__getitem__('covariance', 0)
+            print(covariance)
+            if covariance is not None:
+                rdf_covariance = self.serialize_value(data_graph, covariance)
+                data_graph.add((uncertainty_subject, Namespaces.rdf.type, Namespaces.si.CoverageInterval))
+                data_graph.add((uncertainty_subject, Namespaces.si.hasStandardUnc, rdf_covariance))
+
+            else:
+                data_graph.bind('earl', Namespaces.earl)
+                data_graph.add((uncertainty_subject, Namespaces.rdf.type, Namespaces.earl.NotApplicable))
+
+            data_graph.add((uncertainty_subject, Namespaces.schema.license, Semantics.data_license))
+            result = data_graph
+
+        elif resource_type == ResourceType.observation:
+            data_graph = rdflib.Graph()
+            data_graph.bind('sosa', Namespaces.sosa)
+            data_graph.bind(Semantics.prefix, Semantics.namespace)
+            data_graph.bind('soil', Namespaces.soil)
+            data_graph.bind('qudt', Namespaces.qudt)
+            data_graph.bind('unit', Namespaces.unit)
+            measurement_subject = Semantics.namespace[f'{self._semantic_name}Measurement']
+            result_subject = Semantics.namespace[f'{self._semantic_name}MeasurementResult']
+
+            sensor_triples = list(self._metadata.triples((None, Namespaces.sosa.isObservedBy, None)))
+            assert len(sensor_triples) == 1
+
+            # create observation node
+            data_graph.add((measurement_subject, Namespaces.rdf.type, rdflib.URIRef(Namespaces.sosa.Observation)))
+            data_graph.add((measurement_subject, Namespaces.schema.name, rdflib.Literal(f'{self._name} Measurement')))
+            data_graph.add(
+                (measurement_subject, Namespaces.sosa.observedProperty, Semantics.namespace[self._semantic_name]))
+            data_graph.add((measurement_subject, Namespaces.sosa.hasResult, result_subject))
+            data_graph.add((measurement_subject, Namespaces.sosa.madeBySensor, sensor_triples[0][2]))
+            data_graph.add((measurement_subject, Namespaces.schema.license, Semantics.data_license))
 
             result = data_graph
         else:
             raise DeviceException('The provided kind of semantic information cannot be returned.')
         return result
 
+    def resolve_semantic_path(self, suffix: str) -> ('Element', ResourceType):
+        try:
+            return super().resolve_semantic_path(suffix)
+        except ChildNotFoundException:
+            if suffix == f'{self.semantic_name.split("/")[-1]}MeasurementResult':
+                return self, ResourceType.data
+            elif suffix == f'{self.semantic_name.split("/")[-1]}MeasurementUncertainty':
+                return self, ResourceType.uncertainty
+            elif suffix == f'{self.semantic_name.split("/")[-1]}Measurement':
+                return self, ResourceType.observation
+
+            raise ChildNotFoundException('Could not resolve the semantic path.')
+
     @property
     def semantic_name(self) -> str:
         if self._metadata is None:
diff --git a/src/soil/semantics.py b/src/soil/semantics.py
index 7a3f80159afb64a0937ab0064295ad9b6e0bc17d..9908084f3911b2d27ac9ab4e180379aaa4f5a431 100644
--- a/src/soil/semantics.py
+++ b/src/soil/semantics.py
@@ -37,6 +37,7 @@ class Semantics(object):
 
 class Namespaces(object):
     dcterms = rdflib.namespace.DCTERMS
+    earl = rdflib.Namespace('http://www.w3.org/ns/earl#')
     m4i = rdflib.Namespace('http://w3id.org/nfdi4ing/metadata4ing#')
     owl = rdflib.Namespace('http://www.w3.org/2002/07/owl#')
     quantitykind = rdflib.Namespace('http://qudt.org/vocab/quantitykind/')
diff --git a/src/soil/stream.py b/src/soil/stream.py
index ba6b4551159705bb0f43fd00c8629bb804932c7d..3b6ed19bef93abf8368ca507f91b91727d0706a3 100644
--- a/src/soil/stream.py
+++ b/src/soil/stream.py
@@ -1,16 +1,15 @@
 import datetime
 import json
 from abc import ABC, abstractmethod
-from typing import List, Callable, Any, Union, Dict
+from typing import List, Callable, Any, Union, Dict, Tuple
 
 import rdflib
 from wzl.mqtt.client import MQTTPublisher
 from wzl.mqtt.exceptions import ClientNotFoundError
 
+from . import variable
 from .component import Component
 from .event import Event
-from . import variable
-from .variable import Variable
 from .semantics import Namespaces
 from ..utils import root_logger
 from ..utils import serialize
@@ -61,7 +60,12 @@ class Job(ABC):
         ...
 
     @property
-    def value(self) -> Any:
+    def value(self) -> Tuple[Any, Any]:
+        """
+
+        Returns: the value together with the covariance, which might None
+
+        """
         return self._callback()
 
     def is_triggered(self, time: datetime.datetime = None) -> bool:
@@ -91,52 +95,45 @@ class Job(ABC):
     def stop(self) -> None:
         self._next = None
 
-    def _retrieve_metadata(self, model: Component = None):
-        if model is None:
-            return {}
-        try:
-            uuids = self.topic.split('/')
-            metadata = model.__getitem__(uuids).serialize([], False)
-        except Exception:
-            return {}
-        return metadata
-
-    def _retrieve_semantic_metadata(self, model: Component = None) -> (str, rdflib.Graph):
+    def data(self, model: Component = None) -> Dict:
         if model is None:
-            return "", rdflib.Graph()
+            raise JobError()
         try:
             uuids = self.topic.split('/')
-            element = model.__getitem__(uuids)
-            return element.semantic_name, element.serialize_semantics(ResourceType.data)
-        except Exception:
-            return "", rdflib.Graph()
+            data = model.__getitem__(uuids).serialize([], False)
 
-    def data(self, model: Component = None) -> Dict:
-        try:
-            data = self._retrieve_metadata(model)
+            value, covariance = self.value
             data['uuid'] = self.topic
-            data['value'] = self.value
+            data['value'] = value
+            data['covariance'] = covariance
             data['timestamp'] = variable.serialize_time(datetime.datetime.now())
             return data
         except Exception as e:
             raise JobError()
 
     def semantic_data(self, model: Component = None) -> (str, rdflib.Graph):
+        if model is None:
+            raise JobError()
         try:
-            url, data = self._retrieve_semantic_metadata(model)
+            uuids = self.topic.split('/')
+            element = model.__getitem__(uuids)
+            data = element.serialize_semantics(ResourceType.data)
+            data += element.serialize_semantics(ResourceType.uncertainty)
+            data += element.serialize_semantics(ResourceType.observation)
+
             measurement_subject = \
                 list((data.subjects(predicate=Namespaces.rdf.type, object=Namespaces.soil.Measurement)))[0]
 
             # replace value
             data.remove((None, Namespaces.qudt.value, None))
-            data.add((measurement_subject, Namespaces.qudt.value, self.serialize_value(data, self.value)))
+            value, covariance = self.value
+            data.add((measurement_subject, Namespaces.qudt.value, element.serialize_value(data, value)))
 
             # replace timestamp
             data.remove((None, Namespaces.schema.dateCreated, None))
             data.add((measurement_subject, Namespaces.schema.dateCreated, rdflib.Literal(datetime.datetime.now())))
 
-            # TODO add the uncertainty/covariance
-            return url, data
+            return element.semantic_name, data
         except Exception as e:
             raise JobError()
 
diff --git a/src/soil/variable.py b/src/soil/variable.py
index 1838d66eba612380667d84edbd2a8d27430c3358..5495dc393e47b631fbccf7145e9ccb9ff1d0e178 100644
--- a/src/soil/variable.py
+++ b/src/soil/variable.py
@@ -294,11 +294,11 @@ class Variable(Element, ABC):
             blank_node = rdflib.BNode()
             data_graph.add((blank_node, Namespaces.rdf.rest, Namespaces.rdf.nil))
             data_graph.add(
-                (blank_node, Namespaces.rdf.first, Variable.serialize_value(data_graph, value[len(value) - 1])))
+                (blank_node, Namespaces.rdf.first, self.serialize_value(data_graph, value[len(value) - 1])))
             for entry in reversed(value[:-1]):
                 new_blank_node = rdflib.BNode()
                 data_graph.add((new_blank_node, Namespaces.rdf.rest, blank_node))
-                data_graph.add((new_blank_node, Namespaces.rdf.first, Variable.serialize_value(data_graph, entry)))
+                data_graph.add((new_blank_node, Namespaces.rdf.first, self.serialize_value(data_graph, entry)))
                 blank_node = new_blank_node
             return blank_node
         else:
diff --git a/src/utils/resources.py b/src/utils/resources.py
index 62857be528559991ba0f41cbca6ac06b8063db58..cc66e31cc054b9c8cdb678964b6c58f72ed7758f 100644
--- a/src/utils/resources.py
+++ b/src/utils/resources.py
@@ -4,10 +4,12 @@ from typing import List
 
 class ResourceType(enum.Enum):
     element = 0
-    profile = 1
-    metadata = 2
-    data = 3
-    range = 4
+    profile = 1         # IRI -> <element name>Profile
+    metadata = 2        # IRI -> <element name>
+    data = 3            # IRI -> <measurement name>MeasurementResult
+    range = 4           # IRI -> <parameter/measurement/argument/return name>Range
+    observation = 5     # IRI -> <measurement name>Measurement
+    uncertainty = 6     # IRI -> <measurement name>MeasurementUncertainty
 
     def __str__(self) -> str:
         return self.name
@@ -19,7 +21,8 @@ class ResourceType(enum.Enum):
     @classmethod
     @property
     def semantic_resources(cls) -> List[str]:
-        return [str(ResourceType.profile), str(ResourceType.metadata), str(ResourceType.data), str(ResourceType.range)]
+        return [str(ResourceType.profile), str(ResourceType.metadata), str(ResourceType.data), str(ResourceType.range),
+                str(ResourceType.observation), str(ResourceType.uncertainty)]
 
     def is_semantic(self) -> bool:
-        return self.value in range(1, 5)
+        return self.value in range(1, 7)