コード例 #1
0
    def set_field_value(self, attrib, value):
        """
		Set the value of an attribute to the value provided, setting the change flag if different.
		"""

        try:
            thisUpdated = False

            if attrib in dir(self):
                if getattr(self, attrib) != value:
                    self.updated = True
                    thisUpdated = True
                setattr(self, attrib, value)

                if self.debug and thisUpdated:
                    self.logger.info(
                        "\nDEBUG MODE: CHANGED FIELD {} TO {}\n{}".format(
                            attrib, value, unicode(self)))

                return True

        except Exception as e:
            self.logger.error(ex.stack_trace(e))

        return False
コード例 #2
0
def generic_class_to_dict(obj):
    """
	Using the same exclusions as generic_unicode_output, convert class data to a dictionary object.
	"""

    try:
        data = {}

        for a in dir(obj):
            if callable(getattr(obj, a)): continue
            if a.startswith("_"): continue
            if a == "factory": continue
            if a == "logger": continue
            if a == "tabtitle" or a == "tabcontent": continue

            if type(getattr(obj, a)) == list:
                listitem = []

                for l in getattr(obj, a):
                    if "'instance'" in unicode(type(l)):
                        listitem.append(generic_class_to_dict(l))
                    else:
                        listitem.append(l)

                data[a] = listitem

            else:
                data[a] = getattr(obj, a)

    except Exception as e:
        e.args += (unicode(type(obj)), )
        raise CalcsException(kExceptionOutputPrefix + ex.stack_trace(e))

    return data
コード例 #3
0
    def build_dict(self):
        try:
            rec = {}

            rec["name"] = self.name
            rec["index"] = self.index

            for a in dir(self):
                if a == "debug": continue
                if a == "name": continue
                if a == "index": continue
                if a == "logger": continue
                if a.startswith("_"): continue
                if a == "set_field_value": continue
                if a == "build_dict": continue

                value = getattr(self, a)

                if unicode(type(value)) == "<class 'indigo.List'>":
                    # Build non Indigo list so it can JSON encode
                    newlist = []
                    for i in value:
                        newlist.append(i)

                    rec[a] = newlist
                else:
                    rec[a] = value

        except Exception as e:
            self.logger.error(ex.stack_trace(e))

        if self.debug:
            self.logger.info("\nDEBUG MODE: JSON DATA\n{}".format(
                unicode(rec)))
        return rec
コード例 #4
0
    def __init__(self, recordDef, valuesDict):
        try:
            self.logger = logging.getLogger("Plugin.jstuff_record")

            self.name = recordDef.name
            self.index = valuesDict[recordDef.indexField]
            self.updated = False
            self.debug = False
            self.indexReset = False

            self.debug = recordDef.debug
            self.indexReset = recordDef.indexReset

            for a in recordDef.include:
                setattr(
                    self, a,
                    valuesDict[a])  # Set an attribute matching the field value

            for a in recordDef.manual:
                setattr(
                    self, a,
                    valuesDict[a])  # Set an attribute matching the field value

        except Exception as e:
            self.logger.error(ex.stack_trace(e))
コード例 #5
0
def generic_unicode_output(tabtitle, tabcontent, obj, title=None):
    """
	Generic unicode output for custom classes (called by __str__ functions).
	"""

    try:
        ret = ""

        if title:
            ret += u"{}{} : {}\n".format(tabtitle, title,
                                         type_to_unicode_output(obj))

        for a in dir(obj):
            if callable(getattr(obj, a)): continue
            if a.startswith("_"): continue
            if a == "factory": continue
            if a == "logger": continue
            if a == "tabtitle" or a == "tabcontent": continue

            if type(getattr(obj, a)) == list:
                ret += u"{}{} : (list) \n".format(tabcontent, a)

                for l in getattr(obj, a):
                    ret += u"\t{}item :\n{}{}".format(tabcontent, tabcontent,
                                                      l)
            else:
                ret += u"{}{} : {}\n".format(
                    tabcontent, a, type_to_unicode_output(getattr(obj, a)))

    except Exception as e:
        e.args += (unicode(type(obj)), )
        raise CalcsException(kExceptionOutputPrefix + ex.stack_trace(e))

    return ret
コード例 #6
0
    def __init__(self, factory):
        try:
            self.factory = factory  # References the Indigo plugin

            self.logger = logging.getLogger("Plugin.jstuff")

            self.logger.debug("{} {} loaded".format(__modname__, __version__))

        except Exception as e:
            self.logger.error(ex.stack_trace(e))
コード例 #7
0
    def __init__(self, args, valuesDict, typeId, targetId):
        """
		Create the record using arguments passed from the fieldStuffIndex function in JStuff.
		"""

        try:
            self.logger = logging.getLogger("Plugin.jstuff_record_definition")

            self.name = ""
            self.include = []
            self.exclude = []
            self.manual = []
            self.indexField = ""
            self.debug = False
            self.indexReset = False

            if "name" in args: self.name = args["name"]
            if "indexfield" in args: self.indexField = args["indexfield"]

            if "exclude" in args:
                self.exclude = self.process_include_exclude(
                    args["exclude"], valuesDict, self.indexField)
            if "include" in args:
                self.include = self.process_include_exclude(
                    args["include"], valuesDict, self.indexField)
            if "manual" in args:
                self.manual = self.process_include_exclude(
                    args["manual"], valuesDict, self.indexField)
            if "indexchangereset" in args and args["indexchangereset"].lower(
            ) == "true":
                self.indexReset = True

            # Populate the remaining fields if we got only excludes
            if not self.include:
                for field, value in valuesDict.iteritems():
                    if field.startswith("ipf") or field == self.indexField:
                        continue  # Don't include these
                    if not field in self.exclude:
                        self.include.append(field)

            # Populate the remaining fields if we got only excludes
            if not self.exclude:
                for field, value in valuesDict.iteritems():
                    if field.startswith("ipf") or field == self.indexField:
                        continue  # Don't include these
                    if not field in self.include:
                        self.exclude.append(field)

            if "debug" in args and args["debug"].lower() == "true":
                self.debug = True
                self.logger.info(unicode(self))

        except Exception as e:
            self.logger.error(ex.stack_trace(e))
コード例 #8
0
ファイル: ui.py プロジェクト: paulforrester/HomeKit-Bridge
    def __init__(self, factory):
        try:
            self.factory = factory  # References the Indigo plugin
            self.logger = logging.getLogger("Plugin.ui")
            self.logger.debug("{} {} loaded".format(__modname__, __version__))

            self.deviceFieldCache = {
            }  # For retrieving defaults and knowing if a field changed

        except Exception as e:
            self.logger.error(ex.stack_trace(e))
コード例 #9
0
ファイル: ui.py プロジェクト: paulforrester/HomeKit-Bridge
    def formFieldChanged(self, valuesDict, typeId, devId, setDefault):
        """
		Called from the plugin whenever any form field is changed, then attempts to raise an event in the plugin or ifactory and returns the result.
		
		Arguments:
		valuesDict = form fields
		typeId = device type Id
		devId = device Id
		setDefault = read last list retrieved for this field and default to the first value if the field is blank or its value doesn't exist on the list
		"""

        try:
            errorsDict = indigo.Dict()

            # If there's no version then add it, after this version changes can only happen elsewhere
            if kDeviceVersion not in valuesDict:
                valuesDict[
                    kDeviceVersion] = self.factory.PluginBase.pluginVersion

            # Process through jstuff
            (valuesDict, cbErrors) = self.factory.jstuff.onformFieldChanged(
                valuesDict, typeId, devId)
            if cbErrors: return (valuesDict, cbErrors)

            # Plugin callbacks
            callback = self.factory._callback([])  # Base callback
            if callback:
                (valuesDict, cbErrors) = callback
                if cbErrors: return (valuesDict, cbErrors)

            cleantypeId = ''.join(
                e for e in typeId
                if e.isalnum())  # Scrub type ID to be a valid function name

            callback = self.factory._callback(
                [valuesDict, typeId, devId], None,
                cleantypeId + "_")  # Device type prefix callback
            if callback:
                (valuesDict, cbErrors) = callback
                if cbErrors: return (valuesDict, cbErrors)

            callback = self.factory._callback(
                [valuesDict, typeId, devId], None, None,
                "_" + cleantypeId)  # Device type suffix callback
            if callback:
                (valuesDict, cbErrors) = callback
                if cbErrors: return (valuesDict, cbErrors)

        except Exception as e:
            self.logger.error(ex.stack_trace(e))

        return (valuesDict, errorsDict)
コード例 #10
0
def type_to_unicode_output(obj):
    """
	Converts the type of the object to a string representation including the type (used for __str__ functions).
	"""

    try:
        if obj is None: return "None"
        return u"{} ({})".format(
            obj,
            unicode(type(obj)).replace("<type '",
                                       "").replace("'>",
                                                   "").replace("<class '", ""))

    except Exception as e:
        e.args += (unicode(type(obj)), )
        raise CalcsException(kExceptionOutputPrefix + ex.stack_trace(e))
コード例 #11
0
def filter_to_dict(filter):
    """
	Reads a filter passed from Devices.xml into a dictionary and returns it.
	"""

    try:
        args = {}
        filter = filter.replace("[", "").replace("]", "")

        for f in filter.split(","):
            f = f.strip()  # Clean  up spaces
            valkey = f.split("=")
            valname = valkey[0].lower().strip()
            args[valname] = valkey[1].strip()

        return args

    except Exception as e:
        e.args += (filter, )
        raise CalcsException(kExceptionOutputPrefix + ex.stack_trace(e))
コード例 #12
0
    def _saveJSON(self, valuesDict):
        """
		Write the contents of the cached records to the valuesDict form field in JSON format.
		"""

        try:
            if kFieldIndexer not in valuesDict:
                return valuesDict  # It's not a jstuff defined form
            if kFieldUniqueId not in valuesDict:
                return valuesDict  # Can't do this without the unique Id
            if valuesDict[kFieldUniqueId] not in self.Records:
                return valuesDict  # Haven't saved records yet
            if valuesDict[kFieldUniqueId] in self.ExcludedForms:
                return valuesDict  # This uniqueId has been excluded

            formrecords = self.Records[valuesDict[kFieldUniqueId]]

            jdata = {}
            if kFieldStorage in valuesDict:
                jdata = json.loads(valuesDict[kFieldStorage]
                                   )  # Load existing JSON if it is there

            for recordType, records in formrecords.iteritems():
                data = {}
                for indexValue, record in records.iteritems():
                    #indigo.server.log("{}: {}: \n{}".format(recordType, indexValue, unicode(record)))
                    data[indexValue] = record.build_dict(
                    )  # Add record for each index

                jdata[
                    recordType] = data  # Add dict of records for each record type

            jdump = json.dumps(jdata)
            indigo.server.log(unicode(jdump))

        except Exception as e:
            self.logger.error(ex.stack_trace(e))

        return valuesDict
コード例 #13
0
    def process_include_exclude(self, item, valuesDict, indexField):
        try:
            valueList = []

            items = item.replace("(", "").replace(")", "")
            items = items.split(";")

            for e in items:
                if e.startswith("ipf"):
                    continue  # Don't add special library fields to any list
                if e == indexField: continue  # Don't add the index to any list

                e = e.strip()
                #indigo.server.log(e)
                if "*" in e:
                    filter = e.replace("*", "")

                    for field, value in valuesDict.iteritems():
                        #indigo.server.log(field)
                        if field.startswith("ipf"):
                            continue  # Don't add special library fields to any list
                        if field == indexField:
                            continue  # Don't add the index to any list

                        if e.endswith("*"):
                            if field.startswith(filter):
                                valueList.append(field)

                        if e.startswith("*"):
                            if field.endswith(filter): valueList.append(field)

                else:
                    valueList.append(e)

        except Exception as e:
            self.logger.error(ex.stack_trace(e))

        return valueList
コード例 #14
0
def convert_temperature(value, toCelsius=False, asInteger=False):
    """
	Convert a temperature value to Celsius or Fahrenheit.
	
	Arguments:
		toCelsius:		convert value to Celsius (default is Fahrenheit)
		asInteger:		returns full float value when false and integer value when true
		
	Returns:
		Converted value as a float
	"""

    try:
        if toCelsius:
            # Convert value to celsius
            value = float(value)
            value = (value - 32) / 1.8000
            value = round(value, precision)

            if asInteger: return int(value)

            return value

        else:
            # Default: convert value to fahrenheit
            value = float(value)
            value = (value * 1.8000) + 32
            value = round(value, precision)

            if asInteger: return int(value)

            return value

    except Exception as e:
        e.args += (value, )
        e.args += (u"to Celsius: {}".format(toCelsius), )
        raise CalcsException(kExceptionOutputPrefix + ex.stack_trace(e))
コード例 #15
0
def version_check(pluginBase):
	global ON_PLUGIN_STORE
	if not ON_PLUGIN_STORE: return # We've already tried to check and failed, do not check again
	
	# Create some URLs we'll use later on
	pluginId = pluginBase.pluginId
	current_version_url = INDIGO_API_URL.format(pluginId)
	store_detail_url = INDIGO_STORE_URL
	pluginBase.next_version_check = datetime.now() + timedelta(days=1)
	
	try:
		# GET the url from the servers with a short timeout (avoids hanging the plugin)
		reply = requests.get(current_version_url, timeout=5)
		# This will raise an exception if the server returned an error
		reply.raise_for_status()
		# We now have a good reply so we get the json
		reply_dict = reply.json()
		plugin_dict = reply_dict["plugins"][0]
		# Make sure that the 'latestRelease' element is a dict (could be a string for built-in plugins).
		latest_release = plugin_dict["latestRelease"]
		if isinstance(latest_release, dict):
			# Compare the current version with the one returned in the reply dict
			if LooseVersion(latest_release["number"]) > LooseVersion(pluginBase.pluginVersion):
				# The release in the store is newer than the current version.
				# We'll do a couple of things: first, we'll just log it
				pluginBase.logger.error(
					"A new version of {} (v{}) is available at: {}".format(
						pluginBase.pluginDisplayName,
						latest_release["number"],
						store_detail_url.format(plugin_dict["id"])
					)
				)
				# We'll change the value of a variable named "Plugin_Name_Current_Version" to the new version number
				# which the user can then build a trigger on (or whatever). You could also insert the download URL,
				# the store URL, whatever.
				try:
					variable_name = "u{}_Current_Version".format(pluginBase.pluginDisplayName.replace(" ", "_"))
					indigo.variable.updateValue(variable_name, latest_release["number"])
				except:
					pass
				# We'll execute an action group named "New Version for Plugin Name". The action group could
				# then get the value of the variable above to do some kind of notification.
				try:
					action_group_name = "New Version for {}".format(pluginBase.pluginDisplayName)
					indigo.actionGroup.execute(action_group_name)
				except:
					pass
				# There are lots of other things you could do here. The final thing we're going to do is send
				# an email to self.version_check_email which I'll assume that you've set from the plugin
				# config.
				if hasattr(self, 'version_check_email') and self.version_check_email:
					indigo.server.sendEmailTo(
						pluginBase.version_check_email, 
						subject="New version of Indigo Plugin '{}' is available".format(pluginBase.pluginDisplayName),
						body="It can be downloaded here: {}".format(store_detail_url)
					)
				
			else:
				pluginBase.logger.info(u"{} is running the latest version, no update needed".format(pluginBase.pluginDisplayName))

		
	except Exception as e:
		if "Not Found for url" in unicode(e):
			ON_PLUGIN_STORE = False
			pluginBase.logger.debug(u"{} is not on the Indigo Plugin Store, update checking disabled".format(pluginBase.pluginDisplayName))
		else:
			pluginBase.logger.error(ex.stack_trace(e))
コード例 #16
0
def version_check(pluginBase):
    global ON_PLUGIN_STORE
    if not ON_PLUGIN_STORE:
        return  # We've already tried to check and failed, do not check again

    # Create some URLs we'll use later on
    pluginId = pluginBase.pluginId
    current_version_url = INDIGO_API_URL.format(pluginId)
    store_detail_url = INDIGO_STORE_URL

    try:
        # GET the url from the servers with a short timeout (avoids hanging the plugin)
        reply = requests.get(current_version_url, timeout=5)
        # This will raise an exception if the server returned an error
        reply.raise_for_status()
        # We now have a good reply so we get the json
        reply_dict = reply.json()
        plugin_dict = reply_dict["plugins"][0]
        # Make sure that the 'latestRelease' element is a dict (could be a string for built-in plugins).
        latest_release = plugin_dict["latestRelease"]
        if isinstance(latest_release, dict):
            # Compare the current version with the one returned in the reply dict
            if LooseVersion(latest_release["number"]) > LooseVersion(
                    pluginBase.pluginVersion):
                # The release in the store is newer than the current version.
                # We'll do a couple of things: first, we'll just log it
                self.logger.error(
                    "A new version of {} (v{}) is available at: {}".format(
                        pluginBase.pluginDisplayName, latest_release["number"],
                        store_detail_url.format(plugin_dict["id"])))
                # We'll change the value of a variable named "Plugin_Name_Current_Version" to the new version number
                # which the user can then build a trigger on (or whatever). You could also insert the download URL,
                # the store URL, whatever.
                try:
                    variable_name = "u{}_Current_Version".format(
                        pluginBase.pluginDisplayName.replace(" ", "_"))
                    indigo.variable.updateValue(variable_name,
                                                latest_release["number"])
                except:
                    pass
                # We'll execute an action group named "New Version for Plugin Name". The action group could
                # then get the value of the variable above to do some kind of notification.
                try:
                    action_group_name = "New Version for {}".format(
                        pluginBase.pluginDisplayName)
                    indigo.actionGroup.execute(action_group_name)
                except:
                    pass
                # There are lots of other things you could do here. The final thing we're going to do is send
                # an email to self.version_check_email which I'll assume that you've set from the plugin
                # config.
                if hasattr(self,
                           'version_check_email') and self.version_check_email:
                    indigo.server.sendEmailTo(
                        pluginBase.version_check_email,
                        subject="New version of Indigo Plugin '{}' is available"
                        .format(pluginBase.pluginDisplayName),
                        body="It can be downloaded here: {}".format(
                            store_detail_url))

            else:
                self.logger.info(
                    u"{} is running the latest version, no update needed".
                    format(pluginBase.pluginDisplayName))

    except Exception as e:
        if "Not Found for url" in unicode(e):
            ON_PLUGIN_STORE = False
            pluginBase.logger.debug(
                u"{} is not on the Indigo Plugin Store, update checking disabled"
                .format(pluginBase.pluginDisplayName))
        else:
            pluginBase.logger.error(ex.stack_trace(e))
コード例 #17
0
    def fieldStuffIndex(self, filter, valuesDict, typeId, targetId):
        """
		Device.xml dynamic list field that defines the jstuff record.
		"""

        try:
            ret = [("default", "")]
            if filter == "":
                self.logger.error(
                    "Form field {} cannot be processed without a filter".
                    format(kFieldIndexer))
                return ret  # This doesn't work without filters

            if kFieldUniqueId not in valuesDict:
                return ret  # The first field change has not transpired if we don't this Id so we can't do this yet

            #self.logger.info ("Setting up jstuff record definition")

            # Break down the filter
            args = calcs.filter_to_dict(filter)
            #if not kFieldMethod in args:
            #	indigo.server.log("Couldn't find {}".format(kFieldMethod))
            #	indigo.server.log(unicode(args))
            #	self.ExcludedForms.append(valuesDict[kFieldUniqueId])  # Add to excluded forms so formFieldChanged doesn't try to process
            #	return ret  # If the method= doesn't equal this then it is malformed

            # All record definitions for this unique Id
            definitions = {}
            if valuesDict[kFieldUniqueId] in self.RecordDefinitions:
                definitions = self.RecordDefinitions[
                    valuesDict[kFieldUniqueId]]

            # See if this record definition has already been added
            if definitions and "name" in args and args["name"] in definitions:
                return ret

            # Process the filter
            rec = _JStuffRecordDefinition(args, valuesDict, typeId, targetId)
            definitions[
                rec.
                name] = rec  # Save this record definition to this unique Id list of record definitions
            self.RecordDefinitions[valuesDict[
                kFieldUniqueId]] = definitions  # Save to global cache

            # Create the index so we can find this later
            for field in rec.include:
                recitem = []
                if field in self.RecordFieldIndexes:
                    recitem = self.RecordFieldIndexes[field]

                if not rec.name in recitem:
                    recitem.append(valuesDict[kFieldUniqueId] + "|" + rec.name)
                self.RecordFieldIndexes[field] = recitem

            #indigo.server.log(unicode(self.RecordFieldIndexes))
            #indigo.server.log(unicode(self.RecordDefinitions))

        except Exception as e:
            self.logger.error(ex.stack_trace(e))

        return ret
コード例 #18
0
def convert_to_compared_datatype(source, destination):
    """
	Converts the source value to the destination data type.
	
	Arguments:
		source:			the source value who's data type needs to be changed
		destination:	the value that the data type will be derived from
		
	Returns:
		source:			value of source converted to the data type of destination
	"""

    try:
        converted = False  # Assume failure

        # Convert to string types for ease
        stype = str(type(source)).replace("<type '", "").replace("'>", "")
        dtype = str(type(destination)).replace("<type '", "").replace("'>", "")

        # Convert from None
        if stype == "NoneType":
            if dtype == "float": source = 0.0
            if dtype == "int": source = 0
            if dtype == "bool": source = False
            if dtype == "string": source = ""
            converted = True

        # Convert from Boolean
        if stype == "bool":

            # To integer
            if dtype == "int":
                if source: source = 1
                if not source: source = 0
                converted = True

            # To float
            elif dtype == "float":
                if source: source = 1.0
                if not source: source = 0.0
                converted = True

            # To string
            elif dtype == "str":
                if source: source = "true"
                if not source: source = "false"
                converted = True

        # From string
        if stype == "str":

            # To unicode
            if dtype == "unicode":
                source = unicode(source)
                converted = True

            # To boolean
            if dtype == "bool":
                if source.lower() == "true":
                    source = True
                else:
                    source = False  # It's either absolutely true or it's always false

                converted = True

            # To integer
            if dtype == "int":
                try:
                    source = int(source)
                    converted = True
                except:
                    raise TypeConversionError(
                        u"{} value {} cannot be converted to {}".format(
                            stype, source, dtype))

            # To float
            if dtype == "float":
                try:
                    source = float(source)
                    converted = True
                except:
                    raise TypeConversionError(
                        u"{} value {} cannot be converted to {}".format(
                            stype, source, dtype))

        # From unicode to string
        if stype == "unicode" and dtype == "str":
            source = str(source)
            converted = True

        # From integer to float
        if stype == "int" and dtype == "float":
            source = float(source)
            converted = True

        # From float to integer
        if stype == "float" and dtype == "int":
            source = int(round(source))
            converted = True

        if not converted:
            raise TypeConversionError(
                u"Unable to convert source {} to type {}".format(stype, dtype))

    except Exception as e:
        e.args += (u"{} to {}".format(stype, dtype), )
        raise CalcsException(kExceptionOutputPrefix + ex.stack_trace(e))

    return source
コード例 #19
0
    def onformFieldChanged(self, valuesDict, typeId, devId):
        """
		A form field was changed, prompting a call from the factory to here in case there are JSON parameters.
		"""

        try:
            errorsDict = indigo.Dict()

            if kFieldIndexer not in valuesDict:
                return valuesDict  # Record doesn't have record definitions so no need to do anything else
            if kFieldUniqueId not in valuesDict:
                valuesDict[kFieldUniqueId] = str(
                    int(random.random() * 100000000))  # Add unique Id

            #self.logger.info ("Saving record")

            records = {}
            if valuesDict[kFieldUniqueId] in self.Records:
                records = self.Records[valuesDict[kFieldUniqueId]]

            jstuff = {}
            if kFieldStorage in valuesDict:
                jstuff = json.loads(valuesDict[kFieldStorage])

            # See if any form fields are being used on records
            if valuesDict[kFieldUniqueId] in self.RecordDefinitions:
                #self.logger.info ("Extracting record data")

                recordSet = self.RecordDefinitions[
                    valuesDict[kFieldUniqueId]]  # All records for this Id

                for field, value in valuesDict.iteritems():
                    if field in self.RecordFieldIndexes:
                        attachedRecords = self.RecordFieldIndexes[field]

                        # Loop through all record definitions attached to this field
                        for recordDefs in attachedRecords:
                            (uniqueId, recordDef) = recordDefs.split("|")
                            if uniqueId != valuesDict[kFieldUniqueId]:
                                continue  # Different form

                            # If we get here then we have a def, lets get the record definition class object
                            recordClasses = self.RecordDefinitions[uniqueId]
                            recordClass = recordClasses[recordDef]

                            # No sense going further if the index value of the record def is empty
                            if valuesDict[recordClass.indexField] == "":
                                self.logger.info(
                                    "Index {} is empty, not saving {} record".
                                    format(recordClass.indexField,
                                           recordClass.name))
                                continue

                            # Get all records for this form
                            formRecordTypes = {}
                            if uniqueId in self.Records:
                                formRecordTypes = self.Records[uniqueId]

                            # Get all records for this record type
                            formRecords = {}
                            if recordDef in formRecordTypes:
                                formRecords = formRecordTypes[recordDef]

                            # Get this index
                            if recordClass.indexField in formRecords:
                                rec = formRecords[recordClass.indexField]
                            else:
                                rec = _JStuffRecord(recordClass, valuesDict)

                            # Capture the existing value and compare to new value if this is the index field
                            if field == recordClass.indexField and getattr(
                                    rec, field) != value and rec.indexReset:
                                self.logger.error("Index field changed!")

                            # Set the value
                            rec.set_field_value(field, value)
                            if rec.updated:
                                valuesDict[kFieldRecChanged] = True
                            else:
                                valuesDict[kFieldRecChanged] = False

                            # Save the record to cache
                            formRecords[valuesDict[
                                recordClass.
                                indexField]] = rec  # Save record with the dict key being the value of the index field
                            formRecordTypes[recordDef] = formRecords
                            self.Records[uniqueId] = formRecordTypes

                valuesDict = self._saveJSON(
                    valuesDict)  # Write the JSON data to the record

        except Exception as e:
            self.logger.error(ex.stack_trace(e))

        return (valuesDict, errorsDict)