Secuencia de comandos de demostración de pasarela MQTT

En esta página se proporciona una secuencia de comandos de Python que muestra cómo funcionan las pasarelas. Al usar ejemplos de Cloud IoT Core, la secuencia de comandos crea primero un registro de demostración, una pasarela y un dispositivo. A continuación, vincula el dispositivo a la pasarela, escucha mensajes de configuración y envía datos del estado en nombre del dispositivo. Por último, la secuencia de comandos desvincula el dispositivo automáticamente, elimina el dispositivo y la pasarela y borra el registro.

Funciones

La secuencia de comandos muestra las siguientes funciones de pasarela:

  • Crear una pasarela
  • Crear un dispositivo para vincularlo con la pasarela
  • Vincular el dispositivo a la pasarela
  • Escuchar mensajes de configuración.
  • Enviando datos de estado
  • Desvincular el dispositivo de la pasarela
  • Eliminar el dispositivo y eliminar la pasarela

Objetivos

Después de ejecutar esta secuencia de comandos, conocerás el código que realiza lo siguiente:

  • Crear un registro
  • Crear una pasarela y un dispositivo para vincular
  • Vincular la pasarela al dispositivo
  • Escuchar mensajes de configuración.

Precios

Los datos transmitidos en esta demo se encuentran dentro del nivel de uso gratuito. Consulta Precios para obtener más información.

Antes de empezar

En esta demostración se da por hecho que estás familiarizado con Python y que has revisado la descripción general de las pasarelas.

Para poder ejecutar este ejemplo, debes obtener un ID de proyecto, la herramienta de línea de comandos gcloud y comprobar que la facturación esté habilitada:

  1. Crea un proyecto de la consola de Cloud o obtén el ID de un proyecto que ya existe desde la consola de Google Cloud:

    Ir a la página Proyectos

  2. Instala e inicializa el SDK de Google Cloud:

    Descargar SDK

  3. Comprueba que la facturación esté habilitada en tu proyecto.

    Más información sobre cómo habilitar la facturación

Descargar la secuencia de comandos

Descarga la secuencia de comandos de demostración y configura el directorio actual:

git clone https://github.com/GoogleCloudPlatform/python-docs-samples
cd python-docs-samples/iot/api-client/mqtt_example

Instalar dependencias

En el directorio iot/api-client/mqtt_example, instala las dependencias necesarias para ejecutar el ejemplo:

pip install -r requirements.txt --user

Para obtener más información sobre la configuración de tu entorno de desarrollo de Python, como la instalación de pip en el sistema, consulta la guía de configuración del entorno de desarrollo de Python.

Crear credenciales

Antes de ejecutar el ejemplo, siga estos pasos:

  1. Crea una cuenta de servicio llamada gateway-demo y haz clic en Crear.
  2. Selecciona el rol Editor de proyectos.
  3. Sáltate la pantalla de Permisos de cuenta de servicio y haz clic en Continuar.
  4. Haz clic en Crear clave.
  5. En el panel Crear clave, en Tipo de clave,selecciona JSON.
  6. Haz clic en Crear.
  7. Guarda esta clave en el directorio iot/api-client/mqtt_example y cámbiale el nombre a service_account.json.
  8. Descarga el certificado raíz de CA de Google en el mismo directorio que los archivos de ejemplo. Si quieres, puedes definir la ubicación del certificado mediante la marca --ca_certs.

Añadir ID de proyecto y credenciales a la secuencia de comandos

  1. En la secuencia de comandos, asigna el ID del proyecto a la variable de entorno GOOGLE_CLOUD_PROJECT.
  2. Asigna el valor service_account.json a la variable de entorno GOOGLE_APPLICATION_CREDENTIALS.

Ejecutar la secuencia de comandos de forma local

En el subdirectorio del proyecto python-docs-samples/iot/api-client/mqtt_example, ejecuta la secuencia de comandos invocando el comando:

python gateway_demo.py

Cuando la secuencia de comandos se ejecuta, escribe el resultado en el terminal.

Guía de Gateway_demo.py

En esta sección se describe lo que ocurre en cada paso de la secuencia de comandos.

Primero, la secuencia de comandos crea un registro temporal de dispositivos que incluirá la pasarela y el dispositivo que usa la pasarela para comunicarse con Cloud IoT Core:

Python

print("Creating registry: {}".format(registry_id))
manager.create_registry(
    service_account_json, project_id, cloud_region, pubsub_topic, registry_id
)

La secuencia de comandos llama al siguiente código de la muestra de administración de registro:

Python

# project_id = 'YOUR_PROJECT_ID'
# cloud_region = 'us-central1'
# pubsub_topic = 'your-pubsub-topic'
# registry_id = 'your-registry-id'
client = iot_v1.DeviceManagerClient()
parent = f"projects/{project_id}/locations/{cloud_region}"

if not pubsub_topic.startswith("projects/"):
    pubsub_topic = "projects/{}/topics/{}".format(project_id, pubsub_topic)

body = {
    "event_notification_configs": [{"pubsub_topic_name": pubsub_topic}],
    "id": registry_id,
}

try:
    response = client.create_device_registry(
        request={"parent": parent, "device_registry": body}
    )
    print("Created registry")
    return response
except HttpError:
    print("Error, registry not created")
    raise
except AlreadyExists:
    print("Error, registry already exists")
    raise

A continuación, la secuencia de comandos crea una pasarela y la añade al registro:

Python

print("Creating gateway: {}".format(gateway_id))
manager.create_gateway(
    service_account_json,
    project_id,
    cloud_region,
    registry_id,
    None,
    gateway_id,
    rsa_cert_path,
    "RS256",
)

La secuencia de comandos llama al siguiente código de la muestra de administración de dispositivo:

Python

# project_id = 'YOUR_PROJECT_ID'
# cloud_region = 'us-central1'
# registry_id = 'your-registry-id'
# device_id = 'your-device-id'
# gateway_id = 'your-gateway-id'
# certificate_file = 'path/to/certificate.pem'
# algorithm = 'ES256'
# Check that the gateway doesn't already exist
exists = False
client = iot_v1.DeviceManagerClient()

parent = client.registry_path(project_id, cloud_region, registry_id)
devices = list(client.list_devices(request={"parent": parent}))

for device in devices:
    if device.id == gateway_id:
        exists = True
    print(
        "Device: {} : {} : {} : {}".format(
            device.id, device.num_id, device.config, device.gateway_config
        )
    )

with io.open(certificate_file) as f:
    certificate = f.read()

if algorithm == "ES256":
    certificate_format = iot_v1.PublicKeyFormat.ES256_PEM
else:
    certificate_format = iot_v1.PublicKeyFormat.RSA_X509_PEM

# TODO: Auth type
device_template = {
    "id": gateway_id,
    "credentials": [
        {"public_key": {"format": certificate_format, "key": certificate}}
    ],
    "gateway_config": {
        "gateway_type": iot_v1.GatewayType.GATEWAY,
        "gateway_auth_method": iot_v1.GatewayAuthMethod.ASSOCIATION_ONLY,
    },
}

if not exists:
    res = client.create_device(
        request={"parent": parent, "device": device_template}
    )
    print("Created Gateway {}".format(res))
else:
    print("Gateway exists, skipping")

Ahora, la secuencia de comandos crea un dispositivo en el mismo registro que la pasarela:

Python

print("Creating device to bind: {}".format(device_id))
manager.create_device(
    service_account_json, project_id, cloud_region, registry_id, device_id
)

La secuencia de comandos llama al siguiente código del ejemplo de administrador de dispositivos:

Python

# project_id = 'YOUR_PROJECT_ID'
# cloud_region = 'us-central1'
# registry_id = 'your-registry-id'
# device_id = 'your-device-id'

# Check that the device doesn't already exist
client = iot_v1.DeviceManagerClient()

exists = False

parent = client.registry_path(project_id, cloud_region, registry_id)

devices = list(client.list_devices(request={"parent": parent}))

for device in devices:
    if device.id == device_id:
        exists = True

# Create the device
device_template = {
    "id": device_id,
    "gateway_config": {
        "gateway_type": iot_v1.GatewayType.NON_GATEWAY,
        "gateway_auth_method": iot_v1.GatewayAuthMethod.ASSOCIATION_ONLY,
    },
}

if not exists:
    res = client.create_device(
        request={"parent": parent, "device": device_template}
    )
    print("Created Device {}".format(res))
else:
    print("Device exists, skipping")

Una vez que el dispositivo y la pasarela estén en el registro de demostración, la secuencia de comandos podrá vincularlo a la pasarela. Al vincular el dispositivo, la pasarela podrá vincularse a la conexión de la pasarela al puente de protocolos de Cloud IoT Core y desvincularlo.

Python

print("Binding device")
manager.bind_device_to_gateway(
    service_account_json,
    project_id,
    cloud_region,
    registry_id,
    device_id,
    gateway_id,
)

La secuencia de comandos llama al siguiente código del ejemplo de administrador de dispositivos:

Python

# project_id = 'YOUR_PROJECT_ID'
# cloud_region = 'us-central1'
# registry_id = 'your-registry-id'
# device_id = 'your-device-id'
# gateway_id = 'your-gateway-id'
client = iot_v1.DeviceManagerClient()

create_device(
    service_account_json, project_id, cloud_region, registry_id, device_id
)

parent = client.registry_path(project_id, cloud_region, registry_id)

res = client.bind_device_to_gateway(
    request={"parent": parent, "gateway_id": gateway_id, "device_id": device_id}
)

print("Device Bound! {}".format(res))

La secuencia de comandos se detiene cuando el dispositivo está vinculado a la pasarela. Puedes configurar los datos de configuración de la pasarela o del dispositivo vinculado.

Para definir los datos de configuración, sigue estos pasos:

  1. Ve a la consola de Google Cloud.
  2. Define los datos de configuración de la pasarela o del dispositivo vinculado.
  3. Verifique que la pasarela y el dispositivo estén recibiendo la configuración más reciente antes de que empiece a detectarla.

Una vez que hayas configurado los datos de configuración y continúes con la secuencia de comandos, la pasarela se conecta al puente de protocolos de Cloud IoT Core, asocia el dispositivo y recibe mensajes de configuración del dispositivo.

Para procesar los mensajes de configuración, la secuencia de comandos llama a una función auxiliar del ejemplo MQTT de la siguiente manera:

Python

print("Listening for messages for {} seconds".format(listen_time))
print("Try setting configuration in: ")
print("\t{}".format(edit_template.format(registry_id, project_id)))
try:
    input("Press enter to continue")
except SyntaxError:
    pass

def log_callback(client):
    def log_on_message(unused_client, unused_userdata, message):
        if not os.path.exists(log_path):
            with open(log_path, "w") as csvfile:
                logwriter = csv.writer(csvfile, dialect="excel")
                logwriter.writerow(["time", "topic", "data"])

        with open(log_path, "a") as csvfile:
            logwriter = csv.writer(csvfile, dialect="excel")
            logwriter.writerow(
                [
                    datetime.datetime.now().isoformat(),
                    message.topic,
                    message.payload,
                ]
            )

    client.on_message = log_on_message

cloudiot_mqtt_example.listen_for_messages(
    service_account_json,
    project_id,
    cloud_region,
    registry_id,
    device_id,
    gateway_id,
    num_messages,
    rsa_private_path,
    "RS256",
    ca_cert_path,
    mqtt_bridge_hostname,
    mqtt_bridge_port,
    jwt_exp_time,
    listen_time,
    log_callback,
)

La secuencia de comandos llama al siguiente código del ejemplo de MQTT:

Python

global minimum_backoff_time

jwt_iat = datetime.datetime.utcnow()
jwt_exp_mins = jwt_expires_minutes
# Use gateway to connect to server
client = get_client(
    project_id,
    cloud_region,
    registry_id,
    gateway_id,
    private_key_file,
    algorithm,
    ca_certs,
    mqtt_bridge_hostname,
    mqtt_bridge_port,
)

attach_device(client, device_id, "")
print("Waiting for device to attach.")
time.sleep(5)

# The topic devices receive configuration updates on.
device_config_topic = "/devices/{}/config".format(device_id)
client.subscribe(device_config_topic, qos=1)

# The topic gateways receive configuration updates on.
gateway_config_topic = "/devices/{}/config".format(gateway_id)
client.subscribe(gateway_config_topic, qos=1)

# The topic gateways receive error updates on. QoS must be 0.
error_topic = "/devices/{}/errors".format(gateway_id)
client.subscribe(error_topic, qos=0)

# Wait for about a minute for config messages.
for i in range(1, duration):
    client.loop()
    if cb is not None:
        cb(client)

    if should_backoff:
        # If backoff time is too large, give up.
        if minimum_backoff_time > MAXIMUM_BACKOFF_TIME:
            print("Exceeded maximum backoff time. Giving up.")
            break

        delay = minimum_backoff_time + random.randint(0, 1000) / 1000.0
        time.sleep(delay)
        minimum_backoff_time *= 2
        client.connect(mqtt_bridge_hostname, mqtt_bridge_port)

    seconds_since_issue = (datetime.datetime.utcnow() - jwt_iat).seconds
    if seconds_since_issue > 60 * jwt_exp_mins:
        print("Refreshing token after {}s".format(seconds_since_issue))
        jwt_iat = datetime.datetime.utcnow()
        client.loop()
        client.disconnect()
        client = get_client(
            project_id,
            cloud_region,
            registry_id,
            gateway_id,
            private_key_file,
            algorithm,
            ca_certs,
            mqtt_bridge_hostname,
            mqtt_bridge_port,
        )

    time.sleep(1)

detach_device(client, device_id)

print("Finished.")

Cuando la secuencia de comandos se ejecuta, escribe los mensajes de configuración en un registro que se puede poner en cola para transmitir las configuraciones a los dispositivos vinculados.

Después de demostrar cómo puedes recibir mensajes de configuración en nombre de una pasarela y de su dispositivo vinculado, la secuencia de comandos envía datos de estado en nombre del dispositivo vinculado. Para ello, la secuencia de comandos llama a una función de ayuda del ejemplo de MQTT:

Python

print("Publishing messages demo")
print("Publishing: {} messages".format(num_messages))
cloudiot_mqtt_example.send_data_from_bound_device(
    service_account_json,
    project_id,
    cloud_region,
    registry_id,
    device_id,
    gateway_id,
    num_messages,
    rsa_private_path,
    "RS256",
    ca_cert_path,
    mqtt_bridge_hostname,
    mqtt_bridge_port,
    jwt_exp_time,
    "Hello from gateway_demo.py",
)

print("You can read the state messages for your device at this URL:")
print("\t{}".format(device_url_template).format(registry_id, device_id, project_id))
try:
    input("Press enter to continue after reading the messages.")
except SyntaxError:
    pass

La secuencia de comandos llama al siguiente código del ejemplo de MQTT:

Python

global minimum_backoff_time

# Publish device events and gateway state.
device_topic = "/devices/{}/{}".format(device_id, "state")
gateway_topic = "/devices/{}/{}".format(gateway_id, "state")

jwt_iat = datetime.datetime.utcnow()
jwt_exp_mins = jwt_expires_minutes
# Use gateway to connect to server
client = get_client(
    project_id,
    cloud_region,
    registry_id,
    gateway_id,
    private_key_file,
    algorithm,
    ca_certs,
    mqtt_bridge_hostname,
    mqtt_bridge_port,
)

attach_device(client, device_id, "")
print("Waiting for device to attach.")
time.sleep(5)

# Publish state to gateway topic
gateway_state = "Starting gateway at: {}".format(time.time())
print(gateway_state)
client.publish(gateway_topic, gateway_state)

# Publish num_messages messages to the MQTT bridge
for i in range(1, num_messages + 1):
    client.loop()

    if should_backoff:
        # If backoff time is too large, give up.
        if minimum_backoff_time > MAXIMUM_BACKOFF_TIME:
            print("Exceeded maximum backoff time. Giving up.")
            break

        delay = minimum_backoff_time + random.randint(0, 1000) / 1000.0
        time.sleep(delay)
        minimum_backoff_time *= 2
        client.connect(mqtt_bridge_hostname, mqtt_bridge_port)

    payload = "{}/{}-{}-payload-{}".format(registry_id, gateway_id, device_id, i)

    print(
        "Publishing message {}/{}: '{}' to {}".format(
            i, num_messages, payload, device_topic
        )
    )
    client.publish(device_topic, "{} : {}".format(device_id, payload))

    seconds_since_issue = (datetime.datetime.utcnow() - jwt_iat).seconds
    if seconds_since_issue > 60 * jwt_exp_mins:
        print("Refreshing token after {}s").format(seconds_since_issue)
        jwt_iat = datetime.datetime.utcnow()
        client = get_client(
            project_id,
            cloud_region,
            registry_id,
            gateway_id,
            private_key_file,
            algorithm,
            ca_certs,
            mqtt_bridge_hostname,
            mqtt_bridge_port,
        )

    time.sleep(5)

detach_device(client, device_id)

print("Finished.")

La secuencia de comandos se detiene después de transmitir los datos de estado en nombre del dispositivo vinculado para que puedas acceder a la consola de Cloud para ver los datos de estado antes de que se eliminen el registro, la pasarela y el dispositivo de demostración en el siguiente paso.

Cuando la secuencia de comandos finaliza, se libera la pasarela, el dispositivo y el registro que se asignaron al principio.

Python

# Clean up
manager.unbind_device_from_gateway(
    service_account_json,
    project_id,
    cloud_region,
    registry_id,
    device_id,
    gateway_id,
)
manager.delete_device(
    service_account_json, project_id, cloud_region, registry_id, device_id
)
manager.delete_device(
    service_account_json, project_id, cloud_region, registry_id, gateway_id
)
manager.delete_registry(service_account_json, project_id, cloud_region, registry_id)

El dispositivo se desvincula de la pasarela antes de que se elimine:

Python

# project_id = 'YOUR_PROJECT_ID'
# cloud_region = 'us-central1'
# registry_id = 'your-registry-id'
# device_id = 'your-device-id'
# gateway_id = 'your-gateway-id'
client = iot_v1.DeviceManagerClient()

parent = client.registry_path(project_id, cloud_region, registry_id)

res = client.unbind_device_from_gateway(
    request={"parent": parent, "gateway_id": gateway_id, "device_id": device_id}
)

print("Device unbound: {}".format(res))

La secuencia de comandos llama a una función de ayuda del ejemplo de administrador de dispositivos para eliminar el dispositivo vinculado y la pasarela:

Python

# project_id = 'YOUR_PROJECT_ID'
# cloud_region = 'us-central1'
# registry_id = 'your-registry-id'
# device_id = 'your-device-id'
print("Delete device")
client = iot_v1.DeviceManagerClient()

device_path = client.device_path(project_id, cloud_region, registry_id, device_id)

return client.delete_device(request={"name": device_path})

Por último, la secuencia de comandos llama a una función de ayuda de la muestra de administración del registro para eliminar el registro:

Python

# project_id = 'YOUR_PROJECT_ID'
# cloud_region = 'us-central1'
# registry_id = 'your-registry-id'
print("Delete registry")

client = iot_v1.DeviceManagerClient()
registry_path = client.registry_path(project_id, cloud_region, registry_id)

try:
    client.delete_device_registry(request={"name": registry_path})
    print("Deleted registry")
    return "Registry deleted"
except HttpError:
    print("Error, registry not deleted")
    raise