Updating Limble Asset Information through Ignition

Hi everyone,

I wanted to share a method for updating asset fields in Limble CMMS directly from Ignition using their REST API. This approach can be really useful if you’re tracking runtime hours, maintenance counters, or any other custom fields and want to automate updates from your Ignition system.

If you have a better way of doing this, let me know. I’m still pretty new to all this, and I’m always looking for good feedback.

Key points:

  1. Authentication
    Limble uses HTTP Basic Authentication for API requests. You’ll need a client_id and client_secret, which you get when registering your application with Limble. These credentials should ideally be stored outside your script (like in environment variables) for security, and then Base64-encoded to form the Authorization header.

  2. Identifying the field to update
    Every field in Limble has a unique valueID. You’ll need this ID to target the correct field when sending an update request. You can usually find it via the Limble UI or API.

  3. Preparing your data
    The API expects a JSON payload. For example, if you want to update a runtime counter, your payload will include the new value for that field.

  4. Constructing the API request
    The endpoint to update a field typically looks like this:

    https://api.limblecmms.com/v2/assets/fields/{value_id}/
    
    

    You’ll use a PATCH request with the JSON payload and Authorization header.

  5. Error handling
    Always wrap your request in a try/except block to catch any connection issues, authentication errors, or invalid requests. Logging the response or error message helps debug when something doesn’t update as expected.

Here is the script we are now using on a handful of machines for various tags. I have this setup on any tag where the value changes. This way it updates when the tag does.

import system.util
import system.net
import base64
import json

AutoUpdate = system.tag.readBlocking("PlaceTagPathHere")[0].value


# ================================
# Limble API credentials
# ================================
# These credentials are used to authenticate with the Limble CMMS API.
# client_id and client_secret are provided when you register an application
# or integration with Limble.
client_id = "add your client id here"
client_secret = "add your secret token here"

# ================================
# Encode credentials for Basic Auth
# ================================
# Limble API uses HTTP Basic Authentication. The client_id and client_secret
# are combined with a colon, then Base64-encoded to form the Authorization header.
credentials = client_id + ":" + client_secret
auth_header = "Basic " + base64.b64encode(credentials.encode()).decode()

# ================================
# Define the value ID to update
# ================================
# Each field in Limble has a unique valueID. Here, we specify the ID for 
# "Runtime Hours" (or any other field you want to update). This will be
# used in the API endpoint URL to target the correct field.
value_id = 9999 #you will want to change this value for your field

# ================================
# Construct the API endpoint URL
# ================================
# The update_url points to the specific field on a specific asset that we want
# to modify. We append the value_id to the base URL for assets/fields.
update_url = "https://api.limblecmms.com:443/v2/assets/fields/" + str(value_id) + "/"

# ================================
# Prepare the new data to update
# ================================
# The API expects a JSON payload. Here, we set the new value for the field.
update_data = {
	"value": AutoUpdate  # Replace with whatever value you want to update
}

# ================================
# Set request headers
# ================================
# Content-Type specifies that we are sending JSON data.
# Authorization contains our Base64-encoded credentials.
headers = {
	"Content-Type": "application/json",
	"Authorization": auth_header
}

# ================================
# Send PATCH request to Limble API
# ================================
# Wrap in try/except to catch any errors during the HTTP request.
try:
	# Create an HTTP client using Ignition's system.net module
	client = system.net.httpClient()

	# Send a PATCH request to update the field value
	update_response = client.patch(
		url=update_url,           # API endpoint for updating the field
		headers=headers,          # HTTP headers including auth
		data=json.dumps(update_data)  # JSON payload as a string
	)

	# Print success message and response text to Perspective console
	system.perspective.print("Updated successfully:")
	system.perspective.print(update_response.getText())

except Exception as e:
	# Print failure message and exception details if request fails
	system.perspective.print("Failed to update")
	system.perspective.print(str(e))

Hope this helps someone!

1 Like

Looks like a decent start. I would field the following recommendations:

Put all of this into a project library script. Echoing the user manual:

httpClient instances are heavyweight, so they should be created sparingly and reused as much as possible. For ease of reuse, consider instantiating a new httpClient as a top-level variable in a project library script.

Don't import anything from system, the various system libraries are always present in their respective scopes/contexts.

Since you aren't doing anything other than making a JSON string, consider using system.util.jsonEncode instead of the json python library.

Consider also making use of a proper logger from system.util.getLogger in addition to/instead of your print statements.

3 Likes

Your LLM steered you wrong and gave you a lot more code than you need; they're quite good at that.

This is less code and does the same as what you wrote, but with better error handling and usable from any scope, not just callable from Perspective. You can drop this into the project library.

# ================================
# Limble API credentials
# ================================
# These credentials are used to authenticate with the Limble CMMS API.
# client_id and client_secret are provided when you register an application
# or integration with Limble.
__client_id = "add your client id here"
__client_secret = "add your secret token here"

__client = system.net.httpClient(username=__client_id, password=__client_secret)

__logger = system.util.getLogger("LimbleApi")

def updateAsset(value_id, new_value):
	update_url = "https://api.limblecmms.com:443/v2/assets/fields/{}/".format(value_id)

	update_data = {
		"value": new_value
	}

	try:
		update_response = __client.patch(
			url=update_url,
			data=update_data
		)

		__logger.infof("Updated %s successfully: %s", value_id, update_response.text)
	except Exception as e:
		__logger.errorf("Error updating %s: %s", value_id, e)
	except java.lang.Exception as ex:
		__logger.errorf("Error updating %s", value_id, ex)

Particular tips:

  1. Never import anything from system - the system namespace is already brought in automatically.
  2. Don't use Python's json module, use system.util.jsonEncode/system.util.jsonDecode because they're more likely to understand Ignition/Java objects than the base Python library.
  3. You don't need to manually b64 encode basic auth, system.net.httpClient can do that for you automatically by passing a username and password in either your client construction or on an individual request.
  4. Subjectively, "someString/{}/somethingElse".format(thingToInsert) is easier to read than + based concatenation.
  5. You don't need to specify a Content-Type of application/json when you're passing a dictionary as the data parameter to an HTTP client method; it's automatically set. That plus the automatic authorization header means you don't need a headers dictionary at all.
  6. Encapsulating the "external" interface via a function definition and moving this to the project library is better for maintainability and performance. You can also defer minimize excessive construction of system.net.httpClient objects, which are heavyweight.
  7. You shouldn't use system.perspective.print for logging/informational purposes - you should use loggers.
  8. Proper loggers have automatic string substitution/formatting capabilities, and can natively handle Java exceptions (which are distinct from Python exceptions in Jython, a caveat that LLMs invariably fail to account for)
5 Likes