Having some really weird issues with my Python Script, but I was able to replicate it using Microsoft's sample script included in their Python SDK here.
Basically, when I run this code using anything but MQTT_WS, it fails with various errors. Here is the code: (Note, I removed the actual cert/keys, so this will not work for you).
# Copyright (c) Microsoft. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for
# full license information.
import random
import time
import sys
import iothub_client
from iothub_client import IoTHubClient, IoTHubClientError, IoTHubTransportProvider, IoTHubClientResult
from iothub_client import IoTHubMessage, IoTHubMessageDispositionResult, IoTHubError
from iothub_client_args import get_iothub_opt, OptionError
# HTTP options
# Because it can poll "after 9 seconds" polls will happen effectively
# at ~10 seconds.
# Note that for scalabilty, the default value of minimumPollingTime
# is 25 minutes. For more information, see:
# https://azure.microsoft.com/documentation/articles/iot-hub-devguide/#messaging
TIMEOUT = 241000
MINIMUM_POLLING_TIME = 9
# messageTimeout - the maximum time in milliseconds until a message times out.
# The timeout period starts at IoTHubClient.send_event_async.
# By default, messages do not expire.
MESSAGE_TIMEOUT = 10000
RECEIVE_CONTEXT = 0
AVG_WIND_SPEED = 10.0
MIN_TEMPERATURE = 20.0
MIN_HUMIDITY = 60.0
MESSAGE_COUNT = 5
RECEIVED_COUNT = 0
# global counters
RECEIVE_CALLBACKS = 0
SEND_CALLBACKS = 0
PROTOCOL = IoTHubTransportProvider.MQTT_WS
# String containing Hostname, Device Id in the format:
# "HostName=<host_name>;DeviceId=<device_id>;x509=true"
CONNECTION_STRING = "HostName=hub.azure-devices.net;DeviceId=device;x509=true"
MSG_TXT = "{\"deviceId\": \"device\",\"windSpeed\": %.2f,\"temperature\": %.2f,\"humidity\": %.2f}"
X509_CERTIFICATE = (
'''-----BEGIN CERTIFICATE-----
MIIFtTCCA52gAwIBAgIBAzANBgkqhkiG9w0BAQsFADA0MTIwMAYDVQQDDClBenVy
...
T4kySzeQghCYqpkF06hER0KXTP8shMZedg==
-----END CERTIFICATE-----'''
)
X509_PRIVATEKEY = (
'''-----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEAnEOzMmctBZke/1kD+5g/soUTucJ28odMTW2RIlfj4kAhT1gW
...
UOBMkQUpHEBNWtBbQIyJbgrb26P1oec7dk5f2xvg7tHjSZLDOZprdEp8gxU=
-----END RSA PRIVATE KEY-----'''
)
# some embedded platforms need certificate information
def receive_message_callback(message, counter):
global RECEIVE_CALLBACKS
message_buffer = message.get_bytearray()
size = len(message_buffer)
print("Received Message [%d]:" % counter)
print(" Data: <<<%s>>> & Size=%d" % (message_buffer[:size].decode('utf-8'), size))
map_properties = message.properties()
key_value_pair = map_properties.get_internals()
print(" Properties: %s" % key_value_pair)
counter += 1
RECEIVE_CALLBACKS += 1
print(" Total calls received: %d" % RECEIVE_CALLBACKS)
return IoTHubMessageDispositionResult.ACCEPTED
def send_confirmation_callback(message, result, user_context):
global SEND_CALLBACKS
print("Confirmation[%d] received for message with result = %s" % (user_context, result))
map_properties = message.properties()
print(" message_id: %s" % message.message_id)
print(" correlation_id: %s" % message.correlation_id)
key_value_pair = map_properties.get_internals()
print(" Properties: %s" % key_value_pair)
SEND_CALLBACKS += 1
print(" Total calls confirmed: %d" % SEND_CALLBACKS)
def iothub_client_init():
# prepare iothub client
client = IoTHubClient(CONNECTION_STRING, PROTOCOL)
# HTTP specific settings
if client.protocol == IoTHubTransportProvider.HTTP:
client.set_option("timeout", TIMEOUT)
client.set_option("MinimumPollingTime", MINIMUM_POLLING_TIME)
# set the time until a message times out
client.set_option("messageTimeout", MESSAGE_TIMEOUT)
# this brings in x509 privateKey and certificate
client.set_option("x509certificate", X509_CERTIFICATE)
client.set_option("x509privatekey", X509_PRIVATEKEY)
# to enable MQTT logging set to 1
if client.protocol == IoTHubTransportProvider.MQTT:
client.set_option("logtrace", 0)
client.set_message_callback(
receive_message_callback, RECEIVE_CONTEXT)
return client
def print_last_message_time(client):
try:
last_message = client.get_last_message_receive_time()
print("Last Message: %s" % time.asctime(time.localtime(last_message)))
print("Actual time : %s" % time.asctime())
except IoTHubClientError as iothub_client_error:
if iothub_client_error.args[0].result == IoTHubClientResult.INDEFINITE_TIME:
print("No message received")
else:
print(iothub_client_error)
def iothub_client_sample_x509_run():
try:
client = iothub_client_init()
while True:
# send a few messages every minute
print("IoTHubClient sending %d messages" % MESSAGE_COUNT)
for message_counter in range(0, MESSAGE_COUNT):
temperature = MIN_TEMPERATURE + (random.random() * 10)
humidity = MIN_HUMIDITY + (random.random() * 20)
msg_txt_formatted = MSG_TXT % (
AVG_WIND_SPEED + (random.random() * 4 + 2),
temperature,
humidity)
# messages can be encoded as string or bytearray
if (message_counter & 1) == 1:
message = IoTHubMessage(bytearray(msg_txt_formatted, 'utf8'))
else:
message = IoTHubMessage(msg_txt_formatted)
# optional: assign ids
message.message_id = "message_%d" % message_counter
message.correlation_id = "correlation_%d" % message_counter
# optional: assign properties
prop_map = message.properties()
prop_map.add("temperatureAlert", 'true' if temperature > 28 else 'false')
client.send_event_async(message, send_confirmation_callback, message_counter)
print(
"IoTHubClient.send_event_async accepted message [%d] for transmission to IoT Hub." % message_counter)
# Wait for Commands or exit
print("IoTHubClient waiting for commands, press Ctrl-C to exit")
status_counter = 0
while status_counter <= MESSAGE_COUNT:
status = client.get_send_status()
print("Send status: %s" % status)
time.sleep(10)
status_counter += 1
except IoTHubError as iothub_error:
print("Unexpected error %s from IoTHub" % iothub_error)
return
except KeyboardInterrupt:
print("IoTHubClient sample stopped")
print_last_message_time(client)
def usage():
print("Usage: iothub_client_sample.py -p <protocol> -c <connectionstring>")
print(" protocol : <amqp, http, mqtt>")
print(" connectionstring: <HostName=<host_name>;DeviceId=<device_id>;SharedAccessKey=<device_key>>")
if __name__ == '__main__':
print("\nPython %s" % sys.version)
# print ( "IoT Hub for Python SDK Version: %s" % iothub_client.__version__ )
try:
(CONNECTION_STRING, PROTOCOL) = get_iothub_opt(sys.argv[1:], CONNECTION_STRING, PROTOCOL)
except OptionError as option_error:
print(option_error)
usage()
sys.exit(1)
print("Starting the IoT Hub Python sample...")
print(" Protocol %s" % PROTOCOL)
print(" Connection string=%s" % CONNECTION_STRING)
iothub_client_sample_x509_run()
SUCCESSFUL RUN: MQTT_WS (Port 443)
IoTHubClient.send_event_async accepted message [0] for transmission to IoT Hub.
Confirmation[0] received for message with result = OK
message_id: message_0
correlation_id: correlation_0
Properties: {'temperatureAlert': 'false'}
Total calls confirmed: 1
UNSUCCESSFUL RUN: MQTT(Port 8883) - Message Timeout during retrieval
IoTHubClient.send_event_async accepted message [0] for transmission to IoT Hub.
Confirmation[0] received for message with result = MESSAGE_TIMEOUT
message_id: message_0
correlation_id: correlation_0
Properties: {'temperatureAlert': 'false'}
Total calls confirmed: 1
UNSUCCESSFUL RUN: AMQP(Port 5671) - Errors when trying to connect, then Message Timeout
IoTHubClient.send_event_async accepted message [0] for transmission to IoT Hub.
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\iothub_client\src\iothubtransport_amqp_common.c Func:on_amqp_connection_state_changed Line:772 amqp_connection was closed unexpectedly; connection retry will be triggered.
Info: Preparing transport for re-connection
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\iothub_client\src\iothubtransportamqp_methods.c Func:iothubtransportamqp_methods_unsubscribe Line:891 unsubscribe called while not subscribed
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\c-utility\adapters\tlsio_schannel.c Func:send_chunk Line:430 invalid tls_io_instance->tlsio_state: TLSIO_STATE_NOT_OPEN
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\c-utility\adapters\tlsio_schannel.c Func:internal_send Line:525 send_chunk failed
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\c-utility\adapters\tlsio_schannel.c Func:tlsio_schannel_send Line:1263 send failed
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\uamqp\src\connection.c Func:on_bytes_encoded Line:268 Cannot send encoded bytes
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\c-utility\adapters\tlsio_schannel.c Func:tlsio_schannel_close Line:1195 invalid tls_io_instance->tlsio_state = TLSIO_STATE_NOT_OPEN
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\uamqp\src\connection.c Func:on_bytes_encoded Line:272 xio_close failed
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\c-utility\adapters\tlsio_schannel.c Func:tlsio_schannel_close Line:1195 invalid tls_io_instance->tlsio_state = TLSIO_STATE_NOT_OPEN
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\uamqp\src\connection.c Func:connection_close Line:1437 xio_close failed
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\iothub_client\src\iothubtransport_amqp_common.c Func:on_amqp_connection_state_changed Line:772 amqp_connection was closed unexpectedly; connection retry will be triggered.
Info: Preparing transport for re-connection
Confirmation[0] received for message with result = MESSAGE_TIMEOUT
message_id: message_0
correlation_id: correlation_0
Properties: {'temperatureAlert': 'false'}
Total calls confirmed: 1
UNSUCCESSFUL RUN: AMQP_WS(Port 443) - Errors when trying to connect, then Message Timeout
Same as regular AMQP
UNSUCCESSFUL RUN: HTTP(Port 443) - Errors when trying to connect
IoTHubClient.send_event_async accepted message [0] for transmission to IoT Hub.
Error: Time:Tue Aug 21 17:20:18 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\iothub_client\src\iothubtransporthttp.c Func:DoEvent Line:1700 unexpected HTTP status code (401)
Error: Time:Tue Aug 21 17:20:18 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\iothub_client\src\iothubtransporthttp.c Func:DoMessages Line:2102 expected status code was 200, but actually was received 401... moving on
IoTHubClient.send_event_async accepted message [4] for transmission to IoT Hub.
Error: Time:Tue Aug 21 17:20:18 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\iothub_client\src\iothubtransporthttp.c Func:DoEvent Line:1700 unexpected HTTP status code (401)
I have Python version 3.6.6, on a Windows 10 machine.
Honestly, I'm not sure what is causing this issue. I don't think it's the certificates, because they work when I use them with MQTT_WS. I tried disabling Firewall on Windows for a brief period while I tested and that didn't help either.
Also, this was working before. I had a separate script that I built that would connect up to a protocol that the user passed in, and my test scripts would loop through to ensure it was all working. I took a break to work on getting those scripts to work for Mac/Linux. I ran into road blocks on those platforms, and when I switched back over to Windows, realized that it was running into the same issues. I have not changed anything on the Azure Hub to prevent connections of a certain type, so not sure what is causing this issue.
I've tried generating new certificates and creating a new x509 self signed device, but it was running into the same issues as well :(
Hope someone out there can help!
Edit: I was able to resolve my issue by creating new certificates for IoTHub. It seems the ones I had on there were generated with test scripts, so were only good for 30 days. I do not understand how the MQTT_WS protocol was still able to resolve and get through, though, but that's a separate issue altogether.
Thanks for those who tried to help!