diff --git a/src/energenie/OpenThings.py b/src/energenie/OpenThings.py
index 08a64da..08acf97 100644
--- a/src/energenie/OpenThings.py
+++ b/src/energenie/OpenThings.py
@@ -650,14 +650,17 @@
 			"mfrid" :    None,
 			"productid": None,
 			"sensorid":  None
-		}
+		},
+		"recs":[]
 	}
 
-	def __init__(self, pydict=None):
+	def __init__(self, pydict=None, **kwargs):
 		if pydict == None:
 			pydict = copy.deepcopy(Message.BLANK)
 		self.pydict = pydict
 
+		self.set(**kwargs)
+
 	@untested
 	def __getitem__(self, key):
 		try:
@@ -682,6 +685,79 @@
 		import copy
 		return Message(copy.deepcopy(self.pydict))
 
+	def set(self, **kwargs):
+		"""Set fields in the message from key value pairs"""
+		for key in kwargs:
+			value = kwargs[key]
+			pathed_key = key.split('_')
+			m = self.pydict
+			# walk to the parent
+			for pkey in pathed_key[:-1]:
+				# If the key parseable as an integer, use as a list index instead
+				try:
+					pkey = int(pkey)
+				except:
+					pass
+				m = m[pkey]
+			# set the parent to have a key that points to the new value
+			pkey = pathed_key[-1]
+			# If the key parseable as an integer, use as a list index instead
+			try:
+				pkey = int(pkey)
+			except:
+				pass
+
+			# if index exists, change it, else create it
+			try:
+				m[pkey] = value
+			except IndexError:
+				# expand recs up to pkey
+				l = len(m) # length of list
+				gap = (l - pkey)+1
+				for i in range(gap):
+					m.append({})
+				m[pkey] = value
+
+	def append_rec(self, *args, **kwargs):
+		"""Add a rec"""
+		if type(args[0]) == dict:
+			# This is ({})
+			self.pydict["recs"].append(args[0])
+			return len(self.pydict["recs"])-1 # index of rec just added
+
+		elif type(args[0]) == int:
+			if len(kwargs) == 0:
+				# This is (PARAM_x, pydict)
+				paramid = args[0]
+				pydict  = args[1]
+				pydict["paramid"] = paramid
+				return self.append_rec(pydict)
+			else:
+				# This is (PARAM_x, key1=value1, key2=value2)
+				paramid = args[0]
+				# build a pydict
+				pydict = {"paramid":paramid}
+				for key in kwargs:
+					value = kwargs[key]
+					pydict[key] = value
+				self.append_rec(pydict)
+
+		else:
+			raise ValueError("Not sure how to parse arguments to append_rec")
+
+	def get(self, keypath):
+		"""READ(GET) from a single keypathed entry"""
+		path = keypath.split("_")
+		m = self.pydict
+		# walk to the final item
+		for pkey in path:
+			try:
+				pkey = int(pkey)
+			except:
+				pass
+			m = m[pkey]
+		return m
+
 	def __str__(self): # -> str
 		return "Message.STR"
 
@@ -699,18 +775,17 @@
 
 		# HEADER
 		if "header" in msg:
-			header    = msg["header"]
-			mfrid     = header["mfrid"]
-			if mfrid == None: mfrid = ""
-			else:             mfrid = str(hex(mfrid))
+			header = msg["header"]
 
-			productid = header["productid"]
-			if productid == None: productid = ""
-			else:                 productid = str(hex(productid))
+			def gethex(key):
+				if key in header:
+					value = header[key]
+					if value != None: return str(hex(value))
+				return ""
 
-			sensorid  = header["sensorid"]
-			if sensorid == None: sensorid = ""
-			else:                sensorid = str(hex(sensorid))
+			mfrid     = gethex("mfrid")
+			productid = gethex("productid")
+			sensorid  = gethex("sensorid")
 
 			print("mfrid:%s prodid:%s sensorid:%s" % (mfrid, productid, sensorid))
 
@@ -724,19 +799,19 @@
 					write = "read "
 
 				try:
-					paramname = rec["paramname"] # This only come out from decoded messages
+					paramname = rec["paramname"] #NOTE: This only comes out from decoded messages
 				except:
 					paramname = ""
 
 				try:
-					paramid = rec["paramid"] #This is only present on a input message (e.g SWITCH)
+					paramid = rec["paramid"] #NOTE: This is only present on a input message (e.g SWITCH)
 					paramname = paramid_to_paramname(paramid)
 					paramid = str(hex(paramid))
 				except:
 					paramid = ""
 
 				try:
-					paramunit = rec["paramunit"] # This only come out from decoded messages
+					paramunit = rec["paramunit"] #NOTE: This only comes out from decoded messages
 				except:
 					paramunit = ""
 
@@ -748,7 +823,6 @@
 				print("%s %s %s %s = %s" % (write, paramid, paramname, paramunit, str(value)))
 
 
-
 @deprecated
 def showMessage(msg, timestamp=None):
 	"""Show the message in a friendly format"""
diff --git a/src/energenie/OpenThings_test.py b/src/energenie/OpenThings_test.py
index 05153d6..76f1d5d 100644
--- a/src/energenie/OpenThings_test.py
+++ b/src/energenie/OpenThings_test.py
@@ -154,83 +154,125 @@
 
 class TestMessage(unittest.TestCase):
 
-	def test_blank(self):
-		# create a blank message
+	def XXXtest_blank(self):
+		"""CREATE a completely blank message"""
 		msg = Message()
+		print(msg.pydict)
 		msg.dump()
 
-	def test_blank_create_dict(self): #1 {pydict}      (CREATE)
-		msg = Message({"header":{}, "recs":[{"parmid":PARAM_SWITCH_STATE, "value":1}]})
+	def XXXtest_blank_template(self):
+		"""CREATE a message from a simple pydict template"""
+		# This is useful to simplify all other tests
+		msg = Message(Message.BLANK)
+		print(msg.pydict)
 		msg.dump()
 
-	def XXXtest_add_rec_dict(self): #1 {pydict}        (ADD)
-		# add rec fields from a dict parameter
-		msg = Message()
-		msg.add_rec({"paramid":PARAM_SWITCH_STATE, "value":1})
+	def XXXtest_blank_create_dict(self): #1 {pydict}
+		"""CREATE a blank message with a dict parameter"""
+		msg = Message({"header":{}, "recs":[{"wr":False, "parmid":PARAM_SWITCH_STATE, "value":1}]})
+		print(msg.pydict)
+		msg.dump()
 
-	def XXXtest_create_template(self): #1 {pydict}     (CREATE)
+	def XXXtest_blank_create_header_dict(self): #2 header={pydict}
+		"""CREATE a blank message and add a header at creation time from a dict"""
+		msg = Message(header={"mfrid":123, "productid":456, "sensorid":789})
+		print(msg.pydict)
+		msg.dump()
+
+	def XXXtest_create_big_template(self): #1 {pydict}
+		"""CREATE from a large template message"""
 		# create a message from a template
 		msg = Message(Devices.MIHO005_REPORT)
+		print(msg.pydict)
 		msg.dump()
 
-
-	def XXXtest_blank_create_header_dict(self): #2 header={pydict}     (CREATE)
-		# create a blank message and add a header at creation from a dict
-		msg = Message(header={"mfrid":123, "productid":456, "sensorid":789})
+	def XXXtest_add_rec_dict(self): #1 {pydict}
+		"""UPDATE(APPEND) rec fields from a dict parameter"""
+		msg = Message(Message.BLANK)
+		i = msg.append_rec({"paramid":PARAM_SWITCH_STATE, "wr":True, "value":1})
+		print("added index:%d" % i)
+		print(msg.pydict)
 		msg.dump()
 
-	def XXXtest_add_header_dict(self): #2 header={pydict}    (ADD)
+	def XXXtest_add_header_dict(self): #2 header={pydict}
+		"""UPDATE(SET) a new header to an existing message"""
 		msg = Message()
-		msg.add(header={"mfrid":123, "productid":456, "sensorid":789})
+		msg.set(header={"mfrid":123, "productid":456, "sensorid":789})
+		print(msg.pydict)
 		msg.dump()
 
+	def XXXtest_add_recs_dict(self):
+		"""UPDATE(SET) recs to an existing message"""
+		msg = Message()
+		msg.set(recs=[{"paramid":PARAM_SWITCH_STATE, "wr":True, "value":1}])
+		print(msg.pydict)
+		msg.dump()
 
-	def XXXtest_alter_rec_template(self): #3 header_mfrid=123    (CHANGE)
-		# alter rec fields in a template
+	def XXXtest_add_path(self):
+		"""UPDATE(SET) a pathed key to an existing message"""
+		msg = Message()
+		msg.set(header_productid=1234)
+		print(msg.pydict)
+		msg.dump()
+
+	def XXXtest_alter_template(self): #3 header_mfrid=123
+		"""UPDATE(SET) an existing key with a path"""
 		msg = Message(Devices.MIHO005_REPORT)
-		msg.alter(header_productid=123)
+		msg.set(header_productid=123)
+		msg.dump()
+
+	def XXXtest_alter_template_multiple(self):
+		"""UPDATE(SET) multiple keys with paths"""
+		msg = Message(Devices.MIHO005_REPORT)
+		msg.set(header_productid=123, header_sensorid=99)
+		print(msg.pydict)
 		msg.dump()
 
 	def XXXtest_blank_create_header_paths(self): #3 header_mfrid=123    (CREATE)
-		# create a blank message and add some header fields at creation time
+		"""CREATE message with pathed keys in constructor"""
 		msg = Message(header_mfrid=123, header_productid=456, header_sensorid=789)
+		print(msg.pydict)
 		msg.dump()
 
-	def XXXtest_add_header_path(self): #3 header_mfrid=123    (ADD)
-		# add header fields to a message after creation via pathed keys
+	def XXXtest_blank_create_recs_paths(self):
+		"""CREATE message with pathed keys in constructor"""
+		# uses integer path component to mean array index
+		msg = Message(recs_0={"paramid":PARAM_SWITCH_STATE, "wr":True, "value":1},
+					  recs_1={"paramid":PARAM_AIR_PRESSURE, "wr":True, "value":2})
+		print(msg.pydict)
+		msg.dump()
+
+	def XXXtest_add_rec_path(self): #5 recs_0_paramid=PARAM_SWITCH_STATE
+		"""UPDATE(SET) records in a message"""
+		msg = Message(recs_0={}) # must create rec before you can change it
+		print(msg.pydict)
+		msg.set(recs_0_paramid=PARAM_SWITCH_STATE, recs_0_value=1, recs_0_wr=True)
+		print(msg.pydict)
+		msg.dump()
+
+	def XXXtest_add_rec_fn_pydict(self): #6 SWITCH_STATE={pydict}
+		"""UPDATE(ADD) a rec to message using PARAM constants as keys"""
+		#always creates a new rec at the end and then populates
 		msg = Message()
-		msg.add(header_mfrid=123, header_productid=456)
+		msg.append_rec(PARAM_SWITCH_STATE, {"wr": True, "value":1})
+		print(msg.pydict)
 		msg.dump()
 
-	###4 recs_0={pydict}
-
-	def XXXtest_add_rec_path(self): #5 recs_0_paramid=PARAM_SWITCH_STATE    (ADD)
-		# add rec fields to a message after creation via pathed indexed keys
+	def XXXtest_add_rec_fn_keyed(self): #7 SWITCH_STATE,value=1    (ADD)
+		"""UPDATE(ADD) a rec to message using PARAM const and keyed values"""
 		msg = Message()
-		msg.add(recs_0_paramid=PARAM_SWITCH_STATE, recs_0_value=1)
+		msg.append_rec(PARAM_SWITCH_STATE, wr=True, value=1)
+		print(msg.pydict)
 		msg.dump()
 
-
-	def XXXtest_add_rec_fn_pydict(self): #6 SWITCH_STATE={pydict}    (ADD)
-		# add rec fields to a message after creation via pathed PARAM name keys
-		msg = Message()
-		msg.add_rec(PARAM_SWITCH_STATE, {"value":1})
-		msg.dump()
-
-
-	def XXXtest_add_rec_fn(self): #7 SWITCH_STATE,value=1    (ADD)
-		# add rec fields to a message after creation via pathed PARAM name keys
-		msg = Message()
-		msg.add_rec(PARAM_SWITCH_STATE, value=1)
-		msg.dump()
-
-
-	def XXXtest_alter_rec_template_paramname(self): #8 SWITCH_STATE_value=1    (CHANGE)
-		# alter rec fields in a template
+	def XXXtest_get_pathed(self):
+		"""READ from the message with a path key"""
 		msg = Message(Devices.MIHO005_REPORT)
-		msg.alter(recs_SWITCH_STATE_value=1)
-		msg.dump()
+		print(msg.get("header_mfrid"))
 
+	#----- HERE -----
+
+	#### This is the attribute set/get abstraction
 
 	def XXXtest_pydict_read(self): #9 msg["header"]  msg["recs"][0]    (READ)
 		## access a specific keyed entry like a normal pydict, for read
@@ -256,6 +298,9 @@
 		msg["recs"][0] = {"paramid": PARAM_SWITCH_STATE, "value": 1}
 		msg.dump()
 
+
+	#### This is the PARAMID indexer
+
 	def XXXtest_paramid_read_struct(self): #10 msg[PARAM_SWITCH_STATE]    (READ)
 		# access a paramid entry for read of the whole structure
 		msg = Message(Devices.MIHO005_REPORT)
@@ -273,6 +318,18 @@
 		msg.dump()
 
 
+
+	####TODO: This is where we need an intelligent key parser
+
+	def XXXtest_alter_rec_template_paramname(self): #8 SWITCH_STATE_value=1    (CHANGE)
+		# alter rec fields in a template
+		msg = Message(Devices.MIHO005_REPORT)
+		msg.set(recs_SWITCH_STATE_value=1)
+		msg.dump()
+
+
+	####TODO: This is where dump() might need to dump to a strbuf and then output
+	#some of these might just print the inner pydict though
 	def XXXtest_repr(self):
 		## dump a message in printable format
 		msg = Message(Devices.MIHO005_REPORT)