For one of our customers we build a Canvas app to scan QR codes during events. On each scan a label has to be printed for the batch of de attendees. Because we cannot connect the device to the printer, so cloud printing is the obvious solution.

For printing we choose a Zebra ZD421 Label printer, which has a build in print server that connects to the cloud service of Zebra. To interact with the API we choose Power Automate. We couldn’t use a custom connector because PowerFX doesn’t support base64 conversion.

Ingredients

  1. Zebra ZD421 Label Printer, connected to internet
  2. Cloud registration and plans on https://developer.zebra.com/
  3. Create label template
  4. Power Automate Flow call to Zebra API (Savanne)
  5. Canvas app calling Power Automate Flow

Steps

1 – Connect label printer to Internet

Buying your own label printer is the easy part. Connecting it to internet is the difficult one. Using the Zebra configuration tool all technical internet settings are send as configuration file to the Zebra printer and, with luck, you Zebra printer network icon will glow green.

2 – Cloud registration

Registration to the developer cloud is free (https://developer.zebra.com ). From there you can choose and buy plans to your needs. We use the SendToPrinter (Free) plan, which you may use up to 100 calls per day. If that is not enough, a pay per use plan is also possible.

You also have to select the Zebra Sananne Base Platform API plan for communicating with the printer.

After selecting the plans, you may register your app in the same environment. Select both plans within your app and save App ID and Tenant ID.

Finally you have to register your device. Select Add device and follow the specific steps that are mentioned there. Again a setup file with information about your cloud is send to the printer. Write down the serial of the printer for use in the API.

Zebra provides two API’s, where the staging API may be used for testing purposes. It works just like the production API, without printing te label:

  •  https://api.zebra.com/v2/devices/printers/send
  • https://stage-api.zebra.com/v2/devices/printers/send

3 – Create label template

Zebra is using the ZPL protocol to print labels. Labels may be deigned with the free design tool form Zebra (https://www.zebra.com/gb/en/support-downloads/printer-software/zebra-designer-3-downloads.html ). I use the text {{name}} (for example) to indicate the variables that has to be changed when creating a label.

Please be aware that if you choose another font then the default Zebra fonts, we cannot search and replace the variables anymore. Texts in these fonts are binary included in the ZPL file.

3 – Power Automate call to Zebra API

Within Power Automate you may call the Zebra API. This can be done by creating a custom connector, or directly from the HTTP action. I used the last one. The Zebra API uses the PI key, Tenant number, Serial number of the printer and the ZPL file (text file).

We need to do a few steps to create the text file:

Convert the text of the ZPL file to base64:

And include the bas64 string in a multi-art message to the API of Zebra. This step is a bit tricky, but should work in other environments as well:

Part of the body are both two form fields:

  • sn: Serial number of the printer
  • zpl_file: Filename and contents (base64) of the ZPL text file

The body will be:

{
  "$content-type": "multipart/form-data; boundary=--------------------------186882020322321673087436",
  "$multipart": [
    {
      "headers": {
        "Content-Disposition": "form-data; name=\"sn\"",
        "Content-Type": "text/plain"
      },
      "body": "@{triggerBody()['printer_serial']}"
    },
    {
      "headers": {
        "Content-Disposition": "form-data; name=\"zpl_file\"; filename=\"Test 14.prn\"",
        "Content-Type": "application/octet-stream"
      },
      "body": {
        "$content-type": "application/octet-stream",
        "$content": "@{outputs('LabelBase64')}"
      }
    }
  ]
}

 

5. Canvas app calling the flow

The canvas app has to suit your own needs, of course. However, to replace the variables with the needed text, I use the following code:

Conclusion

I hope this document spares you all the research I needed to build this all! At the end we have a nice app that prints labels in about 2 seconds. Really great!

If you have any suggestions or questioins, pleas contact me on elowy@grootcrm.nl

For several of my customers I need to monitor if flows fail or are being set to the disabled state. For example if a solution import of flows fails. Till now this is not default functionality in Microsoft Automate, so has to be custom developed. I use a flow for this, the periodacaly checks if one of the flows is being disabled, or if any failed runs arises.

I use the “List flows as admin” action to find all custom flows (including flows within solutions). This action lists all flows, together with the state of each flow. Unfortunately, there is no listing of runs within this connector. So we have to make our own connector to list the runs within a flow. All steps for creating this flow are:

  • Create App registration in Azure to access the Automate Management endpoint
  • Create Custom Connector to list runs, using the app registration for authorization
  • Create the Check Status en failed runs flow

Some resources where steps are described in more detail:

Create App Registration

When creating an app registration, the following steps are important:

  • Redirect URL: “https://global.consent.azure-apim.net/redirect”
  • Create permissions for access to read flows:

Create custom connector

In the custom connector there will be the call to the endpoint checking the flow runs. This call is:

https://api.flow.microsoft.com/providers/Microsoft.ProcessSimple/environments/{EnvironmentID}/flows/{FlowID}/runs?api-version=2016-11-01

Create flow

If you see any improvements, pleasae let me know: elowy@grootcrm.nl.

Wish you all the luck with this flow!

When getting data from a WooCommerce, Magento or whatever form API, all fields and values are returned in a (generic) attribute list format. Like from a contact form:

[
	{
		"field_id": "1",
		"code": "Firstname",
		"value": "John"
	},
	{
		"field_id": "2",
		"code": "Lastname",
		"value": "Doe"
	},
	{
		"field_id": "3",
		"code": "Email",
		"value": "test@test.com"
	}
]

To pull each individual field out of this list and store it in Dataverse (for example) can be done with the Filter Array data operation. For a lot of fields (that might change periodically), this is very time consuming. So I tried to build some flow steps that pull out each field from the attribute list and add it to an object. So I am able to use an object in the subsequent flow actions.

To do this, I loop through each item and add both the ‘code’ and ‘value’ (in this example) fields to a (text variable) JSON “object”:

When the JSON text object is created, I parse it.

The following object was the result of parsing the text object that was generated from the example attribute list

So, that’s how I did it. Any suggestions? Or easier solutions? Please respond to elowy@grootcrm.net. Thanks!

In some cases, I want to start a Automate Flow from a Button in a Model Driven App. A really nice method is using the Ribbon Workbech. With this tool you can create a button in the top ribbon, or a sub view ribben which starts a Automate Flow. An explanation of this can be found here:

https://develop1.net/public/post/2019/12/21/smart-buttons-for-the-unified-interface

But what if you want a button on a other place in your screen (for example below a grid), like this:

Lees meer

To show the status of a flow, we first have to know what flow is started and when it is ended.

Lees meer

In all my projects I come to the point where I want the user to wait for a flow to end ór get notified if so. Where is the possibility to start a flow synchronous, like when using workflows? Spoiler alert; I haven’t found a way to start Microsoft Automate flows synchronously from a Model Driven App. Unfortunately. But there are some possibilities to make the user experience a little bit smoother!

Lees meer