diff --git a/src/forecaster/dataset.csv b/data/forecaster_data/dataset.csv
similarity index 100%
rename from src/forecaster/dataset.csv
rename to data/forecaster_data/dataset.csv
diff --git a/src/forecaster/dataset2.csv b/data/forecaster_data/dataset2.csv
similarity index 100%
rename from src/forecaster/dataset2.csv
rename to data/forecaster_data/dataset2.csv
diff --git a/proto/forecaster.proto b/proto/forecaster.proto
index 5a4403b01c7f85d6d5b33548d0eaf463e39558cc..dc2de75eaad355a833348687b62686ad9637c93d 100644
--- a/proto/forecaster.proto
+++ b/proto/forecaster.proto
@@ -18,28 +18,52 @@ package forecaster;
 import "context.proto";
 
 service ForecasterService {
-  rpc GetForecastOfTopology (context.TopologyId) returns (Forecast) {}
-  rpc GetForecastOfLink(context.LinkId) returns (Forecast) {}
-  rpc CheckService (context.ServiceId) returns (ForecastPrediction) {}
+  rpc ComputeTopologyForecast(ForecastTopology) returns (ListLinkCapacity) {}
+  rpc ComputeLinkForecast    (context.LinkId    ) returns (LinkCapacity    ) {}
 }
 
+message ForecastRequest {
+  oneof uuid {
+    context.TopologyId topology_id = 1;
+    context.LinkId link_id = 2;
+  }
+
+  context.TopogyId topology_id             = 1;
+  float            forecast_window_seconds = 2;
+}
+
+
 message SingleForecast {
   context.Timestamp timestamp= 1;
   double value = 2;    
 }
 
 message Forecast {
-    oneof uuid {
-      context.TopologyId topologyId= 1;
-      context.LinkId linkId = 2;
-    }
-    repeated SingleForecast forecast = 3;
+  oneof uuid {
+    context.TopologyId topologyId= 1;
+    context.LinkId linkId = 2;
+  }
+  repeated SingleForecast forecast = 3;
 }
 
 enum AvailabilityPredictionEnum {
-    FORECASTED_AVAILABILITY = 0;
-    FORECASTED_UNAVAILABILITY = 1;
+  FORECASTED_AVAILABILITY = 0;
+  FORECASTED_UNAVAILABILITY = 1;
 }
+
 message ForecastPrediction {
-    AvailabilityPredictionEnum prediction = 1;
+  AvailabilityPredictionEnum prediction = 1;
+}
+
+
+
+message LinkCapacity {
+  context.LinkId link_id                  = 1;
+  float          total_capacity_gbps      = 2;
+  float          current_capacity_gbps    = 3;
+  float          forecasted_capacity_gbps = 4;
+}
+
+message ListLinkCapacity {
+  repeated LinkCapacity forecasted_link_capacities = 1;
 }
diff --git a/src/forecaster/.gitkeep b/src/forecaster/.gitkeep
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/forecaster/.gitlab-ci.yml b/src/forecaster/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5fed57be6b7963f776425c2196e232aacddee04f
--- /dev/null
+++ b/src/forecaster/.gitlab-ci.yml
@@ -0,0 +1,106 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Build, tag, and push the Docker image to the GitLab Docker registry
+build device:
+  variables:
+    IMAGE_NAME: 'device' # name of the microservice
+    IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
+  stage: build
+  before_script:
+    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
+  script:
+    - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/Dockerfile .
+    - docker tag "$IMAGE_NAME:$IMAGE_TAG" "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
+    - docker push "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
+  after_script:
+    - docker images --filter="dangling=true" --quiet | xargs -r docker rmi
+  rules:
+    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
+    - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"'
+    - changes:
+      - src/common/**/*.py
+      - proto/*.proto
+      - src/$IMAGE_NAME/**/*.{py,in,yml}
+      - src/$IMAGE_NAME/Dockerfile
+      - src/$IMAGE_NAME/tests/*.py
+      - manifests/${IMAGE_NAME}service.yaml
+      - .gitlab-ci.yml
+
+# Apply unit test to the component
+unit_test device:
+  variables:
+    IMAGE_NAME: 'device' # name of the microservice
+    IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
+  stage: unit_test
+  needs:
+    - build device
+  before_script:
+    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
+    - if docker network list | grep teraflowbridge; then echo "teraflowbridge is already created"; else docker network create -d bridge teraflowbridge; fi
+    - if docker container ls | grep $IMAGE_NAME; then docker rm -f $IMAGE_NAME; else echo "$IMAGE_NAME image is not in the system"; fi
+  script:
+    - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
+    - docker run --name $IMAGE_NAME -d -p 2020:2020 -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --network=teraflowbridge $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
+    - sleep 5
+    - docker ps -a
+    - docker logs $IMAGE_NAME
+    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary_emulated.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing"
+  coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
+  after_script:
+    - docker rm -f $IMAGE_NAME
+    - docker network rm teraflowbridge
+  rules:
+    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
+    - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"'
+    - changes:
+      - src/common/**/*.py
+      - proto/*.proto
+      - src/$IMAGE_NAME/**/*.{py,in,yml}
+      - src/$IMAGE_NAME/Dockerfile
+      - src/$IMAGE_NAME/tests/*.py
+      - src/$IMAGE_NAME/tests/Dockerfile
+      - manifests/${IMAGE_NAME}service.yaml
+      - .gitlab-ci.yml
+  artifacts:
+      when: always
+      reports:
+        junit: src/$IMAGE_NAME/tests/${IMAGE_NAME}_report.xml
+
+## Deployment of the service in Kubernetes Cluster
+#deploy device:
+#  variables:
+#    IMAGE_NAME: 'device' # name of the microservice
+#    IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
+#  stage: deploy
+#  needs:
+#    - unit test device
+#    # - integ_test execute
+#  script:
+#    - 'sed -i "s/$IMAGE_NAME:.*/$IMAGE_NAME:$IMAGE_TAG/" manifests/${IMAGE_NAME}service.yaml'
+#    - kubectl version
+#    - kubectl get all
+#    - kubectl apply -f "manifests/${IMAGE_NAME}service.yaml"
+#    - kubectl get all
+#  # environment:
+#  #   name: test
+#  #   url: https://example.com
+#  #   kubernetes:
+#  #     namespace: test
+#  rules:
+#    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
+#      when: manual    
+#    - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"'
+#      when: manual
diff --git a/src/forecaster/Config.py b/src/forecaster/Config.py
new file mode 100644
index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612
--- /dev/null
+++ b/src/forecaster/Config.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/forecaster/Dockerfile b/src/forecaster/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..6566625527f8ceaa8de4639d558c92572c4835cb
--- /dev/null
+++ b/src/forecaster/Dockerfile
@@ -0,0 +1,70 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+FROM python:3.9-slim
+
+# Install dependencies
+RUN apt-get --yes --quiet --quiet update && \
+    apt-get --yes --quiet --quiet install wget g++ git && \
+    rm -rf /var/lib/apt/lists/*
+
+# Set Python to show logs as they occur
+ENV PYTHONUNBUFFERED=0
+
+# Download the gRPC health probe
+RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \
+    wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
+    chmod +x /bin/grpc_health_probe
+
+# Get generic Python packages
+RUN python3 -m pip install --upgrade pip
+RUN python3 -m pip install --upgrade setuptools wheel
+RUN python3 -m pip install --upgrade pip-tools
+
+# Get common Python packages
+# Note: this step enables sharing the previous Docker build steps among all the Python components
+WORKDIR /var/teraflow
+COPY common_requirements.in common_requirements.in
+RUN pip-compile --quiet --output-file=common_requirements.txt common_requirements.in
+RUN python3 -m pip install -r common_requirements.txt
+
+# Add common files into working directory
+WORKDIR /var/teraflow/common
+COPY src/common/. ./
+RUN rm -rf proto
+
+# Create proto sub-folder, copy .proto files, and generate Python code
+RUN mkdir -p /var/teraflow/common/proto
+WORKDIR /var/teraflow/common/proto
+RUN touch __init__.py
+COPY proto/*.proto ./
+RUN python3 -m grpc_tools.protoc -I=. --python_out=. --grpc_python_out=. *.proto
+RUN rm *.proto
+RUN find . -type f -exec sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' {} \;
+
+# Create component sub-folders, get specific Python packages
+RUN mkdir -p /var/teraflow/device
+WORKDIR /var/teraflow/device
+COPY src/device/requirements.in requirements.in
+RUN pip-compile --quiet --output-file=requirements.txt requirements.in
+RUN python3 -m pip install -r requirements.txt
+
+# Add component files into working directory
+WORKDIR /var/teraflow
+COPY src/context/. context/
+COPY src/device/. device/
+COPY src/monitoring/. monitoring/
+
+# Start the service
+ENTRYPOINT ["python", "-m", "device.service"]
diff --git a/src/forecaster/__init__.py b/src/forecaster/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..38d04994fb0fa1951fb465bc127eb72659dc2eaf 100644
--- a/src/forecaster/__init__.py
+++ b/src/forecaster/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/src/forecaster/client/ForecasterClient.py b/src/forecaster/client/ForecasterClient.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed33650689547c2ded4e48ebb442215d2887f542
--- /dev/null
+++ b/src/forecaster/client/ForecasterClient.py
@@ -0,0 +1,82 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import grpc, logging
+from common.Constants import ServiceNameEnum
+from common.Settings import get_service_host, get_service_port_grpc
+from common.proto.context_pb2 import Device, DeviceConfig, DeviceId, Empty
+from common.proto.device_pb2 import MonitoringSettings
+from common.proto.device_pb2_grpc import DeviceServiceStub
+from common.tools.client.RetryDecorator import retry, delay_exponential
+from common.tools.grpc.Tools import grpc_message_to_json_string
+
+LOGGER = logging.getLogger(__name__)
+MAX_RETRIES = 15
+DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
+RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+
+class ForecasterClient:
+    def __init__(self, host=None, port=None):
+        if not host: host = get_service_host(ServiceNameEnum.DEVICE)
+        if not port: port = get_service_port_grpc(ServiceNameEnum.DEVICE)
+        self.endpoint = '{:s}:{:s}'.format(str(host), str(port))
+        LOGGER.debug('Creating channel to {:s}...'.format(str(self.endpoint)))
+        self.channel = None
+        self.stub = None
+        self.connect()
+        LOGGER.debug('Channel created')
+
+    def connect(self):
+        self.channel = grpc.insecure_channel(self.endpoint)
+        self.stub = DeviceServiceStub(self.channel)
+
+    def close(self):
+        if self.channel is not None: self.channel.close()
+        self.channel = None
+        self.stub = None
+
+    @RETRY_DECORATOR
+    def AddDevice(self, request : Device) -> DeviceId:
+        LOGGER.debug('AddDevice request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.stub.AddDevice(request)
+        LOGGER.debug('AddDevice result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+
+    @RETRY_DECORATOR
+    def ConfigureDevice(self, request : Device) -> DeviceId:
+        LOGGER.debug('ConfigureDevice request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.stub.ConfigureDevice(request)
+        LOGGER.debug('ConfigureDevice result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+
+    @RETRY_DECORATOR
+    def DeleteDevice(self, request : DeviceId) -> Empty:
+        LOGGER.debug('DeleteDevice request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.stub.DeleteDevice(request)
+        LOGGER.debug('DeleteDevice result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+
+    @RETRY_DECORATOR
+    def GetInitialConfig(self, request : DeviceId) -> DeviceConfig:
+        LOGGER.debug('GetInitialConfig request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.stub.GetInitialConfig(request)
+        LOGGER.debug('GetInitialConfig result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+
+    @RETRY_DECORATOR
+    def MonitorDeviceKpi(self, request : MonitoringSettings) -> Empty:
+        LOGGER.debug('MonitorDeviceKpi request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.stub.MonitorDeviceKpi(request)
+        LOGGER.debug('MonitorDeviceKpi result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
diff --git a/src/forecaster/client/__init__.py b/src/forecaster/client/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612
--- /dev/null
+++ b/src/forecaster/client/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/forecaster/requirements.in b/src/forecaster/requirements.in
new file mode 100644
index 0000000000000000000000000000000000000000..c81e814603d4c84e0211e3b433fc916b616ecd04
--- /dev/null
+++ b/src/forecaster/requirements.in
@@ -0,0 +1,42 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+anytree==2.8.0
+APScheduler==3.10.1
+cryptography==36.0.2
+#fastcache==1.1.0
+Jinja2==3.0.3
+ncclient==0.6.13
+p4runtime==1.3.0
+paramiko==2.9.2
+python-json-logger==2.0.2
+#pytz==2021.3
+#redis==4.1.2
+requests==2.27.1
+requests-mock==1.9.3
+xmltodict==0.12.0
+tabulate
+ipaddress
+macaddress
+yattag
+pyang
+git+https://github.com/robshakir/pyangbind.git
+websockets==10.4
+
+# pip's dependency resolver does not take into account installed packages.
+# p4runtime does not specify the version of grpcio/protobuf it needs, so it tries to install latest one
+# adding here again grpcio==1.47.* and protobuf==3.20.* with explicit versions to prevent collisions
+grpcio==1.47.*
+protobuf==3.20.*
diff --git a/src/forecaster/service/DeviceService.py b/src/forecaster/service/DeviceService.py
new file mode 100644
index 0000000000000000000000000000000000000000..6d27ef96eef4b93fa7d6ca294d1fd645e815af03
--- /dev/null
+++ b/src/forecaster/service/DeviceService.py
@@ -0,0 +1,41 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from common.Constants import ServiceNameEnum
+from common.Settings import get_service_port_grpc
+from common.proto.device_pb2_grpc import add_DeviceServiceServicer_to_server
+from common.tools.service.GenericGrpcService import GenericGrpcService
+from .driver_api.DriverInstanceCache import DriverInstanceCache
+from .DeviceServiceServicerImpl import DeviceServiceServicerImpl
+from .monitoring.MonitoringLoops import MonitoringLoops
+
+# Custom gRPC settings
+# Multiple clients might keep connections alive waiting for RPC methods to be executed.
+# Requests needs to be serialized to ensure correct device configurations
+GRPC_MAX_WORKERS = 200
+
+class DeviceService(GenericGrpcService):
+    def __init__(self, driver_instance_cache : DriverInstanceCache, cls_name: str = __name__) -> None:
+        port = get_service_port_grpc(ServiceNameEnum.DEVICE)
+        super().__init__(port, max_workers=GRPC_MAX_WORKERS, cls_name=cls_name)
+        self.monitoring_loops = MonitoringLoops()
+        self.device_servicer = DeviceServiceServicerImpl(driver_instance_cache, self.monitoring_loops)
+
+    def install_servicers(self):
+        self.monitoring_loops.start()
+        add_DeviceServiceServicer_to_server(self.device_servicer, self.server)
+
+    def stop(self):
+        super().stop()
+        self.monitoring_loops.stop()
diff --git a/src/forecaster/service/DeviceServiceServicerImpl.py b/src/forecaster/service/DeviceServiceServicerImpl.py
new file mode 100644
index 0000000000000000000000000000000000000000..8b7855be177bbb11b46e8d21809b7f83a0a64c86
--- /dev/null
+++ b/src/forecaster/service/DeviceServiceServicerImpl.py
@@ -0,0 +1,56 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import grpc, logging, os, time
+from typing import Dict
+from prometheus_client import Histogram
+from common.Constants import ServiceNameEnum
+from common.Settings import ENVVAR_SUFIX_SERVICE_HOST, get_env_var_name
+from common.method_wrappers.Decorator import MetricTypeEnum, MetricsPool, safe_and_metered_rpc_method
+from common.method_wrappers.ServiceExceptions import NotFoundException, OperationFailedException
+from common.proto.context_pb2 import (
+    Device, DeviceConfig, DeviceDriverEnum, DeviceId, DeviceOperationalStatusEnum, Empty, Link)
+from common.proto.device_pb2 import MonitoringSettings
+from common.proto.device_pb2_grpc import DeviceServiceServicer
+from common.tools.context_queries.Device import get_device
+from common.tools.mutex_queues.MutexQueues import MutexQueues
+from context.client.ContextClient import ContextClient
+
+LOGGER = logging.getLogger(__name__)
+
+METRICS_POOL = MetricsPool('Device', 'RPC')
+
+METRICS_POOL_DETAILS = MetricsPool('Device', 'execution', labels={
+    'driver': '', 'operation': '', 'step': '',
+})
+
+class DeviceServiceServicerImpl(DeviceServiceServicer):
+    def __init__(self, driver_instance_cache : DriverInstanceCache, monitoring_loops : MonitoringLoops) -> None:
+        LOGGER.debug('Creating Servicer...')
+        self.driver_instance_cache = driver_instance_cache
+        self.monitoring_loops = monitoring_loops
+        self.mutex_queues = MutexQueues()
+        LOGGER.debug('Servicer Created')
+
+    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
+    def AddDevice(self, request : Device, context : grpc.ServicerContext) -> DeviceId:
+        return DeviceId()
+
+    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
+    def ConfigureDevice(self, request : Device, context : grpc.ServicerContext) -> DeviceId:
+        return DeviceId()
+
+    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
+    def DeleteDevice(self, request : DeviceId, context : grpc.ServicerContext) -> Empty:
+        return Empty()
diff --git a/src/forecaster/service/__init__.py b/src/forecaster/service/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612
--- /dev/null
+++ b/src/forecaster/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/forecaster/service/__main__.py b/src/forecaster/service/__main__.py
new file mode 100644
index 0000000000000000000000000000000000000000..84dee26905fc404cf1de96f7a1e11e4a59a034b6
--- /dev/null
+++ b/src/forecaster/service/__main__.py
@@ -0,0 +1,80 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging, signal, sys, threading
+from prometheus_client import start_http_server
+from common.Constants import ServiceNameEnum
+from common.Settings import (
+    ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name, get_log_level, get_metrics_port,
+    wait_for_environment_variables)
+from .DeviceService import DeviceService
+from .driver_api.DriverFactory import DriverFactory
+from .driver_api.DriverInstanceCache import DriverInstanceCache, preload_drivers
+from .drivers import DRIVERSfff
+
+terminate = threading.Event()
+LOGGER : logging.Logger = None
+
+def signal_handler(signal, frame): # pylint: disable=redefined-outer-name
+    LOGGER.warning('Terminate signal received')
+    terminate.set()
+
+def main():
+    global LOGGER # pylint: disable=global-statement
+
+    log_level = get_log_level()
+    logging.basicConfig(level=log_level, format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s")
+    logging.getLogger('apscheduler.executors.default').setLevel(logging.WARNING)
+    logging.getLogger('apscheduler.scheduler').setLevel(logging.WARNING)
+    logging.getLogger('monitoring-client').setLevel(logging.WARNING)
+    LOGGER = logging.getLogger(__name__)
+
+    wait_for_environment_variables([
+        get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_HOST     ),
+        get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_GRPC),
+    ])
+
+    signal.signal(signal.SIGINT,  signal_handler)
+    signal.signal(signal.SIGTERM, signal_handler)
+
+    LOGGER.info('Starting...')
+
+    # Start metrics server
+    metrics_port = get_metrics_port()
+    start_http_server(metrics_port)
+
+    # Initialize Driver framework
+    driver_factory = DriverFactory(DRIVERS)
+    driver_instance_cache = DriverInstanceCache(driver_factory)
+
+    # Starting device service
+    grpc_service = DeviceService(driver_instance_cache)
+    grpc_service.start()
+
+    # Initialize drivers with existing devices in context
+    LOGGER.info('Pre-loading drivers...')
+    preload_drivers(driver_instance_cache)
+
+    # Wait for Ctrl+C or termination signal
+    while not terminate.wait(timeout=1.0): pass
+
+    LOGGER.info('Terminating...')
+    grpc_service.stop()
+    driver_instance_cache.terminate()
+
+    LOGGER.info('Bye')
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/src/forecaster/tests/__init__.py b/src/forecaster/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612
--- /dev/null
+++ b/src/forecaster/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/forecaster/tests/test_unitary.py b/src/forecaster/tests/test_unitary.py
new file mode 100644
index 0000000000000000000000000000000000000000..38d04994fb0fa1951fb465bc127eb72659dc2eaf
--- /dev/null
+++ b/src/forecaster/tests/test_unitary.py
@@ -0,0 +1,13 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.