diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 6231e02ee4f971f8a34e728d797694e628f1208c..269f7a687c9d5bac7075cc1fcc9bb37fbee23650 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -321,7 +321,7 @@ service_test_docker:
   extends: .test_docker
   script:
     # Do not remove 'bash' or statement will be ignored by primitive docker shell
-    - bash -e $CI_PROJECT_DIR/sbin/run_service_test.sh
+    - bash -e $CI_PROJECT_DIR/sbin/run_integration_test.sh --no-build --skip-tests --module="services" --station=cs
 
 multi_project_integration_test:
   stage: integration-tests
diff --git a/README.md b/README.md
index 9fe839d03d82e24424fd0d64f27da5335a51b39d..d6189af1a9b672d9beb366d275d224d0ea8b9b43 100644
--- a/README.md
+++ b/README.md
@@ -150,6 +150,7 @@ Next change the version in the following places:
    through [https://git.astron.nl/lofar2.0/tango/-/tags](Deploy Tags)
 
 # Release Notes
+* 0.47.0 Migrate from lofar-station-client to lofar-lotus package. Update various package dependencies.
 * 0.46.1 Include clock_RW in metadata JSON
 * 0.46.0 Expose latest BST/SST/XST from ZMQ over gRPC
 * 0.45.12 Improve syslog parsing for PyPCC logs
diff --git a/docker/jupyter-lab/Dockerfile b/docker/jupyter-lab/Dockerfile
index af215b0d201a31463b08b07edf961d22c9cd8e7f..6f8926c2f5746ec6e653db3d6edb3188eaaf98a0 100644
--- a/docker/jupyter-lab/Dockerfile
+++ b/docker/jupyter-lab/Dockerfile
@@ -17,7 +17,7 @@ RUN mamba install --yes 'jupyterlab-git>=0.50.0' && \
 RUN jupyter server extension enable --py jupyterlab_git
 
 COPY requirements.txt ./
-RUN pip install --break-system-packages -r requirements.txt --extra-index-url=https://git.astron.nl/api/v4/projects/395/packages/pypi/simple
+RUN pip install --break-system-packages -r requirements.txt --extra-index-url=https://git.astron.nl/api/v4/groups/36/-/packages/pypi/simple
 RUN rm requirements.txt
 
 USER root
diff --git a/docker/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/01-stationcontrol.py b/docker/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/01-stationcontrol.py
index a9c046560add3bae6d2223810b72a6e7ecf97155..3d143d2ac03c1552f3a2230d6d5ee86d71ed0693 100644
--- a/docker/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/01-stationcontrol.py
+++ b/docker/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/01-stationcontrol.py
@@ -2,7 +2,8 @@
 #  SPDX-License-Identifier: Apache-2.0
 
 # noinspection PyUnresolvedReferences
-import lofar_station_client
+import lofar_lotus as lofar_station_client
+import lofar_lotus
 
 # noinspection PyUnresolvedReferences
 import tangostationcontrol
diff --git a/docker/jupyter-lab/requirements.txt b/docker/jupyter-lab/requirements.txt
index 222dc787a24e754285580a29ac469fb27c758f3c..e052016d4347c1e7115d61121a461919da6a6bd3 100644
--- a/docker/jupyter-lab/requirements.txt
+++ b/docker/jupyter-lab/requirements.txt
@@ -11,7 +11,7 @@ jupyter_server_nbmodel[lab,rtc] # BSD-3
 # NB: tangostationcontrol will also install lofar-station-client. The latter
 #     is omitted here to avoid pip getting confused.
 tangostationcontrol >= 0.39
---extra-index-url https://git.astron.nl/api/v4/projects/71/packages/pypi/simple
+--extra-index-url https://git.astron.nl/api/v4/groups/36/-/packages/pypi/simple
 PyTango
 
 # low-level access to station components
diff --git a/docker/lofar-device-base/Dockerfile b/docker/lofar-device-base/Dockerfile
index 3abd30ee66df2ff501ded9cffc89519d5ac4dea2..21e50e6695ea4e60b4c8e53d9630283d98a2002f 100644
--- a/docker/lofar-device-base/Dockerfile
+++ b/docker/lofar-device-base/Dockerfile
@@ -65,12 +65,12 @@ COPY tmp/debug-requirements.txt /tangostationcontrol-debug-requirements.txt
 RUN echo "DEBUG_BUILD: ${DEBUG_BUILD}"
 RUN if [ $DEBUG_BUILD ]; then \
     echo "Installing debug requirements"; \
-    sudo pip3 install -r /tangostationcontrol-debug-requirements.txt --extra-index-url=https://git.astron.nl/api/v4/projects/395/packages/pypi/simple; \
+    sudo pip3 install -r /tangostationcontrol-debug-requirements.txt --extra-index-url=https://git.astron.nl/api/v4/groups/36/-/packages/pypi/simple; \
   fi
 
 COPY tmp/*.whl /
 RUN echo "Installing prebuild Station Control wheel"; \
-    sudo pip3 install /*.whl --extra-index-url=https://git.astron.nl/api/v4/projects/395/packages/pypi/simple;
+    sudo pip3 install /*.whl --extra-index-url=https://git.astron.nl/api/v4/groups/36/-/packages/pypi/simple;
 
 # install and use ephimerides and geodetic ("measures") tables for casacore.
 # we install a _stub_ since the tables need to be deployed explicitly from within the software.
diff --git a/infra/dev/services/services.hcl b/infra/dev/services/services.hcl
index ce72e23e5dd9ce345cda1d638dac34108f7dccf2..77bb8a1e826a374d4f20e687691963ad4a095c6a 100644
--- a/infra/dev/services/services.hcl
+++ b/infra/dev/services/services.hcl
@@ -9,17 +9,17 @@ resource "nomad_job" "monitoring" {
   }
 }
 
-resource "nomad_job" "logging" {
-  cluster = variable.nomad_cluster
-
-  paths = ["../jobs/station/logging.nomad"]
-  depends_on = ["resource.nomad_job.monitoring"]
-
-  health_check {
-    timeout = "300s"
-    jobs    = ["log-scraping"]
-  }
-}
+# resource "nomad_job" "logging" {
+#   cluster = variable.nomad_cluster
+#
+#   paths = ["../jobs/station/logging.nomad"]
+#   depends_on = ["resource.nomad_job.monitoring"]
+#
+#   health_check {
+#     timeout = "300s"
+#     jobs    = ["log-scraping"]
+#   }
+# }
 
 resource "ingress" "grafana" {
   port = 3000
diff --git a/infra/dev/tango/tango.hcl b/infra/dev/tango/tango.hcl
index 976ee8090325aaa42fb6ad94ba70d32d3a0f7369..f89b1b5871375d9d7ad9a1fbc96b1226871e2ce2 100644
--- a/infra/dev/tango/tango.hcl
+++ b/infra/dev/tango/tango.hcl
@@ -140,6 +140,19 @@ resource "nomad_job" "device-servers" {
     jobs    = ["device-servers"]
   }
 }
+resource "nomad_job" "rpc-server" {
+  depends_on = [
+    "resource.nomad_job.device-servers"
+  ]
+  cluster = variable.nomad_cluster
+
+  paths = ["../jobs/station/rpc-server.nomad"]
+
+  health_check {
+    timeout = "3000s"
+    jobs    = ["rpc-server"]
+  }
+}
 
 resource "ingress" "minio_s3" {
   port = 9000
diff --git a/infra/jobs/station/rpc-server.levant.nomad b/infra/jobs/station/rpc-server.levant.nomad
index 9c8ac0d01cecb8994895a96d7d8c7002cb19f528..4fd85a1968ba83de42d52f68f9cccc5f6933e752 100644
--- a/infra/jobs/station/rpc-server.levant.nomad
+++ b/infra/jobs/station/rpc-server.levant.nomad
@@ -45,8 +45,9 @@ job "rpc-server" {
         entrypoint = [""]
         command    = "l2ss-rpc-server"
         args       = [
-          "--port", "${NOMAD_PORT_rpc}",
-          "--metrics-port", "${NOMAD_PORT_metrics}"
+          "--port", "${NOMAD_PORT_grpc}",
+          "--metrics-port", "${NOMAD_PORT_metrics}",
+          "--station", "[[.station]]"
     [[ range $name, $tr := $.sdptr.instances ]]
           , "--antenna-field", "[[ $name ]]"
           , "--statistics-zmq", "tcp://stingray-[[ $name ]]-bst-zmq.service.consul:6001"
diff --git a/sbin/run_integration_test.sh b/sbin/run_integration_test.sh
index 7e0f526e42605fd4b57ed978265d3f1becfad184..72077cfb6aac66c661c4148090a470e0fc2f9eda 100755
--- a/sbin/run_integration_test.sh
+++ b/sbin/run_integration_test.sh
@@ -23,7 +23,7 @@ function usage {
       disables building of docker images"
     echo ""
     echo "./$(basename "$0") --skip-tests
-      Only setup environment, implies --preserve"
+      Only setup environment"
     echo ""
     echo "./$(basename "$0") --preserve
       Prevents tearing down the dev environment afterwards"
@@ -67,9 +67,7 @@ while true; do
       ;;
     --skip-tests)
       echo "Only setup and configure environment don't run any tests"
-      echo "Implies --preserve"
       export no_tests=1
-      export preserve=1
       ;;
     --preserve)
       echo "Preserve test environment"
diff --git a/sbin/run_service_test.sh b/sbin/run_service_test.sh
deleted file mode 100755
index e2060eef654ea1e44004e3f35664edaa291038b4..0000000000000000000000000000000000000000
--- a/sbin/run_service_test.sh
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/bin/bash -e
-#
-# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
-#
-
-if [ -z "$LOFAR20_DIR" ]; then
-    # We assume we aren't in the PATH, so we can derive our path.
-    # We need our parent directory.
-    LOFAR20_DIR_RELATIVE=$(dirname "$0")/..
-
-    # As an absolute path
-    LOFAR20_DIR=$(readlink -f "${LOFAR20_DIR_RELATIVE}")
-fi
-
-if [ -z "$TAG" ]; then
-  export TAG="latest"
-fi
-
-docker network rm station || true
-
-# prepare a docker volume for nomad
-tmp_volume="test_$(hexdump -n 16 -v -e '/1 "%02X"' /dev/urandom)"
-
-function cleanup {
-  cd "$LOFAR20_DIR"
-  if [ -n "${save_logs}" ]; then
-    mkdir -p log
-    for container in $(docker ps -a --format "{{.Names}}")
-    do
-      echo "Saving log for container $container"
-      docker logs "${container}" >& "log/${container}.log"
-    done
-    bash "${LOFAR20_DIR}"/sbin/dsconfig.sh --dump >& log/dump_ConfigDb.log
-  fi
-
-  if [ -z "${preserve}" ]; then
-    HOME="$JUMPPAD_HOME" jumppad down
-    docker volume rm "$tmp_volume" || true
-  fi
-}
-
-trap cleanup INT EXIT TERM ERR SIGTERM SIGINT
-
-cd "$LOFAR20_DIR" || exit 1
-
-source "${LOFAR20_DIR}"/sbin/prepare_dev_env.sh --volume="$tmp_volume"
-
-if [ -z "$JUMPPAD_HOME" ]; then
-  JUMPPAD_HOME="$HOME"
-fi
-
-rm -rf "$JUMPPAD_HOME/.jumppad/"
-
-jumppad_options=(
-  --var="host_volume=$tmp_volume"
-  --var="image_tag=$TAG"
-)
-
-HOME="$JUMPPAD_HOME" jumppad up "${jumppad_options[@]}" infra/dev/services.hcl --no-browser
-
-echo "success"
diff --git a/tangostationcontrol/VERSION b/tangostationcontrol/VERSION
index 620104d8208446199c33c5cdafb5ae4be455ce81..421ab545d9adc503fddf2ea2ce447a9a8c5323af 100644
--- a/tangostationcontrol/VERSION
+++ b/tangostationcontrol/VERSION
@@ -1 +1 @@
-0.46.1
+0.47.0
diff --git a/tangostationcontrol/integration_test/common/base_device_classes/power_hierarchy_tests.py b/tangostationcontrol/integration_test/common/base_device_classes/power_hierarchy_tests.py
index aded09f1fbb1e317f9e27c9edd9935437760d7dd..d10330e6b169807202d337ada85a607e8934bff0 100644
--- a/tangostationcontrol/integration_test/common/base_device_classes/power_hierarchy_tests.py
+++ b/tangostationcontrol/integration_test/common/base_device_classes/power_hierarchy_tests.py
@@ -10,7 +10,7 @@ from tango import DevState, DeviceProxy
 
 from integration_test import base
 from integration_test.device_proxy import TestDeviceProxy
-from lofar_station_client.common import CaseInsensitiveString
+from lofar_lotus.dict import CaseInsensitiveString
 from tangostationcontrol.common.constants import N_rcu, N_rcu_inp
 from tangostationcontrol.devices.base_device_classes.hierarchy_device import (
     NotFoundException,
diff --git a/tangostationcontrol/integration_test/common/device_digitalbeam_tests.py b/tangostationcontrol/integration_test/common/device_digitalbeam_tests.py
index 4ec817c05803c7a090ae0d434f3358efb405e123..1df9562ae52f7d88d0d7f3670ab0424cc9559cf1 100644
--- a/tangostationcontrol/integration_test/common/device_digitalbeam_tests.py
+++ b/tangostationcontrol/integration_test/common/device_digitalbeam_tests.py
@@ -5,7 +5,7 @@ import logging
 import time
 
 import numpy
-import timeout_decorator
+import pytest
 
 from integration_test.device_proxy import TestDeviceProxy
 from integration_test.default.devices.base import TestDeviceBase
@@ -223,7 +223,7 @@ class DigitalBeamDeviceTests(TestDeviceBase):
             f"{FPGA_bf_weights_pp_RW}",
         )
 
-    @timeout_decorator.timeout(15)
+    @pytest.mark.timeout(15)
     def test_beam_tracking_90_percent_interval(self):
         """Verify that the beam tracking operates within 95% of interval"""
 
diff --git a/tangostationcontrol/integration_test/common/device_observation_control_tests.py b/tangostationcontrol/integration_test/common/device_observation_control_tests.py
index 1e7caa9dc18b5426928947fadcfdfa8517fbe75d..30ff14af6aa9a9c58aece120b92bf40b2989174d 100644
--- a/tangostationcontrol/integration_test/common/device_observation_control_tests.py
+++ b/tangostationcontrol/integration_test/common/device_observation_control_tests.py
@@ -7,12 +7,14 @@ import time
 from datetime import datetime
 from datetime import timedelta
 
+
 import numpy
+import pytest
+
 from integration_test.default.devices.base import TestDeviceBase
 from tango import DevFailed, DevState
 
 from tangostationcontrol.common.constants import CS001_TILES
-from timeout_decorator import timeout_decorator
 
 logger = logging.getLogger()
 
@@ -271,7 +273,7 @@ class DeviceObservationControlTests(TestDeviceBase):
         self.on_device_assert(self.proxy)
         self.assertRaises(DevFailed, self.proxy.add_observation, json.dumps(parameters))
 
-    @timeout_decorator.timeout(60)
+    @pytest.mark.timeout(60)
     def test_add_observation_callbacks(self):
         """Test adding an observation and checking start / stop callbacks work"""
 
diff --git a/tangostationcontrol/integration_test/common/device_tilebeam_tests.py b/tangostationcontrol/integration_test/common/device_tilebeam_tests.py
index bc71c941ab4d792d174f2f1556e82026a2619f55..2c3a23dd29156bce31b2e2cfbd81366ffbd9ff37 100644
--- a/tangostationcontrol/integration_test/common/device_tilebeam_tests.py
+++ b/tangostationcontrol/integration_test/common/device_tilebeam_tests.py
@@ -7,7 +7,7 @@ import json
 import time
 
 import numpy
-import timeout_decorator
+import pytest
 
 from integration_test.device_proxy import TestDeviceProxy
 from integration_test.default.devices.base import TestDeviceBase
@@ -251,7 +251,7 @@ class TileBeamDeviceTests(TestDeviceBase):
             new_pointings[0:2], list(self.proxy.Pointing_direction_R[0:2])
         )
 
-    @timeout_decorator.timeout(120)
+    @pytest.mark.timeout(120)
     def test_beam_tracking_95_percent_interval(self):
         """Verify that the beam tracking operates within 95% of interval"""
 
diff --git a/tangostationcontrol/integration_test/configuration/test_device_configuration.py b/tangostationcontrol/integration_test/configuration/test_device_configuration.py
index 4ffa79404db469fff84be55870a115bd9d60fdb9..ede698fcd87e3a7897c501bf38246135d6f5114e 100644
--- a/tangostationcontrol/integration_test/configuration/test_device_configuration.py
+++ b/tangostationcontrol/integration_test/configuration/test_device_configuration.py
@@ -11,8 +11,8 @@ except ImportError:
     from importlib_resources import files  # type: ignore
 
 from integration_test.default.devices.base import TestDeviceBase
-from lofar_station_client.common import CaseInsensitiveDict
-from lofar_station_client.common import CaseInsensitiveString
+from lofar_lotus.dict import CaseInsensitiveDict
+from lofar_lotus.dict import CaseInsensitiveString
 
 
 from tango import DevState
diff --git a/tangostationcontrol/integration_test/default/devices/test_device_protection_control.py b/tangostationcontrol/integration_test/default/devices/test_device_protection_control.py
index e539aeadc1ed5ff68625420e1d0ea1968f344121..599d21786bddfac110a5d7221031d581bbaef231 100644
--- a/tangostationcontrol/integration_test/default/devices/test_device_protection_control.py
+++ b/tangostationcontrol/integration_test/default/devices/test_device_protection_control.py
@@ -5,7 +5,7 @@ import logging
 import time
 from typing import List
 
-import timeout_decorator
+import pytest
 from tango import Database, DevState
 
 from tangostationcontrol.devices import UNB2, SDPFirmware, RECVL, RECVH, APSCT, APSPU
@@ -84,7 +84,7 @@ class TestDeviceProtectionControl(TestDeviceBase):
             self.proxy.number_of_connected_devices_R,
         )
 
-    @timeout_decorator.timeout(10)
+    @pytest.mark.timeout(10)
     def test_change_events_received(self):
 
         self.proxy.boot()
diff --git a/tangostationcontrol/integration_test/default/devices/test_observation_client.py b/tangostationcontrol/integration_test/default/devices/test_observation_client.py
index fb1290d53f757e3db90ed4feccaf182ceed663ca..e21c38cc21e9b55b8853bcfdc066c1807325b261 100644
--- a/tangostationcontrol/integration_test/default/devices/test_observation_client.py
+++ b/tangostationcontrol/integration_test/default/devices/test_observation_client.py
@@ -4,15 +4,17 @@
 from json import loads
 from os import environ
 
-from lofar_station_client.observation.station_observation import (
-    StationObservation,
-)
 from integration_test import base
 from integration_test.device_proxy import TestDeviceProxy
 from tangostationcontrol.test.dummy_observation_settings import (
     get_observation_settings_hba_core_immediate,
 )
-
+import grpc
+from lofar_sid.interface.stationcontrol.observation_pb2 import (
+    StartObservationRequest,
+    StopObservationRequest,
+)
+from lofar_sid.interface.stationcontrol.observation_pb2_grpc import ObservationStub
 from tango import DevState
 
 
@@ -57,25 +59,27 @@ class TestObservation(base.IntegrationTestCase):
         """Test of the observation_wrapper class basic functionality"""
 
         # convert the JSON specification to a dict for this class
-        specification_dict = loads(
-            get_observation_settings_hba_core_immediate().to_json()
-        )
+        specification = get_observation_settings_hba_core_immediate().to_json()
+        specification_dict = loads(specification)
 
         # create an observation class using the dict and as host just get it using a
         # util function
-        observation = StationObservation(
-            specification=specification_dict, host=environ["TANGO_HOST"]
-        )
 
-        # Assert the observation is running after starting it
-        observation.start()
-        self.assertTrue(observation.is_running)
+        tango_host = environ["TANGO_HOST"]
+        obs = TestDeviceProxy(f"tango://{tango_host}/STAT/ObservationControl/1")
+
+        obs_id = specification_dict["antenna_fields"][0]["observation_id"]
 
-        # Assert the proxy is on
-        station = observation.observation
-        proxy = station.observation_field_proxies[0]
-        self.assertTrue(proxy.state() == DevState.ON)
+        with grpc.insecure_channel("rpc.service.consul:50051") as channel:
+            stub = ObservationStub(channel)
+            stub.StartObservation(StartObservationRequest(configuration=specification))
+
+        # Assert the observation is running after starting it
+        self.assertTrue(obs.is_observation_running(obs_id))
 
         # Assert the observation has stopped after aborting
-        observation.stop()
-        self.assertFalse(observation.is_running)
+
+        with grpc.insecure_channel("rpc.service.consul:50051") as channel:
+            stub = ObservationStub(channel)
+            stub.StopObservation(StopObservationRequest(observation_id=obs_id))
+        self.assertFalse(obs.is_observation_running(obs_id))
diff --git a/tangostationcontrol/pyproject.toml b/tangostationcontrol/pyproject.toml
index 63f09a96268a6e5f9e8829c758d9e208a1b8bb97..a4cd4e9bce1b099e4ea70708d4c16f11bf141c56 100644
--- a/tangostationcontrol/pyproject.toml
+++ b/tangostationcontrol/pyproject.toml
@@ -1,3 +1,7 @@
 [build-system]
 requires = ['setuptools>=62.6', 'wheel']
 build-backend = 'setuptools.build_meta'
+[tool.pytest.ini_options]
+markers = [
+    "timeout",
+]
diff --git a/tangostationcontrol/requirements.txt b/tangostationcontrol/requirements.txt
index 9a398792a2ee6fd267337fe315c92e358d25a727..7b66147ed058c2a42f68c6daa4803d843b3d9ff7 100644
--- a/tangostationcontrol/requirements.txt
+++ b/tangostationcontrol/requirements.txt
@@ -2,17 +2,18 @@
 # order of appearance. Changing the order has an impact on the overall
 # integration process, which may cause wedges in the gate later.
 
-lofar-station-client[tango] >= 0.22.3 # Apache 2
+lofar-sid >= 1.0.11 # Apache 2
+lofar-lotus>=0.0.4 # Apache 2
 PyTango>=10.0.0 # LGPL v3
 numpy>=1.21.6 # BSD3
 asyncua >= 0.9.90 # LGPLv3
 psycopg2-binary >= 2.9.2 # LGPL
-pyasn1 == 0.4.8 # BSD, pinned because https://github.com/pyasn1/pyasn1/issues/28
-pysnmplib >= 5.0.23 # BSD2
+pyasn1 >= 0.6.1 # BSD
+pysnmp >= 7 # BSD2
+pysmi >= 1.5.11 # BSD2
 h5py >= 3.1.0 # BSD
 jsonschema > 4.18 # MIT
 referencing > 0.30 # MIT
-docker >= 5.0.3 # Apache 2
 python-casacore >= 3.3.1 # LGPLv3
 etrs-itrs@git+https://github.com/brentjens/etrs-itrs # Apache 2
 lofarantpos >= 0.5.0 # Apache 2
diff --git a/tangostationcontrol/tangostationcontrol/clients/snmp/attribute_classes.py b/tangostationcontrol/tangostationcontrol/clients/snmp/attribute_classes.py
index 7d2e60aded9aa7d26ae611e1f4d4df13af4b2524..ee73ed6c318d9788d4260a7c884086146141226d 100644
--- a/tangostationcontrol/tangostationcontrol/clients/snmp/attribute_classes.py
+++ b/tangostationcontrol/tangostationcontrol/clients/snmp/attribute_classes.py
@@ -1,6 +1,6 @@
 import logging
 import numpy
-from pysnmp import hlapi
+import pysnmp.hlapi.v3arch.asyncio as hlapi
 
 logger = logging.getLogger()
 
diff --git a/tangostationcontrol/tangostationcontrol/clients/snmp/snmp_client.py b/tangostationcontrol/tangostationcontrol/clients/snmp/snmp_client.py
index da1805b113a05e5d8c59d480654307a86ce38d65..f779c3f1243f93c4574eb51e92e722e8db3f5879 100644
--- a/tangostationcontrol/tangostationcontrol/clients/snmp/snmp_client.py
+++ b/tangostationcontrol/tangostationcontrol/clients/snmp/snmp_client.py
@@ -1,10 +1,10 @@
 # Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
 # SPDX-License-Identifier: Apache-2.0
-
+import asyncio
 import logging
 from os import path
 
-from pysnmp import hlapi
+import pysnmp.hlapi.v3arch.asyncio as hlapi
 from pysnmp.smi import builder, compiler
 from tangostationcontrol.clients.comms_client import CommClient
 
@@ -28,7 +28,7 @@ class SNMPComm:
         self.port = port
         self.engine = hlapi.SnmpEngine()
         self.community = hlapi.CommunityData(community, mpModel=pysnmp_snmp_version)
-        self.transport = hlapi.UdpTransportTarget((host, port))
+        self.transport = asyncio.run(hlapi.UdpTransportTarget.create((host, port)))
 
         # context data sets the version used. Default SNMPv2
         self.ctx_data = hlapi.ContextData()
@@ -152,28 +152,26 @@ class MIBLoader:
                 f"file://{mib_dir}",
             ],
         )
-        logger.debug(f"mib sources: {self.mibBuilder.getMibSources()}")
+        logger.debug(f"mib sources: {self.mibBuilder.get_mib_sources()}")
 
     def load_pymib(self, mib_name):
-        self.mibBuilder.loadModules(mib_name)
+        self.mibBuilder.load_modules(mib_name)
 
 
-def test_SNMP_connection(community, host, port=161):
+async def test_SNMP_connection(community, host, port=161):
     """
     Attempts to read a single SNMP point with the given parameters
     sysUpTime is chosen because it is pretty much Ubiquitous point.
     """
 
-    iterator = hlapi.getCmd(
+    errorIndication, errorStatus, errorIndex, _ = await hlapi.get_cmd(
         hlapi.SnmpEngine(),
         hlapi.CommunityData(community, mpModel=1),
-        hlapi.UdpTransportTarget((host, port), timeout=2, retries=1),
+        await hlapi.UdpTransportTarget.create((host, port), timeout=2, retries=1),
         hlapi.ContextData(),
         hlapi.ObjectType(hlapi.ObjectIdentity("SNMPv2-MIB", "sysUpTime", 0)),
     )
 
-    errorIndication, errorStatus, errorIndex, _ = next(iterator)
-
     if errorIndication or errorStatus or errorIndex:
         # if not (None, 0, 0) there has been some error
         logger.debug(
diff --git a/tangostationcontrol/tangostationcontrol/common/antennas.py b/tangostationcontrol/tangostationcontrol/common/antennas.py
index 7228944964ddd519b9aa738761d3073035b75e7a..b40cae96123bccdb778f8727aab00462aa38173f 100644
--- a/tangostationcontrol/tangostationcontrol/common/antennas.py
+++ b/tangostationcontrol/tangostationcontrol/common/antennas.py
@@ -4,7 +4,7 @@
 import numpy
 
 from tango import Util
-from lofar_station_client.common import CaseInsensitiveString
+from lofar_lotus.dict import CaseInsensitiveString
 
 
 def antenna_set_to_mask(
diff --git a/tangostationcontrol/tangostationcontrol/common/calibration.py b/tangostationcontrol/tangostationcontrol/common/calibration.py
index b952b42f6ecc3ac66ce02b4da6d7724419dc9cf3..2fe724d4472e39eca8f8e738bc6e0c15ab082e28 100644
--- a/tangostationcontrol/tangostationcontrol/common/calibration.py
+++ b/tangostationcontrol/tangostationcontrol/common/calibration.py
@@ -7,7 +7,7 @@ from typing import Dict
 from urllib.parse import urlparse
 
 import numpy
-from lofar_station_client.file_access import member, attribute, read_hdf5
+from lofar_lotus.file_access import member, attribute, read_hdf5
 from minio import Minio
 from tango import DeviceProxy
 
@@ -20,7 +20,7 @@ from tangostationcontrol.common.constants import (
 )
 from tangostationcontrol.common.sdp import complex_to_weights, are_subbands_decreasing
 from tangostationcontrol.common.type_checking import device_name_matches
-from lofar_station_client.common import CaseInsensitiveString
+from lofar_lotus.dict import CaseInsensitiveString
 
 logger = logging.getLogger()
 
diff --git a/tangostationcontrol/tangostationcontrol/common/env_decorators.py b/tangostationcontrol/tangostationcontrol/common/env_decorators.py
index a80fbc911a35ca5b7fb5057ffa045d5badaae0f5..d456fc3ccc1853bd2f9474e6f5f58e168820f44d 100644
--- a/tangostationcontrol/tangostationcontrol/common/env_decorators.py
+++ b/tangostationcontrol/tangostationcontrol/common/env_decorators.py
@@ -7,7 +7,7 @@ import logging
 import time
 
 from tango import DevFailed, DeviceProxy
-from lofar_station_client.common import CaseInsensitiveString
+from lofar_lotus.dict import CaseInsensitiveString
 
 
 logger = logging.getLogger()
diff --git a/tangostationcontrol/tangostationcontrol/common/events/change_events.py b/tangostationcontrol/tangostationcontrol/common/events/change_events.py
index 00eb0a8a6087f4753912bf2b71e41ece5ca861d1..eea33317c0b6353830a2a80c98081350457ca6b9 100644
--- a/tangostationcontrol/tangostationcontrol/common/events/change_events.py
+++ b/tangostationcontrol/tangostationcontrol/common/events/change_events.py
@@ -3,7 +3,7 @@
 
 import numpy
 
-from lofar_station_client.common import CaseInsensitiveString, CaseInsensitiveDict
+from lofar_lotus.dict import CaseInsensitiveString, CaseInsensitiveDict
 from tango.server import Device
 
 
diff --git a/tangostationcontrol/tangostationcontrol/common/events/subscriptions.py b/tangostationcontrol/tangostationcontrol/common/events/subscriptions.py
index a8bd2ed22653b6bcaca49c42577ca01f2b99bf06..60cc6401b33fb9c610238224b70da18947a7f96f 100644
--- a/tangostationcontrol/tangostationcontrol/common/events/subscriptions.py
+++ b/tangostationcontrol/tangostationcontrol/common/events/subscriptions.py
@@ -15,7 +15,7 @@ from tangostationcontrol.common.lofar_logging import exception_to_str
 from tangostationcontrol.common.constants import (
     DEFAULT_METRICS_POLLING_PERIOD_MS,
 )
-from lofar_station_client.common import CaseInsensitiveString
+from lofar_lotus.dict import CaseInsensitiveString
 from tangostationcontrol.metrics import AttributeMetric
 
 logger = logging.getLogger()
diff --git a/tangostationcontrol/tangostationcontrol/common/proxies/create_proxies.py b/tangostationcontrol/tangostationcontrol/common/proxies/create_proxies.py
index 3a23f1734ef3a050442f6fecc3f464e590da26b9..ce7fb78d5828a6a542f02df4729e8e2dc563f1aa 100644
--- a/tangostationcontrol/tangostationcontrol/common/proxies/create_proxies.py
+++ b/tangostationcontrol/tangostationcontrol/common/proxies/create_proxies.py
@@ -6,7 +6,7 @@ import abc
 
 import tango
 
-from lofar_station_client.common import CaseInsensitiveDict
+from lofar_lotus.dict import CaseInsensitiveDict
 
 from tangostationcontrol.common.proxies.proxy import create_device_proxy
 from tangostationcontrol.common.types.device_config_types import (
diff --git a/tangostationcontrol/tangostationcontrol/common/types/device_config_types.py b/tangostationcontrol/tangostationcontrol/common/types/device_config_types.py
index f12a42c22056a25e80d00aa5e44091cc0466dcc5..81bd86fb8fc98cf30cbf4fdad400750d6ae6cf0c 100644
--- a/tangostationcontrol/tangostationcontrol/common/types/device_config_types.py
+++ b/tangostationcontrol/tangostationcontrol/common/types/device_config_types.py
@@ -7,7 +7,7 @@ from typing import List, Union
 
 from tango import DeviceProxy
 
-from lofar_station_client.common import CaseInsensitiveDict
+from lofar_lotus.dict import CaseInsensitiveDict
 
 # Dictionary of device class keys with attribute name list
 device_config_type = CaseInsensitiveDict[str, List[str]]
diff --git a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/hierarchy_device.py b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/hierarchy_device.py
index 7e488828b6fe1f9ab4b65fd097ee6a1d61112296..786d9629492f7341bf9bf6561b9446374ffaf5e7 100644
--- a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/hierarchy_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/hierarchy_device.py
@@ -10,7 +10,7 @@ from typing import Dict, List, Optional, Callable, Union
 
 from tango import Database, DevState, DeviceProxy
 
-from lofar_station_client.common import CaseInsensitiveDict, CaseInsensitiveString
+from lofar_lotus.dict import CaseInsensitiveDict, CaseInsensitiveString
 from tangostationcontrol.common.type_checking import device_name_matches
 from tangostationcontrol.common.proxies.proxy import create_device_proxy
 
diff --git a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/lofar_device.py b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/lofar_device.py
index 67f4f72e418a31d31a480895f24e430a396231b2..84a4fd3a053d0be097d276d950ae6077ce3b7021 100644
--- a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/lofar_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/lofar_device.py
@@ -48,7 +48,7 @@ from tangostationcontrol.common.constants import (
     DEFAULT_METRICS_POLLING_PERIOD_MS,
     DEFAULT_POLLING_PERIOD_MS,
 )
-from lofar_station_client.common import CaseInsensitiveDict
+from lofar_lotus.dict import CaseInsensitiveDict
 from tangostationcontrol.common.events import EventSubscriptions, ChangeEvents
 from tangostationcontrol.common.json_encoder import JsonEncoder
 from tangostationcontrol.common.lofar_logging import (
diff --git a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/snmp_device.py b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/snmp_device.py
index 37649c5350e2610d4e6aa10ada154a2e02a996c8..c5ef2988150b31383d26e18e6fbe848a7d440407 100644
--- a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/snmp_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/snmp_device.py
@@ -2,7 +2,7 @@
 # SPDX-License-Identifier: Apache-2.0
 
 """SNMP Device Server for LOFAR2.0"""
-
+import asyncio
 import logging
 import os
 
@@ -10,7 +10,6 @@ try:
     from importlib.resources import files
 except ImportError:
     from importlib_resources import files  # type: ignore
-from pysmi import debug
 from tango.server import device_property, attribute
 from tangostationcontrol.clients.snmp.attribute_classes import SNMPAttribute
 
@@ -66,18 +65,18 @@ class SNMPDevice(LOFARDevice):
         """
         Test whether we can connect with the SNMP server
         """
-        return test_SNMP_connection(self.SNMP_community, self.SNMP_host)
+        return asyncio.run(test_SNMP_connection(self.SNMP_community, self.SNMP_host))
 
     def __init__(self, cl, name):
         # Super must be called after variable assignment due to executing init_device!
         super().__init__(cl, name)
 
         # Configure PySMI debug logging
-        debug.setLogger(
-            debug.Debug(
-                "searcher", "compiler", "borrower", "reader", loggerName="pysmi"
-            )
-        )
+        # debug.set_logger(
+        #     debug.Debug(
+        #         "searcher", "compiler", "borrower", "reader", loggerName="pysmi"
+        #     )
+        # )
 
     def configure_for_initialise(self, simulator_class):
         if self.SNMP_use_simulators:
diff --git a/tangostationcontrol/tangostationcontrol/devices/calibration.py b/tangostationcontrol/tangostationcontrol/devices/calibration.py
index c78c328307a9be9d55f522063fd96c0e173de7d1..512ecd94f966029c397983dc636b72c2435e51f2 100644
--- a/tangostationcontrol/tangostationcontrol/devices/calibration.py
+++ b/tangostationcontrol/tangostationcontrol/devices/calibration.py
@@ -12,7 +12,7 @@ from tangostationcontrol.common.calibration import (
     calibrate_RCU_attenuator_dB,
     calibrate_input_samples_delay,
 )
-from lofar_station_client.common import (
+from lofar_lotus.dict import (
     CaseInsensitiveDict,
     CaseInsensitiveString,
 )
diff --git a/tangostationcontrol/tangostationcontrol/devices/metadata.py b/tangostationcontrol/tangostationcontrol/devices/metadata.py
index 05d1aae7f3ac878accc4c0ad3cd61f1ae96d0494..d5a2601d4eb6545f2f20ed1ae7558a22bb21a3a2 100644
--- a/tangostationcontrol/tangostationcontrol/devices/metadata.py
+++ b/tangostationcontrol/tangostationcontrol/devices/metadata.py
@@ -13,8 +13,8 @@ from tango import DebugIt, DeviceProxy
 from tango.server import device_property, attribute, command
 
 # PyTango imports
-from lofar_station_client.common import CaseInsensitiveDict
-from lofar_station_client.zeromq.publisher import ZeroMQPublisher
+from lofar_lotus.dict import CaseInsensitiveDict
+from lofar_lotus.zeromq import ZeroMQPublisher
 
 from tangostationcontrol.asyncio import PeriodicTask
 from tangostationcontrol.common.device_decorators import only_in_states, log_exceptions
@@ -203,7 +203,7 @@ class Metadata(LOFARDevice):
 
         if not self._publisher:
             self._publisher = ZeroMQPublisher(
-                ZeroMQPublisher.contstruct_bind_uri(
+                ZeroMQPublisher.construct_bind_uri(
                     self.metadata_protocol, self.metadata_bind, self.metadata_port
                 ),
                 [self.metadata_topic],
diff --git a/tangostationcontrol/tangostationcontrol/devices/protection_control.py b/tangostationcontrol/tangostationcontrol/devices/protection_control.py
index 769c34b6269ae23758571805d7f66494fc46c848..929e4fc01ef5adc25ddf153e4e78380289ce8255 100644
--- a/tangostationcontrol/tangostationcontrol/devices/protection_control.py
+++ b/tangostationcontrol/tangostationcontrol/devices/protection_control.py
@@ -15,7 +15,7 @@ from tango.server import attribute, device_property
 from tango.server import command
 
 # Additional import
-from lofar_station_client.common import CaseInsensitiveDict
+from lofar_lotus.dict import CaseInsensitiveDict
 
 from tangostationcontrol.asyncio import PeriodicTask
 from tangostationcontrol.common.types.device_config_types import (
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py
index bff5e860be1d67eb352d4b8d0be6cd701138c028..45deb68ec87cf425956b48b5a4aef59e1a24d430 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py
@@ -16,7 +16,7 @@ from typing import Dict
 
 from prometheus_client import Counter, Gauge
 
-from lofar_station_client.zeromq.subscriber import AsyncZeroMQSubscriber
+from lofar_lotus.zeromq import AsyncZeroMQSubscriber
 
 # PyTango imports
 from tango.server import device_property, attribute
diff --git a/tangostationcontrol/tangostationcontrol/metadata/metadata_organizer.py b/tangostationcontrol/tangostationcontrol/metadata/metadata_organizer.py
index dfac39c6b50700ab006a46503a83f9c1b817997c..5384e322f2963160fa74797a9459bf0a455a3a7d 100644
--- a/tangostationcontrol/tangostationcontrol/metadata/metadata_organizer.py
+++ b/tangostationcontrol/tangostationcontrol/metadata/metadata_organizer.py
@@ -9,7 +9,7 @@ from typing import Any
 import numpy
 from tango import DeviceProxy
 
-from lofar_station_client.common import CaseInsensitiveDict
+from lofar_lotus.dict import CaseInsensitiveDict
 from tangostationcontrol.common.lofar_logging import exception_to_str
 from tangostationcontrol.common.proxies.create_proxies import (
     create_proxies,
diff --git a/tangostationcontrol/tangostationcontrol/protection/config_types.py b/tangostationcontrol/tangostationcontrol/protection/config_types.py
index 62a4843d296e1ca38cdad500529dc32e99d384c1..d70bd68868d68e41a83ea096b8120cccd6d29cf3 100644
--- a/tangostationcontrol/tangostationcontrol/protection/config_types.py
+++ b/tangostationcontrol/tangostationcontrol/protection/config_types.py
@@ -5,7 +5,7 @@ import copy
 import logging
 
 import tango
-from lofar_station_client.common import CaseInsensitiveDict
+from lofar_lotus.dict import CaseInsensitiveDict
 from tango import DevFailed
 
 from tangostationcontrol.common.types.device_config_types import device_config_type
diff --git a/tangostationcontrol/tangostationcontrol/protection/protection_manager.py b/tangostationcontrol/tangostationcontrol/protection/protection_manager.py
index 3867a976d00996102be63c6470055c0ca6332649..629e2b122011159b986c73a3c9abfb5212b2901f 100644
--- a/tangostationcontrol/tangostationcontrol/protection/protection_manager.py
+++ b/tangostationcontrol/tangostationcontrol/protection/protection_manager.py
@@ -9,7 +9,7 @@ from concurrent.futures import ThreadPoolExecutor
 from typing import Any
 
 import tango
-from lofar_station_client.common import CaseInsensitiveDict
+from lofar_lotus.dict import CaseInsensitiveDict
 from tango import DevFailed
 
 from tangostationcontrol.common.atomic import Atomic
diff --git a/tangostationcontrol/tangostationcontrol/rpc/messagehandler.py b/tangostationcontrol/tangostationcontrol/rpc/messagehandler.py
index c5d738bb9f283d6ae0dfd51ac9bbb73dfb576672..08a9314d3a3244a1b11bda1a14d19d3ee49a8cf9 100644
--- a/tangostationcontrol/tangostationcontrol/rpc/messagehandler.py
+++ b/tangostationcontrol/tangostationcontrol/rpc/messagehandler.py
@@ -9,7 +9,7 @@ from typing import Callable, Dict
 from prometheus_client import Counter, Gauge
 import zmq
 
-from lofar_station_client.zeromq.subscriber import ZeroMQSubscriber
+from lofar_lotus.zeromq import ZeroMQSubscriber
 from tangostationcontrol.metrics import AttributeMetric
 
 import logging
diff --git a/tangostationcontrol/tangostationcontrol/toolkit/generate_caltable_from_lofar1.py b/tangostationcontrol/tangostationcontrol/toolkit/generate_caltable_from_lofar1.py
index 3710e4b28f1a2afae0b4eba74dc13b8aafd77b91..fa58d936821bccd2778698fd0a4fce723487250c 100755
--- a/tangostationcontrol/tangostationcontrol/toolkit/generate_caltable_from_lofar1.py
+++ b/tangostationcontrol/tangostationcontrol/toolkit/generate_caltable_from_lofar1.py
@@ -6,7 +6,7 @@ from tangostationcontrol.toolkit.lofar1.caltable import LOFAR1CalTables
 
 import numpy
 
-from lofar_station_client.file_access import create_hdf5
+from lofar_lotus.file_access import create_hdf5
 from tangostationcontrol.common import calibration
 
 
diff --git a/tangostationcontrol/test-requirements.txt b/tangostationcontrol/test-requirements.txt
index bd14f1512a97338c4b0315e331fced966a12a6d2..a3026ac04573e70db5ed03c05389a03cd6259014 100644
--- a/tangostationcontrol/test-requirements.txt
+++ b/tangostationcontrol/test-requirements.txt
@@ -20,9 +20,9 @@ python-subunit<1.4.3,>=1.4.0 # Apache-2.0/BSD
 Pygments>=2.6.0 # BSD
 testscenarios>=0.5.0 # Apache-2.0/BSD
 testtools>=2.4.0 # MIT
-timeout-decorator>=0.5.0  # MIT
 xenon>=0.8.0 # MIT
 prometheus_client # Apache-2.0
 pytest>=7.3.0 # MIT
 pytest-forked>=1.6.0 # MIT
 pytest-cov >= 3.0.0 # MIT
+pytest-timeout # MIT
diff --git a/tangostationcontrol/test/clients/test_snmp_client.py b/tangostationcontrol/test/clients/test_snmp_client.py
index 3812ffa46e539f6d81440654b124f7529ccf53fa..727c810a260e3b0f5452f56966ffa8314dc88b06 100644
--- a/tangostationcontrol/test/clients/test_snmp_client.py
+++ b/tangostationcontrol/test/clients/test_snmp_client.py
@@ -6,7 +6,7 @@ from unittest import mock
 from test import base
 
 import numpy
-from pysnmp import hlapi
+import pysnmp.hlapi.v3arch.asyncio as hlapi
 from pysnmp.smi import view, error
 from pysnmp.smi.rfc1902 import ObjectIdentity
 
@@ -162,8 +162,8 @@ class TestSNMP(base.TestCase):
             with self.assertRaises(ValueError):
                 client._process_annotation(annotation=i)
 
-    @mock.patch("pysnmp.hlapi.ObjectIdentity")
-    @mock.patch("pysnmp.hlapi.ObjectType")
+    @mock.patch("pysnmp.hlapi.v3arch.asyncio.ObjectIdentity")
+    @mock.patch("pysnmp.hlapi.v3arch.asyncio.ObjectType")
     @mock.patch("tangostationcontrol.clients.snmp.snmp_client.SNMPComm.getter")
     def test_snmp_obj_get(self, m_next, m_obj_T, m_obj_i):
         """
@@ -206,9 +206,9 @@ class TestSNMP(base.TestCase):
                         f"During test {j} {i}; Expected: {checkval} of type {i}, got: {val} of type {type(val)}",
                     )
 
-    @mock.patch("pysnmp.hlapi.ObjectIdentity")
-    @mock.patch("pysnmp.hlapi.ObjectType")
-    @mock.patch("pysnmp.hlapi.setCmd")
+    @mock.patch("pysnmp.hlapi.v3arch.asyncio.ObjectIdentity")
+    @mock.patch("pysnmp.hlapi.v3arch.asyncio.ObjectType")
+    @mock.patch("pysnmp.hlapi.v3arch.asyncio.set_cmd")
     @mock.patch("tangostationcontrol.clients.snmp.snmp_client.SNMPComm.setter")
     def test_snmp_obj_set(self, m_next, m_nextCmd, m_obj_T, m_obj_ID):
         """
@@ -286,7 +286,7 @@ class TestSNMP(base.TestCase):
             [
                 (
                     None,
-                    hlapi.Integer.withNamedValues(enable=1, disable=0)(1),
+                    hlapi.Integer.with_named_values(enable=1, disable=0)(1),
                 )
             ]
         ]
diff --git a/tangostationcontrol/test/common/events/test_register_subscriptions.py b/tangostationcontrol/test/common/events/test_register_subscriptions.py
index 2421974c9d894c4de51b72edd5cf1e62c800032d..20aee763cb02d5de057018fb135e8a43b27bce93 100644
--- a/tangostationcontrol/test/common/events/test_register_subscriptions.py
+++ b/tangostationcontrol/test/common/events/test_register_subscriptions.py
@@ -6,7 +6,7 @@ from unittest import mock
 from unittest.mock import call
 
 import tango
-from lofar_station_client.common import CaseInsensitiveDict
+from lofar_lotus.dict import CaseInsensitiveDict
 from tango import DeviceProxy, DevFailed
 
 from tangostationcontrol.common.constants import DEFAULT_POLLING_PERIOD_METADATA_MS
diff --git a/tangostationcontrol/test/common/proxy/test_create_proxies.py b/tangostationcontrol/test/common/proxy/test_create_proxies.py
index cb532188f204ce02755ec238c64e3e55f28e22a7..a5c3222f25ea071b0d8c1a4c28686291360ee26d 100644
--- a/tangostationcontrol/test/common/proxy/test_create_proxies.py
+++ b/tangostationcontrol/test/common/proxy/test_create_proxies.py
@@ -5,7 +5,7 @@ import logging
 from unittest import mock
 
 import tango
-from lofar_station_client.common import CaseInsensitiveDict
+from lofar_lotus.dict import CaseInsensitiveDict
 
 from tangostationcontrol.common.types.device_config_types import (
     device_config_type,
diff --git a/tangostationcontrol/test/devices/base_device_classes/test_beam_device.py b/tangostationcontrol/test/devices/base_device_classes/test_beam_device.py
index 07d83166810ca455fb4f8f09e67f3d8692e72c4e..0517460b5a25f0c46356bf179c8fc916e112a633 100644
--- a/tangostationcontrol/test/devices/base_device_classes/test_beam_device.py
+++ b/tangostationcontrol/test/devices/base_device_classes/test_beam_device.py
@@ -9,7 +9,7 @@ import logging
 import time
 
 import numpy
-import timeout_decorator
+import pytest
 from tango import DevFailed
 
 from tangostationcontrol.common.atomic import Atomic
@@ -101,7 +101,7 @@ class TestBeamTracker(base.TestCase):
             )
             time.sleep(step)
 
-    @timeout_decorator.timeout(5)
+    @pytest.mark.timeout(5)
     def test_beamtracker_start_stop(self):
         """Test repeatedly starting and stopping the beamtracking
 
@@ -191,7 +191,7 @@ class TestBeamTracker(base.TestCase):
 
         self.beamtracker_base(interval, setup=setup, test=test)
 
-    @timeout_decorator.timeout(5)
+    @pytest.mark.timeout(5)
     def test_update_pointing_early(self):
         """Test that early updates sleep until the intended update time has passed"""
 
@@ -216,7 +216,7 @@ class TestBeamTracker(base.TestCase):
             test=test,
         )
 
-    @timeout_decorator.timeout(5)
+    @pytest.mark.timeout(5)
     def test_update_pointing_late(self):
         """Test that late updates do not cause any additional sleep"""
 
@@ -241,7 +241,7 @@ class TestBeamTracker(base.TestCase):
             test=test,
         )
 
-    @timeout_decorator.timeout(5)
+    @pytest.mark.timeout(5)
     def test_update_pointing_stale(self):
         """Test the :attr:`stale_pointing` functionality for beamtracking"""
         test_value = "update-pointing-stale"
diff --git a/tangostationcontrol/test/devices/base_device_classes/test_opcua_device.py b/tangostationcontrol/test/devices/base_device_classes/test_opcua_device.py
index 09d314780706aa39384f48417051b9520b4ea6fb..cb266b1b3f4a641963be0af737493636c4cb6845 100644
--- a/tangostationcontrol/test/devices/base_device_classes/test_opcua_device.py
+++ b/tangostationcontrol/test/devices/base_device_classes/test_opcua_device.py
@@ -5,10 +5,10 @@ import logging
 
 import numpy
 import time
+import pytest
 from tango import DevFailed
 from tango.test_context import DeviceTestContext
 from attribute_wrapper.attribute_wrapper import AttributeWrapper
-import timeout_decorator
 
 from tangostationcontrol.devices.base_device_classes import opcua_device
 
@@ -34,11 +34,11 @@ class RunOPCUADevice(device_base.DeviceTestCase):
 
         self.test_device = opcua_device.OPCUADevice
 
-    @timeout_decorator.timeout(10)
+    # @pytest.mark.timeout(10)
     def test_connect(self):
         """Test if device connects to the OPC-UA server."""
 
-        with RunOPCUAServer(12345, "http://lofar.eu") as opcua_server:
+        with RunOPCUAServer(12345, "http://lofar.eu"):
             with DeviceTestContext(
                 self.TestDevice, properties=self.TestDevice.PROPERTIES, process=True
             ) as proxy:
@@ -49,7 +49,7 @@ class RunOPCUADevice(device_base.DeviceTestCase):
 
                 proxy.off()
 
-    @timeout_decorator.timeout(30)
+    @pytest.mark.timeout(30)
     def test_read_after_disconnect(self):
         """Test if reads fail after disconnecting from the OPC-UA server."""
 
@@ -66,21 +66,21 @@ class RunOPCUADevice(device_base.DeviceTestCase):
             with self.assertRaises(DevFailed):
                 _ = proxy.float_R
 
-    @timeout_decorator.timeout(30)
+    @pytest.mark.timeout(30)
     def test_read_after_reconnect(self):
         """Test if reads succeeds after reconnecting to the OPC-UA server."""
 
         with DeviceTestContext(
             self.TestDevice, properties=self.TestDevice.PROPERTIES, process=True
         ) as proxy:
-            with RunOPCUAServer(12345, "http://lofar.eu") as opcua_server:
+            with RunOPCUAServer(12345, "http://lofar.eu"):
                 proxy.initialise()
 
             while proxy.connected_R:
                 logger.debug("Sleeping to wait for disconnect")
                 time.sleep(0.5)
 
-            with RunOPCUAServer(12345, "http://lofar.eu") as opcua_server:
+            with RunOPCUAServer(12345, "http://lofar.eu"):
                 while not proxy.connected_R:
                     logger.debug("Sleeping to wait for reconnect")
                     time.sleep(0.5)
diff --git a/tangostationcontrol/test/devices/test_station_manager_device.py b/tangostationcontrol/test/devices/test_station_manager_device.py
index b831361f9c553e906506efdf20c1697bafa62e24..520e061573a2d07a4f8f049231ecc17507bad076 100644
--- a/tangostationcontrol/test/devices/test_station_manager_device.py
+++ b/tangostationcontrol/test/devices/test_station_manager_device.py
@@ -4,7 +4,7 @@
 import asyncio
 import time
 
-from timeout_decorator import timeout_decorator
+import pytest
 
 from unittest import mock
 
@@ -45,7 +45,7 @@ class TestStationManagerDevice(device_base.DeviceTestCase):
         )
         self.off_to_hibernate = lambda: time.sleep(30)
 
-    @timeout_decorator.timeout(10)
+    @pytest.mark.timeout(10)
     def test_state_transition_timeout(self):
         timeout = 1
 
diff --git a/tangostationcontrol/test/metadata/test_metadata_organizer.py b/tangostationcontrol/test/metadata/test_metadata_organizer.py
index a1d596ba1f96581b1b30d1d32976fd1ab5831223..7d737a2c4b926667db4e4f1d97cc555412334928 100644
--- a/tangostationcontrol/test/metadata/test_metadata_organizer.py
+++ b/tangostationcontrol/test/metadata/test_metadata_organizer.py
@@ -10,7 +10,7 @@ from unittest.mock import patch
 import numpy
 from tango import DevFailed
 
-from lofar_station_client.common import CaseInsensitiveDict
+from lofar_lotus.dict import CaseInsensitiveDict
 
 from tangostationcontrol.common.lofar_logging import exception_to_str
 from tangostationcontrol.common.proxies import create_proxies
diff --git a/tangostationcontrol/test/opcua.py b/tangostationcontrol/test/opcua.py
index 4172cbec89730f3efaa77531345070f405f74180..ec9cd20e9c6c99995337b7d67f352cbacc446426 100644
--- a/tangostationcontrol/test/opcua.py
+++ b/tangostationcontrol/test/opcua.py
@@ -1,5 +1,6 @@
 import asyncio
 import asyncua
+import asyncua.ua
 import time
 
 from tangostationcontrol.asyncio import EventLoopThread
@@ -43,7 +44,7 @@ class RunOPCUAServer:
         self.future = None
 
     async def run_server(self):
-        async with await make_opcua_server(self.port, self.namespace) as opcua_server:
+        async with await make_opcua_server(self.port, self.namespace):
             self.running = True
             while not self.stopping:
                 await asyncio.sleep(0.1)
@@ -63,6 +64,10 @@ class RunOPCUAServer:
 
     def stop(self):
         self.stopping = True
-        _ = self.future.result()
+        try:
+            _ = self.future.result(2)
+        except TimeoutError:
+            print("The coroutine took too long, cancelling the task...")
+            self.future.cancel()
 
         self.event_loop_thread.stop()
diff --git a/tangostationcontrol/test/protection/test_config_types.py b/tangostationcontrol/test/protection/test_config_types.py
index 00889507551d6d823ac3983f82ac6eed14f3be9a..b2cb330774228ac400dd166f6d4292fa35760dca 100644
--- a/tangostationcontrol/test/protection/test_config_types.py
+++ b/tangostationcontrol/test/protection/test_config_types.py
@@ -4,7 +4,7 @@
 import logging
 from unittest import mock
 
-from lofar_station_client.common import CaseInsensitiveDict
+from lofar_lotus.dict import CaseInsensitiveDict
 
 from tangostationcontrol.devices import ProtectionControl
 from tangostationcontrol.protection import config_types
diff --git a/tangostationcontrol/test/protection/test_metrics.py b/tangostationcontrol/test/protection/test_metrics.py
index 37a2e52208d00a06a898d971d1511a793e5d0507..b43389e1aef69c8f26e746c640a26a77be8604a8 100644
--- a/tangostationcontrol/test/protection/test_metrics.py
+++ b/tangostationcontrol/test/protection/test_metrics.py
@@ -4,7 +4,7 @@
 import logging
 from unittest import mock
 
-from lofar_station_client.common import CaseInsensitiveDict
+from lofar_lotus.dict import CaseInsensitiveDict
 from tango import DeviceProxy, DevFailed
 
 from tangostationcontrol.protection.config_types import (
diff --git a/tangostationcontrol/test/protection/test_protection_manager.py b/tangostationcontrol/test/protection/test_protection_manager.py
index 99622c7f6b187542ebc067b2a4f527b1dab3047f..eaeed73c8bfc99d97b216807474549e961871e17 100644
--- a/tangostationcontrol/test/protection/test_protection_manager.py
+++ b/tangostationcontrol/test/protection/test_protection_manager.py
@@ -6,9 +6,9 @@ from unittest import mock
 from unittest.mock import PropertyMock
 
 import tango
-from lofar_station_client.common import CaseInsensitiveDict
+from lofar_lotus.dict import CaseInsensitiveDict
 from tango import DevFailed
-from timeout_decorator import timeout_decorator
+import pytest
 
 from tangostationcontrol.common.constants import N_pn
 from tangostationcontrol.protection.threshold import NumberProtectionThreshold
@@ -105,7 +105,7 @@ class TestProtectionManager(base.TestCase):
         t_manager.evaluate("sdpfirmware", "fpga_temp_r", [91.4] * N_pn)
         m_shutdown["mock"].assert_called_once()
 
-    @timeout_decorator.timeout(10)
+    @pytest.mark.timeout(10)
     def test_protective_shutdown_already_protected(self):
         """Test the iterative shutdown sequence"""
 
@@ -123,7 +123,7 @@ class TestProtectionManager(base.TestCase):
 
         self.assertTrue(self.m_create_proxy["mock"].return_value.protection_lock_RW)
 
-    @timeout_decorator.timeout(10)
+    @pytest.mark.timeout(10)
     def test_protective_shutdown_transition_once(self):
         """Test having to transition once for protective shutdown"""
         m_transition_hibernate = mock.Mock()
@@ -158,7 +158,7 @@ class TestProtectionManager(base.TestCase):
         m_transition_hibernate.assert_called_once()
         self.assertEqual(t_manager._state, ProtectionStateEnum.OFF)
 
-    @timeout_decorator.timeout(10)
+    @pytest.mark.timeout(10)
     def test_protective_shutdown_transition_await(self):
         """Test awaiting state transition completion"""
         m_transition_hibernate = mock.Mock()
@@ -196,7 +196,7 @@ class TestProtectionManager(base.TestCase):
         m_time["mock"].assert_called_once()
         self.assertEqual(t_manager._state, ProtectionStateEnum.OFF)
 
-    @timeout_decorator.timeout(10)
+    @pytest.mark.timeout(10)
     def test_protective_shutdown_anticipate_transition(self):
         """Test protective shutdown anticipate ongoing transition"""
 
diff --git a/tangostationcontrol/test/rpc/test_messagehandler.py b/tangostationcontrol/test/rpc/test_messagehandler.py
index c4becbec480f7ee2082996aa8c0f90e31af4ddf0..21ff2229456ce8c092a62228b88a4478f4348f41 100644
--- a/tangostationcontrol/test/rpc/test_messagehandler.py
+++ b/tangostationcontrol/test/rpc/test_messagehandler.py
@@ -2,9 +2,9 @@
 # SPDX-License-Identifier: Apache-2.0
 
 import time
+import pytest
 
-from lofar_station_client.zeromq.publisher import ZeroMQPublisher
-from timeout_decorator import timeout_decorator
+from lofar_lotus.zeromq import ZeroMQPublisher
 
 from tangostationcontrol.rpc.messagehandler import MultiEndpointZMQMessageHandler
 
@@ -15,7 +15,7 @@ class TestMultiEndpointZMQMessageHandler(base.TestCase):
     DEFAULT_PUBLISH_ADDRESS = "tcp://*:6001"
     DEFAULT_SUBSCRIBE_ADDRESS = "tcp://127.0.0.1:6001"
 
-    @timeout_decorator.timeout(2)
+    @pytest.mark.timeout(2)
     def test(self):
         t_msg = '{"foo": "bar"}'
         t_topic = "topic"
diff --git a/tangostationcontrol/tox.ini b/tangostationcontrol/tox.ini
index f1e8c83088557d0b671317420efd7ed41d916503..090286a2a5a32c2a804d3a619cd93ff2ee52274e 100644
--- a/tangostationcontrol/tox.ini
+++ b/tangostationcontrol/tox.ini
@@ -16,7 +16,7 @@ wheel_build_env = .pkg
 ;  - lofar-station-client
 setenv =
     PYTHONWARNINGS=default::DeprecationWarning
-    PIP_EXTRA_INDEX_URL=https://git.astron.nl/api/v4/projects/395/packages/pypi/simple
+    PIP_EXTRA_INDEX_URL=https://git.astron.nl/api/v4/groups/36/-/packages/pypi/simple
 ; Share the same envdir with as many jobs as possible due to extensive time it
 ; takes to compile the pytango wheel, in addition to its large install size.
 ; should the environment change (such as the Python version) the environment
@@ -47,7 +47,7 @@ passenv =
 setenv =
     VIRTUAL_ENV={envdir}
     TESTS_DIR=./integration_test/{env:TEST_MODULE:default}
-    PIP_EXTRA_INDEX_URL=https://git.astron.nl/api/v4/projects/395/packages/pypi/simple
+    PIP_EXTRA_INDEX_URL=https://git.astron.nl/api/v4/groups/36/-/packages/pypi/simple
 commands =
     echo "Integration test directory configured for{env:TESTS_DIR} ({env:TEST_MODULE:default})"
     {envpython} -m pytest --version
@@ -62,7 +62,7 @@ runner = ignore_env_name_mismatch
 envdir = {toxworkdir}/coverage
 setenv =
     VIRTUAL_ENV={envdir}
-    PIP_EXTRA_INDEX_URL=https://git.astron.nl/api/v4/projects/395/packages/pypi/simple
+    PIP_EXTRA_INDEX_URL=https://git.astron.nl/api/v4/groups/36/-/packages/pypi/simple
 commands =
     {envpython} -m pytest --version
     {envpython} -m coverage --version