diff --git a/devices/HW_device_implementation.py b/devices/HW_device_template.py
similarity index 91%
rename from devices/HW_device_implementation.py
rename to devices/HW_device_template.py
index 0c76b0c519d96e0491da7d1852f48afc0bb74752..3d8b19c20b37cdf9b5d1d5780d28edad0c333f0d 100644
--- a/devices/HW_device_implementation.py
+++ b/devices/HW_device_template.py
@@ -7,7 +7,7 @@
 # Distributed under the terms of the APACHE license.
 # See LICENSE.txt for more info.
 
-""" PCC Device Server for LOFAR2.0
+""" Hardware Device Server for LOFAR2.0
 
 """
 
@@ -80,7 +80,7 @@ class HW_dev(hardware_device):
 # Run server
 # ----------
 def main(args=None, **kwargs):
-	"""Main function of the PCC module."""
+	"""Main function of the hardware device module."""
 	return run((HW_dev,), args=args, **kwargs)
 
 
diff --git a/devices/LICENSE.txt b/devices/LICENSE.txt
index 583bd5a9884fcc0c6e3e6908de9a930203b19d24..8a0eaeb196094a651006f51fd99c0c05cb16ccd6 100644
--- a/devices/LICENSE.txt
+++ b/devices/LICENSE.txt
@@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
    same "printed page" as the copyright notice for easier
    identification within third-party archives.
 
-Copyright [yyyy] [name of copyright owner]
+Copyright 2021 ASTRON Netherlands Institute for Radio Astronomy
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/devices/PCC.py b/devices/PCC.py
index a0cb16d4ac6b2bc50e06d5153f270366707b6104..d9f1c04305d26a0a4ea60942097d3ba639412c44 100644
--- a/devices/PCC.py
+++ b/devices/PCC.py
@@ -12,7 +12,8 @@
 """
 
 # PyTango imports
-from tango.server import run
+from tango import DebugIt
+from tango.server import run, command
 from tango.server import device_property
 # Additional import
 
@@ -55,6 +56,10 @@ class PCC(hardware_device):
 		dtype='DevDouble',
 		mandatory=True
 	)
+	OPC_namespace = device_property(
+		dtype='DevString',
+		mandatory=False
+	)
 
 	# ----------
 	# Attributes
@@ -85,15 +90,6 @@ class PCC(hardware_device):
 
 	RCU_monitor_rate_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_monitor_rate_RW"], datatype=numpy.float64, access=AttrWriteType.READ_WRITE)
 
-	def setup_value_dict(self):
-		""" set the initial value for all the attribute wrapper objects"""
-
-		self.value_dict = {str(i): i.initial_value() for i in self.attr_list()}
-
-	def always_executed_hook(self):
-		"""Method always executed before any TANGO command is executed."""
-		pass
-
 	def delete_device(self):
 		"""Hook to delete resources allocated in init_device.
 
@@ -116,15 +112,20 @@ class PCC(hardware_device):
 		self.opcua_connection.stop()
 
 	def initialise(self):
-		""" user code here. is called when the state is set to STANDBY """
-
-		"""Initialises the attributes, values and properties of the PCC."""
-
-		# will contain all the values for this device
-		self.setup_value_dict()
+		""" user code here. is called when the state is set to INIT """
+
+		# Init the dict that contains function to OPC-UA function mappings.
+		self.function_mapping = {}
+		self.function_mapping["RCU_on"] = {}
+		self.function_mapping["RCU_off"] = {}
+		self.function_mapping["ADC_on"] = {}
+		self.function_mapping["RCU_update"] = {}
+		self.function_mapping["CLK_on"] = {}
+		self.function_mapping["CLK_off"] = {}
+		self.function_mapping["CLK_PLL_setup"] = {}
 
 		#set up the OPC ua client
-		self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), self.OPC_Time_Out, self.Standby, self.Fault, self)
+		self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), "http://lofar.eu", self.OPC_Time_Out, self.Standby, self.Fault, self)
 
 		# map the attributes to the OPC ua comm client
 		for i in self.attr_list():
@@ -135,6 +136,83 @@ class PCC(hardware_device):
 	# --------
 	# Commands
 	# --------
+	@command()
+	@DebugIt()
+	@only_when_on
+	@fault_on_error
+	def RCU_off(self):
+		"""
+
+		:return:None
+		"""
+		self.function_mapping["RCU_off"]()
+
+	@command()
+	@DebugIt()
+	@only_when_on
+	@fault_on_error
+	def RCU_on(self):
+		"""
+
+		:return:None
+		"""
+		self.function_mapping["RCU_on"]()
+
+	@command()
+	@DebugIt()
+	@only_when_on
+	@fault_on_error
+	def ADC_on(self):
+		"""
+
+		:return:None
+		"""
+		self.function_mapping["ADC_on"]()
+
+	@command()
+	@DebugIt()
+	@only_when_on
+	@fault_on_error
+	def RCU_update(self):
+		"""
+
+		:return:None
+		"""
+		self.function_mapping["RCU_update"]()
+
+	@command()
+	@DebugIt()
+	@only_when_on
+	@fault_on_error
+	def CLK_off(self):
+		"""
+
+		:return:None
+		"""
+		self.function_mapping["CLK_off"]()
+
+	@command()
+	@DebugIt()
+	@only_when_on
+	@fault_on_error
+	def CLK_on(self):
+		"""
+
+		:return:None
+		"""
+		self.function_mapping["CLK_on"]()
+
+	@command()
+	@DebugIt()
+	@only_when_on
+	@fault_on_error
+	def CLK_PLL_setup(self):
+		"""
+
+		:return:None
+		"""
+		self.function_mapping["CLK_PLL_setup"]()
+
 
 # ----------
 # Run server
diff --git a/devices/README.md b/devices/README.md
index 598164453ad74836dbab9d961c9b700c742663ba..37f5db0d5e35aa74f6e26d63524713f40d0e42b6 100644
--- a/devices/README.md
+++ b/devices/README.md
@@ -10,8 +10,7 @@ declare what client the attribute has to use in the initialisation and provide s
 To see how to add support for new clients, see `clients/README.md`
 
 In addition it also provides an abstraction to the tango device, specifically for hardware devices. Examples of hardware devices 
-can be found in SDP.py, PCC.py and test_device.py. as well as a completely empty template in HW_device_implementation.py
-
+can be found in TODO and an empty template can be found in `HW_device_tempalte.py`
 
 Requires numpy 
 ```pip install numpy```
@@ -24,6 +23,4 @@ Requires pytango
 
 ### usage
 You can start the device by calling it in any console with:
-sdp.py instance_name
-PCC.py instance_name
-test_device.py instance_name 
\ No newline at end of file
+<Device_name>.py instance_name
diff --git a/devices/SDP.py b/devices/SDP.py
index ef991709f2c53fddf2aecc9ce6f6f13be2eb574c..2f54bed7bc727323eed635b81880ad4b783f19c0 100644
--- a/devices/SDP.py
+++ b/devices/SDP.py
@@ -73,9 +73,6 @@ class SDP(hardware_device):
 	tr_tod_R = attribute_wrapper(comms_annotation=["1:tr_tod_R"], datatype=numpy.uint64)
 	tr_uptime_R = attribute_wrapper(comms_annotation=["1:tr_uptime_R"], datatype=numpy.uint64)
 
-	def setup_value_dict(self):
-		self.value_dict = {str(i): i.initial_value() for i in self.attr_list()}
-
 	def always_executed_hook(self):
 		"""Method always executed before any TANGO command is executed."""
 		pass
@@ -106,7 +103,7 @@ class SDP(hardware_device):
 		"""Initialises the attributes and properties of the PCC."""
 
 		# set up the OPC ua client
-		self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), self.OPC_Time_Out, self.Standby, self.Fault, self)
+		self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), "http://lofar.eu", self.OPC_Time_Out, self.Standby, self.Fault, self)
 
 		# will contain all the values for this object
 		self.setup_value_dict()
diff --git a/devices/SNMP.py b/devices/SNMP.py
new file mode 100644
index 0000000000000000000000000000000000000000..7f1a7a4e91b841dc818b763dc5c3a04ae5d5ecd2
--- /dev/null
+++ b/devices/SNMP.py
@@ -0,0 +1,119 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of the PCC project
+#
+#
+#
+# Distributed under the terms of the APACHE license.
+# See LICENSE.txt for more info.
+
+""" PCC Device Server for LOFAR2.0
+
+"""
+
+# PyTango imports
+from tango.server import run
+from tango.server import device_property
+# Additional import
+
+from clients.SNMP_client import SNMP_client
+from src.attribute_wrapper import *
+from src.hardware_device import *
+
+
+__all__ = ["PCC", "main"]
+
+class PCC(hardware_device):
+	"""
+
+	**Properties:**
+
+	- Device Property
+		SNMP_community
+			- Type:'DevString'
+		SNMP_host
+			- Type:'DevULong'
+		SNMP_timeout
+			- Type:'DevDouble'
+	"""
+
+	# -----------------
+	# Device Properties
+	# -----------------
+
+	SNMP_community = device_property(
+		dtype='DevString',
+		mandatory=True
+	)
+
+	SNMP_host = device_property(
+		dtype='DevString',
+		mandatory=True
+	)
+
+	SNMP_timeout = device_property(
+		dtype='DevDouble',
+		mandatory=True
+	)
+
+	# ----------
+	# Attributes
+	# ----------
+
+
+	attr0 = attribute_wrapper(comms_annotation={"oids": [""]}, datatype=numpy.bool_, dims=(32,), access=AttrWriteType.READ_WRITE)
+	attr1 = attribute_wrapper(comms_annotation={"oids": ["1.3.6.1.2.1.1.6.0"]}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE)
+
+	attr2 = attribute_wrapper(comms_annotation={"host": "127.0.0.1", "oids": ["1.3.6.1.2.1.1.5.0"]}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE)
+
+	attr3 = attribute_wrapper(comms_annotation={"host": "127.0.0.1", "oids": ["1.3.6.1.2.1.1.5.1", "1.3.6.1.2.1.1.5.2", "1.3.6.1.2.1.1.5.3"]}, dims=(3,), datatype=numpy.bool_)
+	attr4 = attribute_wrapper(comms_annotation={"host": "127.0.0.1", "oids": ["1.3.6.1.2.1.1.5.0"]}, dims=(3,), datatype=numpy.bool_)
+	# ["1.3.6.1.2.1.1.5.0"] gets transformed in to an array the size of dims with ".1", ".2" .. added
+	# ["1.3.6.1.2.1.1.5.0.1", "1.3.6.1.2.1.1.5.0.2", "1.3.6.1.2.1.1.5.0.3"]
+
+	def always_executed_hook(self):
+		"""Method always executed before any TANGO command is executed."""
+		pass
+
+	def delete_device(self):
+		"""Hook to delete resources allocated in init_device.
+
+		This method allows for any memory or other resources allocated in the
+		init_device method to be released.  This method is called by the device
+		destructor and by the device Init command (a Tango built-in).
+		"""
+		self.debug_stream("Shutting down...")
+
+		self.Off()
+		self.debug_stream("Shut down.  Good bye.")
+
+	# --------
+	# overloaded functions
+	# --------
+	def initialise(self):
+		""" user code here. is called when the state is set to STANDBY """
+
+		#set up the SNMP ua client
+		self.snmp_manager = SNMP_client(self.SNMP_community, self.SNMP_host, self.SNMP_timeout, self.Standby, self.Fault, self)
+
+		# map the attributes to the OPC ua comm client
+		for i in self.attr_list():
+			i.set_comm_client(self.OPCua_client)
+
+		self.OPCua_client.start()
+
+	# --------
+	# Commands
+	# --------
+
+# ----------
+# Run server
+# ----------
+def main(args=None, **kwargs):
+	"""Main function of the PCC module."""
+	return run((PCC,), args=args, **kwargs)
+
+
+if __name__ == '__main__':
+	main()
+
diff --git a/devices/clients/SNMP_client.py b/devices/clients/SNMP_client.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e742603e4542c524ad57b41e243886504676ba5
--- /dev/null
+++ b/devices/clients/SNMP_client.py
@@ -0,0 +1,166 @@
+from src.comms_client import *
+import snmp
+
+
+__all__ = ["SNMP_client"]
+
+numpy_to_snmp_dict = {
+	"<class 'numpy.bool_'>": opcua.ua.VariantType.Boolean,
+	"<class 'numpy.int8'>": opcua.ua.VariantType.SByte,
+	"<class 'numpy.uint8'>": opcua.ua.VariantType.Byte,
+	"<class 'numpy.int16'>": opcua.ua.VariantType.Int16,
+	"<class 'numpy.uint16'>": opcua.ua.VariantType.UInt16,
+	"<class 'numpy.int32'>": opcua.ua.VariantType.Int32,
+	"<class 'numpy.uint32'>": opcua.ua.VariantType.UInt32,
+	"<class 'numpy.int64'>": opcua.ua.VariantType.Int64,
+	"<class 'numpy.uint64'>": opcua.ua.VariantType.UInt64,
+	"<class 'numpy.datetime_data'>": opcua.ua.VariantType.DateTime, # is this the right type, does it even matter?
+	"<class 'numpy.float32'>": opcua.ua.VariantType.Float,
+	"<class 'numpy.float64'>": opcua.ua.VariantType.Double,
+	"<class 'numpy.double'>": opcua.ua.VariantType.Double,
+	"<class 'numpy.str_'>": opcua.ua.VariantType.String,
+	"<class 'numpy.str'>": opcua.ua.VariantType.String,
+	"str": opcua.ua.VariantType.String
+}
+
+class SNMP_client(CommClient):
+	"""
+	  messages to keep a check on the connection. On connection failure, reconnects once.
+	"""
+
+	def start(self):
+		super().start()
+
+	def __init__(self, community, host, timeout, on_func, fault_func, streams, try_interval=2):
+		"""
+		Create the SNMP and connect() to it
+		"""
+		super().__init__(on_func, fault_func, streams, try_interval)
+
+		self.community = community
+		self.host = host
+		self.manager = snmp.Manager(community, host, timeout)
+
+		# Explicitly connect
+		if not self.connect():
+			# hardware or infra is down -- needs fixing first
+			fault_func()
+			return
+
+	def connect(self):
+		"""
+		Try to connect to the client
+		"""
+
+		self.streams.debug_stream("Connecting to server %s %s", self.community, self.host)
+		self.connected = True
+		return True
+
+	def disconnect(self):
+		"""
+		disconnect from the client
+		"""
+		self.connected = False  # always force a reconnect, regardless of a successful disconnect
+
+
+	def ping(self):
+		"""
+		ping the client to make sure the connection with the client is still functional.
+		"""
+		pass
+
+	def _setup_annotation(self, annotation):
+		"""
+		This class's Implementation of the get_mapping function. returns the read and write functions
+		"""
+
+		if isinstance(annotation, dict):
+			# check if required path inarg is present
+			if annotation.get('oids') is None:
+				AssertionError("SNMP get attributes require an oid")
+			oids = annotation.get("oids")  # required
+
+			if annotation.get('host') is None:
+				AssertionError("SNMP get attributes require an host")
+			host = annotation.get("host")  # required
+
+		else:
+			TypeError("SNMP attributes require a dict with oid and adress")
+			return
+
+		return host, oids
+
+	def setup_value_conversion(self, attribute):
+		"""
+		gives the client access to the attribute_wrapper object in order to access all data it could potentially need.
+		the OPC ua read/write functions require the dimensionality and the type to be known
+		"""
+
+		dim_x = attribute.dim_x
+		dim_y = attribute.dim_y
+		snmp_type = numpy_to_snmp_dict[str(attribute.numpy_type)]	 # convert the numpy type to a corresponding UA type
+
+		return dim_x, dim_y, snmp_type
+
+	def setup_attribute(self, annotation, attribute):
+		"""
+		MANDATORY function: is used by the attribute wrapper to get read/write functions. must return the read and write functions
+		"""
+
+		# process the annotation
+		host, oids = self._setup_annotation(annotation)
+
+		# get all the necessary data to set up the read/write functions from the attribute_wrapper
+		dim_x, dim_y, snmp_type = self.setup_value_conversion(attribute)
+
+		def _read_function(self):
+			vars = self.manager.get(host, *oids)
+			#TODO convert type
+			#todo
+
+		def _write_function(self, value):
+			self.manager.set(host, oids, value)
+
+		# return the read/write functions
+		return _read_function, _write_function
+
+
+
+class snmp_get:
+	"""
+	This class provides a small wrapper for the OPC ua read/write functions in order to better organise the code
+	"""
+
+	def __init__(self, host, oid, dim_x, dim_y, snmp_type):
+		self.host = host
+		self.oid = oid
+		self.dim_y = dim_y
+		self.dim_x = dim_x
+		self.snmp_type = snmp_type
+
+	def read_function(self):
+		"""
+		Read_R function
+		"""
+		value = numpy.array(self.node.get_value())
+
+		if self.dim_y != 0:
+			value = numpy.array(numpy.split(value, indices_or_sections=self.dim_y))
+		else:
+			value = numpy.array(value)
+		return value
+
+	def write_function(self, value):
+		"""
+		write_RW function
+		"""
+		# set_data_value(opcua.ua.uatypes.Variant(value = value.tolist(), varianttype=opcua.ua.VariantType.Int32))
+
+		if self.dim_y != 0:
+			v = numpy.concatenate(value)
+			self.node.set_data_value(opcua.ua.uatypes.Variant(value=v.tolist(), varianttype=self.ua_type))
+
+		elif self.dim_x != 1:
+			self.node.set_data_value(opcua.ua.uatypes.Variant(value=value.tolist(), varianttype=self.ua_type))
+		else:
+			self.node.set_data_value(opcua.ua.uatypes.Variant(value=value, varianttype=self.ua_type))
diff --git a/devices/clients/opcua_connection.py b/devices/clients/opcua_connection.py
index b8707022f8355e84a286368532b2cffeb162e0ca..56539a2a296501d6b7a7497637570b3597e19246 100644
--- a/devices/clients/opcua_connection.py
+++ b/devices/clients/opcua_connection.py
@@ -3,40 +3,40 @@ from src.comms_client import *
 
 __all__ = ["OPCUAConnection"]
 
-OPCua_to_numpy_dict = {
-	"VariantType.Boolean": numpy.bool_,
-	"VariantType.SByte": numpy.int8,
-	"VariantType.Byte": numpy.uint8,
-	"VariantType.Int16": numpy.int16,
-	"VariantType.UInt16": numpy.uint16,
-	"VariantType.Int32": numpy.int32,
-	"VariantType.UInt32": numpy.uint32,
-	"VariantType.Int64": numpy.int64,
-	"VariantType.UInt64": numpy.uint64,
-	"VariantType.DateTime": numpy.datetime_data, # is this the right type, does it even matter?
-	"VariantType.Float": numpy.float32,
-	"VariantType.Double": numpy.double,
-	"VariantType.String": numpy.str,
-	"VariantType.ByteString": numpy.uint8  # sequence of bytes, not a string
-}
+# OPCua_to_numpy_dict = {
+# 	"VariantType.Boolean": numpy.bool_,
+# 	"VariantType.SByte": numpy.int8,
+# 	"VariantType.Byte": numpy.uint8,
+# 	"VariantType.Int16": numpy.int16,
+# 	"VariantType.UInt16": numpy.uint16,
+# 	"VariantType.Int32": numpy.int32,
+# 	"VariantType.UInt32": numpy.uint32,
+# 	"VariantType.Int64": numpy.int64,
+# 	"VariantType.UInt64": numpy.uint64,
+# 	"VariantType.DateTime": numpy.datetime_data, # is this the right type, does it even matter?
+# 	"VariantType.Float": numpy.float32,
+# 	"VariantType.Double": numpy.double,
+# 	"VariantType.String": numpy.str,
+# 	"VariantType.ByteString": numpy.uint8  # sequence of bytes, not a string
+# }
 
 numpy_to_OPCua_dict = {
-	"<class 'numpy.bool_'>": opcua.ua.VariantType.Boolean,
-	"<class 'numpy.int8'>": opcua.ua.VariantType.SByte,
-	"<class 'numpy.uint8'>": opcua.ua.VariantType.Byte,
-	"<class 'numpy.int16'>": opcua.ua.VariantType.Int16,
-	"<class 'numpy.uint16'>": opcua.ua.VariantType.UInt16,
-	"<class 'numpy.int32'>": opcua.ua.VariantType.Int32,
-	"<class 'numpy.uint32'>": opcua.ua.VariantType.UInt32,
-	"<class 'numpy.int64'>": opcua.ua.VariantType.Int64,
-	"<class 'numpy.uint64'>": opcua.ua.VariantType.UInt64,
-	"<class 'numpy.datetime_data'>": opcua.ua.VariantType.DateTime, # is this the right type, does it even matter?
-	"<class 'numpy.float32'>": opcua.ua.VariantType.Float,
-	"<class 'numpy.float64'>": opcua.ua.VariantType.Double,
-	"<class 'numpy.double'>": opcua.ua.VariantType.Double,
-	"<class 'numpy.str_'>": opcua.ua.VariantType.String,
-	"<class 'numpy.str'>": opcua.ua.VariantType.String,
-	"str": opcua.ua.VariantType.String
+	numpy.bool_: opcua.ua.VariantType.Boolean,
+	numpy.int8: opcua.ua.VariantType.SByte,
+	numpy.uint8: opcua.ua.VariantType.Byte,
+	numpy.int16: opcua.ua.VariantType.Int16,
+	numpy.uint16: opcua.ua.VariantType.UInt16,
+	numpy.int32: opcua.ua.VariantType.Int32,
+	numpy.uint32: opcua.ua.VariantType.UInt32,
+	numpy.int64: opcua.ua.VariantType.Int64,
+	numpy.uint64: opcua.ua.VariantType.UInt64,
+	numpy.datetime_data: opcua.ua.VariantType.DateTime, # is this the right type, does it even matter?
+	numpy.float32: opcua.ua.VariantType.Float,
+	numpy.double: opcua.ua.VariantType.Double,
+	numpy.float64: opcua.ua.VariantType.Double,
+	numpy.str_: opcua.ua.VariantType.String,
+	numpy.str: opcua.ua.VariantType.String,
+	str: opcua.ua.VariantType.String
 }
 
 # <class 'numpy.bool_'>
@@ -50,7 +50,7 @@ class OPCUAConnection(CommClient):
 	def start(self):
 		super().start()
 
-	def __init__(self, address, timeout, on_func, fault_func, streams, try_interval=2):
+	def __init__(self, address, namespace, timeout, on_func, fault_func, streams, try_interval=2):
 		"""
 		Create the OPC ua client and connect() to it and get the object node
 		"""
@@ -64,11 +64,13 @@ class OPCUAConnection(CommClient):
 			fault_func()
 			return
 
-		self.streams.debug_stream("Demo ||\t connection established")
-
 		# determine namespace used
 		try:
-			self.name_space_index = self.client.get_namespace_index("http://lofar.eu")
+			if type(namespace) is str:
+				self.name_space_index = self.client.get_namespace_index(namespace)
+			elif type(namespace) is int:
+				self.name_space_index = namespace
+
 		except Exception as e:
 			self.streams.warn_stream("Cannot determine the OPC-UA name space index.  Will try and use the default = 2.")
 			self.name_space_index = 2
@@ -149,7 +151,7 @@ class OPCUAConnection(CommClient):
 
 		dim_x = attribute.dim_x
 		dim_y = attribute.dim_y
-		ua_type = numpy_to_OPCua_dict[str(attribute.numpy_type)]	 # convert the numpy type to a corresponding UA type
+		ua_type = numpy_to_OPCua_dict[attribute.numpy_type]	 # convert the numpy type to a corresponding UA type
 
 		return dim_x, dim_y, ua_type
 
diff --git a/devices/src/attribute_wrapper.py b/devices/src/attribute_wrapper.py
index f003df926801b207c7538fd52593a19d676c609f..1ea654f92f68c00080c3c31059a381411939580f 100644
--- a/devices/src/attribute_wrapper.py
+++ b/devices/src/attribute_wrapper.py
@@ -6,24 +6,13 @@ import numpy
 from src.wrappers import only_when_on, fault_on_error
 
 
-def swap_dims_tuple(dims):
-	"""
-	arrays are inconsistent between tango and numpy.
-	This function exists to swap around the tuple containing the dimension data
-	"""
-	if len(dims) == 2:
-		return tuple((dims[1], dims[0]))
-	else:
-		return dims
-
-
 
 class attribute_wrapper(attribute):
 	"""
 		Wraps all the attributes in a wrapper class to manage most of the redundant code behind the scenes
 	"""
 
-	def __init__(self, comms_annotation=None, datatype=None, dims=(1,), **kwargs):
+	def __init__(self, comms_annotation=None, datatype=None, dims=(1,), access=AttrWriteType.READ, init_value=None, **kwargs):
 		"""
 		wraps around the tango Attribute class. Provides an easier interface for 1d or 2d arrays. Also provides a way to abstract
 		managing the communications interface.
@@ -38,10 +27,7 @@ class attribute_wrapper(attribute):
 		self.comms_annotation = comms_annotation # store data that can be used by the comms interface. not used by the wrapper itself
 		self.numpy_type = datatype # tango changes our attribute to their representation (E.g numpy.int64 becomes "DevLong64")
 
-		wrap_RW = kwargs.get("access", AttrWriteType.READ)
-
-		self.init_value = kwargs.get("init_value", None) 	# if not None, gets used as default value
-
+		self.init_value = init_value
 		max_dim_y = 0
 
 		# tango doesn't recognise numpy.str_, for consistencies sake we convert it here and hide this from the top level
@@ -52,10 +38,6 @@ class attribute_wrapper(attribute):
 		# check if not scalar
 		if isinstance(dims, tuple):
 
-			# fill the array with initial values
-			# self.value = numpy.zeros(swap_dims_tuple(dims), dtype=datatype)
-			# self.value = numpy.full(swap_dims_tuple(dims), datatype(0), dtype=data_type)
-
 			# get first dimension
 			max_dim_x = dims[0]
 
@@ -72,7 +54,7 @@ class attribute_wrapper(attribute):
 			max_dim_x = 1
 
 
-		if wrap_RW == AttrWriteType.READ_WRITE:
+		if access == AttrWriteType.READ_WRITE:
 			""" if the attribute is of READ_WRITE type, assign the RW and write function to it"""
 
 			@only_when_on
@@ -83,7 +65,7 @@ class attribute_wrapper(attribute):
 				read_RW returns the value that was last written to the attribute
 				"""
 				try:
-					return device.value_dict[str(self)]
+					return device.value_dict[self]
 				except:
 					print()
 
@@ -94,7 +76,7 @@ class attribute_wrapper(attribute):
 				_write_RW writes a value to this attribute
 				"""
 				self.write_function(value)
-				device.value_dict[str(self)] = value
+				device.value_dict[self] = value
 
 			self.fget = read_RW
 			self.fset = write_RW
@@ -109,12 +91,12 @@ class attribute_wrapper(attribute):
 				"""
 				_read_R reads the attribute value, stores it and returns it"
 				"""
-				device.value_dict[str(self)] = self.read_function()
-				return device.value_dict[str(self)]
+				device.value_dict[self] = self.read_function()
+				return device.value_dict[self]
 
 			self.fget = read_R
 
-		super().__init__(dtype=datatype, max_dim_y=max_dim_y, max_dim_x=max_dim_x, **kwargs)
+		super().__init__(dtype=datatype, max_dim_y=max_dim_y, max_dim_x=max_dim_x, access=access, **kwargs)
 
 		return
 
@@ -131,7 +113,12 @@ class attribute_wrapper(attribute):
 			dims = (self.dim_x,)
 
 		# x and y are swapped for numpy and Tango. to maintain tango conventions, x and y are swapped for numpy
-		value = numpy.zeros(swap_dims_tuple(dims), dtype=self.numpy_type)
+		if len(dims) == 2:
+			numpy_dims = tuple((dims[1], dims[0]))
+		else:
+			numpy_dims = dims
+
+		value = numpy.zeros(numpy_dims, dtype=self.numpy_type)
 		return value
 
 	def set_comm_client(self, client):
diff --git a/devices/src/hardware_device.py b/devices/src/hardware_device.py
index 68afa9b82bfb28bcb30d972f9519b96007f56c56..0b9d488fb5f9df78d0768923c0b360c034b06acc 100644
--- a/devices/src/hardware_device.py
+++ b/devices/src/hardware_device.py
@@ -54,6 +54,11 @@ class hardware_device(Device):
 		""" Return a list of all the attribute_wrapper members of this class. """
 		return [v for k, v in cls.__dict__.items() if type(v) == attribute_wrapper]
 
+	def setup_value_dict(self):
+		""" set the initial value for all the attribute wrapper objects"""
+
+		self.value_dict = {i: i.initial_value() for i in self.attr_list()}
+
 	def init_device(self):
 		""" Instantiates the device in the OFF state. """
 
@@ -76,12 +81,10 @@ class hardware_device(Device):
 		:return:None
 		"""
 		self.set_state(DevState.INIT)
-		self.initialise()
+		self.setup_value_dict()
 
-		# if self.get_state() == DevState.STANDBY:
-		# 	# Already STANDBY. Don't complain.
-		# 	return
-		# self.set_state(DevState.STANDBY)
+
+		self.initialise()
 
 	@only_in_states([DevState.INIT])
 	def Standby(self):
diff --git a/devices/test_device.py b/devices/test_device.py
index e38ff5b9a39646890a2dbe31767a7ec0f178ab5a..27bdbfcb079e5019be6826ad8e7d3354ac296722 100644
--- a/devices/test_device.py
+++ b/devices/test_device.py
@@ -59,14 +59,6 @@ class test_device(hardware_device):
 	float32_image_R = attribute_wrapper(comms_annotation="numpy.float32 type read image (dims = 8x2)", datatype=numpy.float32, dims=(8, 2))
 	uint8_image_RW = attribute_wrapper(comms_annotation="numpy.uint8 type read/write image (dims = 2x8)", datatype=numpy.uint8, dims=(2, 8), access=AttrWriteType.READ_WRITE)
 
-	tr_tod_R = attribute_wrapper(comms_annotation=["1:tr_tod_R"], datatype=numpy.uint64)
-	tr_uptime_R = attribute_wrapper(comms_annotation=["1:tr_uptime_R"], datatype=numpy.uint64)
-
-	def setup_value_dict(self):
-		""" set the initial value for all the attribute wrapper objects"""
-
-		self.value_dict = {str(i): i.initial_value() for i in self.attr_list()}
-
 	# --------
 	# overloaded functions
 	# --------
@@ -80,10 +72,6 @@ class test_device(hardware_device):
 		#set up the OPC ua client
 		self.example_client = example_client(self.Standby, self.Fault, self)
 
-
-		# NOTE: MANDATORY will contain all attribute values for this tango device instance
-		self.setup_value_dict()
-
 		# map an access helper class
 		for i in self.attr_list():
 			i.set_comm_client(self.example_client)