diff --git a/devices/HW_device_template.py b/devices/HW_device_template.py
index 950faf39daf790962ef1f4cc3b93fb00382533fb..0249747de12fa08893d0c1c4c3534827b61f59f1 100644
--- a/devices/HW_device_template.py
+++ b/devices/HW_device_template.py
@@ -1,13 +1,11 @@
 # -*- coding: utf-8 -*-
 #
-# This file is part of the PCC project
-#
-#
+# This file wraps around a tango device class and provides a number of abstractions useful for hardware devices. It works together
 #
 # Distributed under the terms of the APACHE license.
 # See LICENSE.txt for more info.
 
-""" Hardware Device Server for LOFAR2.0
+"""
 
 """
 
@@ -15,6 +13,7 @@
 from tango.server import run
 # Additional import
 
+from src.attribute_wrapper import *
 from src.hardware_device import *
 
 __all__ = ["HW_dev"]
@@ -22,19 +21,19 @@ __all__ = ["HW_dev"]
 
 class HW_dev(hardware_device):
     """
-	This class is the minimal (read empty) implementation of a class using 'hardware_device'
-	"""
+    This class is the minimal (read empty) implementation of a class using 'hardware_device'
+    """
 
     # ----------
     # Attributes
     # ----------
     """
-	attribute wrapper objects can be declared here. All attribute wrapper objects will get automatically put in a ist (attr_list) for easy access
+    attribute wrapper objects can be declared here. All attribute wrapper objects will get automatically put in a list (attr_list) for easy access
 
-	example = attribute_wrapper(comms_annotation="this is an example", datatype=numpy.double, dims=(8, 2), access=AttrWriteType.READ_WRITE)
-	...
+    example = attribute_wrapper(comms_annotation="this is an example", datatype=numpy.double, dims=(8, 2), access=AttrWriteType.READ_WRITE)
+    ...
 
-	"""
+    """
 
     def always_executed_hook(self):
         """Method always executed before any TANGO command is executed."""
@@ -43,10 +42,10 @@ class HW_dev(hardware_device):
     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).
-		"""
+        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()
diff --git a/devices/PCC.py b/devices/PCC.py
index 9e605cac2f33983c12d70f2a3d41fb6b2b9cd00f..d6b255335f50e75b49ff8d2898ed59f085d86246 100644
--- a/devices/PCC.py
+++ b/devices/PCC.py
@@ -112,7 +112,6 @@ class PCC(hardware_device):
     # --------
     def off(self):
         """ user code here. is called when the state is set to OFF """
-
         # Stop keep-alive
         self.OPCua_client.stop()
 
diff --git a/devices/SNMP.py b/devices/SNMP.py
new file mode 100644
index 0000000000000000000000000000000000000000..5af5d75a49eb96396c9ab35cc0b1ddaff5f271e1
--- /dev/null
+++ b/devices/SNMP.py
@@ -0,0 +1,124 @@
+# -*- 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.
+
+""" SNMP Device 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__ = ["SNMP", "main"]
+
+
+class SNMP(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
+    # ----------
+
+    sys_description_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.1.0"}, datatype=numpy.str_)
+    sys_objectID_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.2.0", "type": "OID"}, datatype=numpy.int64)
+    sys_uptime_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.3.0", "type": "TimeTicks"}, datatype=numpy.str_)
+    sys_name_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.5.0"}, datatype=numpy.str_)
+    ip_route_mask_127_0_0_1_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.4.21.1.11.127.0.0.1", "type": "IpAddress"}, datatype=numpy.str_)
+    TCP_active_open_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.6.5.0", "type": "Counter32"}, datatype=numpy.str_)
+
+    sys_contact_RW = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.4.0"}, datatype=numpy.str_, access= AttrWriteType.READ_WRITE)
+    sys_contact_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.4.0"}, datatype=numpy.str_)
+
+    TCP_Curr_estab_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.6.9.0", "type": "Gauge"}, datatype=numpy.int64)
+
+    # inferred spectrum
+    if_index_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.2.2.1.1"}, dims=(10,), datatype=numpy.int64)
+
+    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.Fault, self)
+
+        # map the attributes to the OPC ua comm client
+        for i in self.attr_list():
+            i.set_comm_client(self.snmp_manager)
+
+        self.snmp_manager.start()
+
+
+# --------
+# Commands
+# --------
+
+
+# ----------
+# Run server
+# ----------
+def main(args=None, **kwargs):
+    """Main function of the PCC module."""
+    return run((SNMP,), 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..fe6d20dc6874267268d5c4f7e0cceae0fa109c02
--- /dev/null
+++ b/devices/clients/SNMP_client.py
@@ -0,0 +1,156 @@
+from src.comms_client import *
+import snmp
+import numpy
+import traceback
+
+__all__ = ["SNMP_client"]
+
+
+snmp_to_numpy_dict = {
+    snmp.types.INTEGER: numpy.int64,
+    snmp.types.TimeTicks: numpy.int64,
+    snmp.types.OCTET_STRING: str,
+    snmp.types.OID: str
+}
+
+snmp_types = {
+    "Integer": numpy.int64,
+    "Gauge": numpy.int64,
+    "TimeTick": numpy.int64,
+    "Counter32": numpy.int64,
+    "OctetString": str,
+    "IpAddress": str,
+    "OID": str,
+}
+
+
+# numpy_to_snmp_dict = {
+#     numpy.int64,
+#     numpy.int64,
+#     str,
+# }
+
+
+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, fault_func, streams, try_interval=2):
+        """
+        Create the SNMP and connect() to it
+        """
+        super().__init__(fault_func, streams, try_interval)
+
+        self.community = community
+        self.host = host
+        self.manager = snmp.Manager(community=bytes(community, "utf8"))
+
+        # 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 community: %s, host: %s", self.community, self.host)
+
+        self.connected = True
+        return True
+
+    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
+        else:
+            TypeError("SNMP attributes require a dict with oid(s)")
+            return
+
+        return 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.
+        """
+
+        dim_x = attribute.dim_x
+        dim_y = attribute.dim_y
+
+        return dim_x, dim_y
+
+    def get_oids(self, x, y, in_oid):
+
+        if x == 0:
+            x = 1
+        if y == 0:
+            y = 1
+
+        nof_oids = x * y
+
+        if nof_oids == 1:
+            # is scalar
+            if type(in_oid) is str:
+                # for ease of handling put single oid in a 1 element list
+                in_oid = [in_oid]
+            return in_oid
+
+        elif type(in_oid) is list and len(in_oid) == nof_oids:
+            # already is an array and of the right length
+            return in_oid
+        elif type(in_oid) is list and len(in_oid) != nof_oids:
+            # already is an array but the wrong length. Unable to handle this
+            raise ValueError("SNMP oids need to either be a single value or an array the size of the attribute dimensions. got: {} expected: {}x{}={}".format(len(in_oid),x,y,x*y))
+        else:
+            out_oids = []
+
+            for i in range(nof_oids):
+                out_oids.append(in_oid + ".{}".format(i+1))
+
+            return out_oids
+
+    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
+        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 = self.setup_value_conversion(attribute)
+        oids = self.get_oids(dim_x, dim_y, oids)
+
+        def _read_function():
+            vars = self.manager.get(self.host, *oids)
+
+            value = []
+            for i in vars:
+                val = snmp_to_numpy_dict[type(i.value)](str(i.value))
+                value.append(val)
+            return value
+
+        def _write_function(value):
+            for i in range(len(oids)):
+                    self.manager.set(self.host, oids[i], value[i])
+
+
+        # return the read/write functions
+        return _read_function, _write_function
diff --git a/devices/src/attribute_wrapper.py b/devices/src/attribute_wrapper.py
index 35670b0705e75668e628283198daba26107e78a2..03bd9d5e5148b8539e339d57328d2f53c6262d70 100644
--- a/devices/src/attribute_wrapper.py
+++ b/devices/src/attribute_wrapper.py
@@ -11,17 +11,22 @@ logger = logging.getLogger()
 
 class attribute_wrapper(attribute):
     """
-		Wraps all the attributes in a wrapper class to manage most of the redundant code behind the scenes
-	"""
+    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,), 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.
-		"""
+        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.
+
+        comms_annotation: data passed along to the attribute. can be given any form of data. handling is up to client implementation
+        datatype: any numpy datatype
+        dims: dimensions of the
+        init_value: value
+        """
 
         # ensure the type is a numpy array
-        if "numpy" not in str(datatype) and type(datatype) != str:
+        if "numpy" not in str(datatype) and datatype != str:
             raise TypeError("Attribute needs to be a Tango-supported numpy or str type, but has type \"%s\"" % (datatype,))
 
         self.comms_annotation = comms_annotation  # store data that can be used by the comms interface. not used by the wrapper itself