Sending Cloud-To-Device message for devices provisoned through IoT-central

我怕爱的太早我们不能终老 提交于 2019-12-02 06:33:56

For your specific use case, the only way to do this today is to trigger a Logic App workflow using its HTTP endpoint from the Azure Functions. In the Logic app, you can build a workflow using the Azure IoT Central connector that updates device properties and settings.

We are working on APIs in IoT Central that can light up use cases like yours, so stay tuned!

As I mentioned in my comment, the Azure IoT Central has a full control over the internal IoT Hub service-facing endpoint. However, there is a way, where the Azure IoT Central allows a limited access to this service-facing endpoint and using a REST API to handle a device twin and invoking a Direct Method on the device.

The following are steps how to obtain a sas token for authorization header needed for REST Api calls:

  1. Get the Access Token from your Azure IoT Central application.

    the format is:

    SharedAccessSignature sr=appId&sig=xxxxx&skn=myTokenName&se=1577730019340
    

    Note, that the appId is showing an application id of your Azure IoT Central application

  2. Call the REST POST request to obtain an iothubTenantSasToken.sasToken

    POST https://api.azureiotcentral.com/v1-beta/applications/{appId}/diagnostics/sasTokens  
    Authorization:SharedAccessSignature sr=appId&sig=xxxxx&skn=myTokenName&se=1577730019340
    

    The response has the following format:

    {
      "iothubTenantSasToken": {
        "sasToken": "SharedAccessSignature sr=saas-iothub-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.azure-devices.net&sig=xxxxx&se=1546197703&skn=service"
        },
      "eventhubSasToken": {
        "sasToken": "SharedAccessSignature sr=sb%3A%2F%2Fep-ns-saas-ep-15-262-xxxxxxxxxx.servicebus.windows.net%2Fep-ehub-saas-iothu-1044564-xxxxxxxxxx&sig=xxxxxx&se=1546197703&skn=service",
        "entityPath": "ep-ehub-saas-iothu-1044564-xxxxxxxxxx",
        "hostname": "sb://ep-ns-saas-ep-15-262-xxxxxxxxxx.servicebus.windows.net/"
        },
      "expiry": 1546197703
    }
    
  3. The sasToken for our service-facing endpoint calls is:

    SharedAccessSignature sr=saas-iothub-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.azure-devices.net&sig=xxxxx&se=1546197703&skn=service
    

Now, we can use some Azure IoT Hub REST APIs, basically the calls with the twins in the uri path, such as:

https://docs.microsoft.com/en-us/rest/api/iothub/service/gettwin

https://docs.microsoft.com/en-us/rest/api/iothub/service/updatetwin

https://docs.microsoft.com/en-us/rest/api/iothub/service/replacetwin

https://docs.microsoft.com/en-us/rest/api/iothub/service/invokedevicemethod

Example for invoking a Direct Method on the device1:

POST https://saas-iothub-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.azure-devices.net/twins/device1/methods?api-version=2018-06-30
Authorization:SharedAccessSignature sr=saas-iothub-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.azure-devices.net&sig=xxxxx&se=1546197703&skn=service

body:
    {
      "methodName": "writeLine",
      "timeoutInSeconds": 20,
      "payload": {
         "input1": 12345,
         "input2": "HelloDevice"
         }
    }

Example of updating a device twin tags property:

PATCH https://saas-iothub-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.azure-devices.net/twins/device1?api-version=2018-06-30
Authorization:SharedAccessSignature sr=saas-iothub-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.azure-devices.net&sig=xxxxx&se=1546197703&skn=service

body:
    {
      "tags": {
        "test":12345
        }
    }

Note, that the sasToken expiry time is 60 minutes. I do recommend to cache the response object from the step 2. and refreshing based on the expiry time.

UPDATE:

The following are steps for using an IoT Central access token for handling a device twins and device direct method within the azure function.

  1. Generate an access token in your IoT Central application, see the following screen snippet:

  1. Add this access token to your function application settings. In this example, the App Setting Name is used AzureIoTCAccessToken. Note, that this access token can be stored in the Azure Key Vault, see more details here.

  2. Create HttpTrigger function in your Function App.

  3. Replace run.csx with the following code:

    #r "Newtonsoft.Json"
    #r "Microsoft.Azure.WebJobs.Extensions.Http"
    
    using System.Net;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Primitives;
    using Microsoft.Azure.WebJobs.Extensions.Http;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Linq;
    using System.Linq;
    using System.Text;
    
    // reusable client proxy
    static HttpClientHelper iothub = new HttpClientHelper(Environment.GetEnvironmentVariable("AzureIoTCAccessToken"));
    
    public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
    {
        log.LogInformation("C# HTTP trigger function processed a request.");
    
        var atype = new { device = new { deviceId = "", properties = new JObject(), measurements = new JObject() } };
        var iotcobj = JsonConvert.DeserializeAnonymousType(await req.ReadAsStringAsync(), atype);
    
        // get deviceId, for test puspose use the device1
        string deviceId = iotcobj?.device?.deviceId ?? "device1";
    
        // get a device twins
        var response = await iothub.Client.GetAsync($"/twins/{deviceId}?api-version=2018-06-30");
        string jsontext = await response.Content.ReadAsStringAsync();
        log.LogInformation($"DeviceTwin: {JsonConvert.DeserializeObject(jsontext)}");
    
       // patch on desired property
       var patch = JsonConvert.SerializeObject(new { properties = new { desired = new { ping = DateTime.UtcNow } } });
       response = await iothub.Client.PatchAsync($"/twins/{deviceId}?api-version=2018-06-30", new StringContent(patch, Encoding.UTF8, "application/json"));
       jsontext = await response.Content.ReadAsStringAsync();
       log.LogInformation($"Patch: {JsonConvert.DeserializeObject(jsontext)}");
    
       // invoke a device method
       var method = new { methodName = "writeLine", timeoutInSeconds = 30, payload = new {input1 = 12345, input2 = "HelloDevice" } };
       response = await iothub.Client.PostAsJsonAsync($"/twins/{deviceId}/methods?api-version=2018-06-30", method );
       jsontext = await response.Content.ReadAsStringAsync();
       log.LogInformation($"DirectMethod: {JsonConvert.DeserializeObject(jsontext)}");
    
       return new OkObjectResult(jsontext);      
    }
    
    class HttpClientHelper
    {
        HttpClient client;
        string accessToken;
        dynamic iothub;
        long toleranceInSeconds = 60;
    
        public HttpClientHelper(string accessToken)
        {
            this.accessToken = accessToken;
            this.iothub = GetIoTHubTenant(accessToken);
            string hostname = GetHostNameFromSaSToken(this.iothub.iothubTenantSasToken.sasToken);
            client = new HttpClient() { BaseAddress = new Uri($"https://{hostname}") };
            client.DefaultRequestHeaders.Add("Authorization", iothub.iothubTenantSasToken.sasToken);
        }
        public HttpClient Client
        {
            get
            {
                if((new DateTime(1970, 1, 1)).AddSeconds(this.iothub.expiry - toleranceInSeconds) < DateTime.UtcNow)
                    SetAuthorizationHeader();
                return client;
            }
        }
        private void SetAuthorizationHeader()
        {
            lock (client)
            {
                if ((new DateTime(1970, 1, 1)).AddSeconds(this.iothub.expiry - toleranceInSeconds) < DateTime.UtcNow)
                {
                    if (client.DefaultRequestHeaders.Contains("Authorization"))
                        client.DefaultRequestHeaders.Remove("Authorization");
                    this.iothub = GetIoTHubTenant(this.accessToken);
                    client.DefaultRequestHeaders.Add("Authorization", this.iothub.iothubTenantSasToken.sasToken);
                }
            }
        }
        private string GetHostNameFromSaSToken(string sastoken)
        {
            var parts = sastoken.Replace("SharedAccessSignature", "").Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Split(new[] { '=' }, 2)).ToDictionary(x => x[0].Trim(), x => x[1].Trim());
            return parts["sr"] ?? "";
        }
    
        private dynamic GetIoTHubTenant(string iotcAccessToken)
        {
            string appId = GetHostNameFromSaSToken(iotcAccessToken);
            using (var hc = new HttpClient())
            {
                hc.DefaultRequestHeaders.Add("Authorization", accessToken);
                string address = $"https://api.azureiotcentral.com/v1-beta/applications/{appId}/diagnostics/sasTokens";
                var response = hc.PostAsync(address, new StringContent("{}", Encoding.UTF8, "application/json")).Result;
                return JsonConvert.DeserializeAnonymousType(response.Content.ReadAsStringAsync().Result, new { iothubTenantSasToken = new { sasToken = "" }, expiry = 0L });
            }
        }
    }
    

Note:, The above implementation is based on the generated access token by your IoT Central application like it has been recently released for general availability, see here. In the case of the changing a format, etc. all clients, testers, etc. included the above solution will be impacted.

You can try using Settings there is a setting type called "Toggle". In order to implement it, you can go to your IoT Central Application from the Azure Portal. Then:

  • Go to Device Explorer tab

  • Select a device

  • Click on the "Settings" tab for the device
  • Click on the "Edit Template" Button located on the upper right corner
  • Under "Library" find and click on "Toggle"

  • Enter the settings for the toggle functionality

If you are looking into doing it at scale you could Create and Run a Job

In IoT Central you can manage your connected devices at scale using jobs. The jobs functionality enables you to perform bulk updates to device properties, settings, and commands.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!