为通过 IoT-central 提供的设备发送 Cloud-To-Device 消息
Sending Cloud-To-Device message for devices provisoned through IoT-central
我一直在阅读这个新的 SaaS 产品的文档,但我没有看到任何关于能够向设备发送消息的提及,例如:到 switch ON/OFF an装备.
https://docs.microsoft.com/en-us/azure/iot-central/tutorial-add-device
我确实看到了通过改变设备孪生来更改设备设置的规定。
此外,我读到有一种方法可以向设备发送 "echo"。但是,这些并不符合我的确切目的。
那么,有没有一种方法可以使用可以使用例程构建的连接字符串来发送 C2D 消息?
https://docs.microsoft.com/en-us/azure/iot-central/tutorial-add-device#prepare-the-client-code
我想通过 AzureFunction 发送此 C2D,但最好知道这是否可以以某种方式集成到 IoT-Central UI。
实现我的要求的任何其他输入(切换ON/OFF设备)也会有很大帮助!
您可以尝试使用 设置 有一个名为 "Toggle" 的设置类型。为了实现它,您可以从 Azure 门户转到您的 IoT Central 应用程序。那么:
转到“设备资源管理器”选项卡
Select 一台设备
- 单击设备的 "Settings" 选项卡
- 点击右上角的"Edit Template"按钮
- 在 "Library" 下找到并点击 "Toggle"
- 输入切换功能的设置
如果您正在考虑大规模实施,您可以Create and Run a Job
在 IoT Central 中,您可以使用作业大规模管理连接的设备。作业功能使您能够对设备属性、设置和命令执行批量更新。
正如我在评论中提到的,Azure IoT Central 可以完全控制内部 IoT 中心 service-facing 端点。但是,有一种方法,Azure IoT Central 允许有限访问此 service-facing 端点并使用 REST API 处理设备孪生并在设备上调用直接方法。
以下是如何获取 REST Api 调用所需的用于授权 header 的 sas 令牌的步骤:
从您的 Azure IoT Central 应用程序获取访问令牌。
格式为:
SharedAccessSignature sr=appId&sig=xxxxx&skn=myTokenName&se=1577730019340
请注意,appId 显示您的 Azure IoT Central 应用程序的应用程序 ID
调用 REST POST 请求获取 iothubTenantSasToken.sasToken
POST https://api.azureiotcentral.com/v1-beta/applications/{appId}/diagnostics/sasTokens
Authorization:SharedAccessSignature sr=appId&sig=xxxxx&skn=myTokenName&se=1577730019340
响应格式如下:
{
"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
}
我们 service-facing 端点调用的 sasToken 是:
SharedAccessSignature sr=saas-iothub-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.azure-devices.net&sig=xxxxx&se=1546197703&skn=service
现在,我们可以使用一些 Azure IoT Hub REST APIs,基本上是在 uri 路径中带有 twins 的调用,例如:
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
在设备 1 上调用直接方法的示例:
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"
}
}
更新设备孪生标记的示例属性:
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
}
}
请注意,sasToken 到期 时间为 60 分钟。我确实建议缓存第 2 步中的响应 object,并根据到期时间进行刷新。
更新:
以下是使用 IoT Central 访问令牌在 azure 函数中处理设备孪生和设备直接方法的步骤。
- 在您的 IoT Central 应用程序中生成访问令牌,请参见以下屏幕片段:
将此访问令牌添加到您的函数应用程序设置中。在此示例中,使用 App 设置名称 AzureIoTAccessToken。请注意,此访问令牌可以存储在 Azure Key Vault 中,请参阅更多详细信息 here。
在函数应用程序中创建 HttpTrigger 函数。
将run.csx替换为以下代码:
#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 });
}
}
}
注意:,上面的实现基于您的 IoT Central 应用程序生成的访问令牌,就像它最近发布的一般可用性一样,请参阅 here。在更改格式等情况下,上述解决方案中包含的所有客户端、测试人员等都将受到影响。
对于您的特定用例,今天执行此操作的唯一方法是从 Azure Functions trigger a Logic App workflow using its HTTP endpoint。在 Logic 应用程序中,您可以使用 Azure IoT Central 连接器 构建工作流,以更新设备属性和设置。
我们正在 IoT Central 中开发可以点亮您这样的用例的 API,敬请期待!
现在可以使用 API 来实现此目的:
https://docs.microsoft.com/en-us/rest/api/iotcentral/devices/executecomponentcommand
我一直在阅读这个新的 SaaS 产品的文档,但我没有看到任何关于能够向设备发送消息的提及,例如:到 switch ON/OFF an装备.
https://docs.microsoft.com/en-us/azure/iot-central/tutorial-add-device
我确实看到了通过改变设备孪生来更改设备设置的规定。
此外,我读到有一种方法可以向设备发送 "echo"。但是,这些并不符合我的确切目的。
那么,有没有一种方法可以使用可以使用例程构建的连接字符串来发送 C2D 消息? https://docs.microsoft.com/en-us/azure/iot-central/tutorial-add-device#prepare-the-client-code
我想通过 AzureFunction 发送此 C2D,但最好知道这是否可以以某种方式集成到 IoT-Central UI。
实现我的要求的任何其他输入(切换ON/OFF设备)也会有很大帮助!
您可以尝试使用 设置 有一个名为 "Toggle" 的设置类型。为了实现它,您可以从 Azure 门户转到您的 IoT Central 应用程序。那么:
转到“设备资源管理器”选项卡
Select 一台设备
- 单击设备的 "Settings" 选项卡
- 点击右上角的"Edit Template"按钮
- 在 "Library" 下找到并点击 "Toggle"
- 输入切换功能的设置
如果您正在考虑大规模实施,您可以Create and Run a Job
在 IoT Central 中,您可以使用作业大规模管理连接的设备。作业功能使您能够对设备属性、设置和命令执行批量更新。
正如我在评论中提到的,Azure IoT Central 可以完全控制内部 IoT 中心 service-facing 端点。但是,有一种方法,Azure IoT Central 允许有限访问此 service-facing 端点并使用 REST API 处理设备孪生并在设备上调用直接方法。
以下是如何获取 REST Api 调用所需的用于授权 header 的 sas 令牌的步骤:
从您的 Azure IoT Central 应用程序获取访问令牌。
格式为:
SharedAccessSignature sr=appId&sig=xxxxx&skn=myTokenName&se=1577730019340
请注意,appId 显示您的 Azure IoT Central 应用程序的应用程序 ID
调用 REST POST 请求获取 iothubTenantSasToken.sasToken
POST https://api.azureiotcentral.com/v1-beta/applications/{appId}/diagnostics/sasTokens Authorization:SharedAccessSignature sr=appId&sig=xxxxx&skn=myTokenName&se=1577730019340
响应格式如下:
{ "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 }
我们 service-facing 端点调用的 sasToken 是:
SharedAccessSignature sr=saas-iothub-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.azure-devices.net&sig=xxxxx&se=1546197703&skn=service
现在,我们可以使用一些 Azure IoT Hub REST APIs,基本上是在 uri 路径中带有 twins 的调用,例如:
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
在设备 1 上调用直接方法的示例:
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"
}
}
更新设备孪生标记的示例属性:
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
}
}
请注意,sasToken 到期 时间为 60 分钟。我确实建议缓存第 2 步中的响应 object,并根据到期时间进行刷新。
更新:
以下是使用 IoT Central 访问令牌在 azure 函数中处理设备孪生和设备直接方法的步骤。
- 在您的 IoT Central 应用程序中生成访问令牌,请参见以下屏幕片段:
将此访问令牌添加到您的函数应用程序设置中。在此示例中,使用 App 设置名称 AzureIoTAccessToken。请注意,此访问令牌可以存储在 Azure Key Vault 中,请参阅更多详细信息 here。
在函数应用程序中创建 HttpTrigger 函数。
将run.csx替换为以下代码:
#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 }); } } }
注意:,上面的实现基于您的 IoT Central 应用程序生成的访问令牌,就像它最近发布的一般可用性一样,请参阅 here。在更改格式等情况下,上述解决方案中包含的所有客户端、测试人员等都将受到影响。
对于您的特定用例,今天执行此操作的唯一方法是从 Azure Functions trigger a Logic App workflow using its HTTP endpoint。在 Logic 应用程序中,您可以使用 Azure IoT Central 连接器 构建工作流,以更新设备属性和设置。
我们正在 IoT Central 中开发可以点亮您这样的用例的 API,敬请期待!
现在可以使用 API 来实现此目的:
https://docs.microsoft.com/en-us/rest/api/iotcentral/devices/executecomponentcommand