Google Cloud IoT - Single MQTT client instance for all devices in a registry - google-cloud-platform

I am able to publish events to a device in my Cloud IOT Registry via an MQTT client created this way (using paho python):
self.__client = mqtt.Client(client_id='projects/{}/locations/{}/registries/{}/devices/{}'.format(project_id,
cloud_region,
registry_id,
device_id))
Now I'm wondering if I can create an MQTT client being able to publish events to multiple devices by setting the client id at registry level (i.e. not specifying the device id):
self.__client = mqtt.Client(client_id='projects/{}/locations/{}/registries/{}'.format(project_id,
cloud_region,
registry_id))
This client is not able to connect even if I've added a CA Certificate to the registry.
My question is: can a single MQTT Client instance publish events to a set of devices defined in a registry?
Should I use a gateway instead?

No, you can't send messages to a registry like this.
The way you'd want to do this is either 1) Use a gateway like you say, send one message then spread it to the devices locally. Or 2) Grab the list of devices in the registry using the DeviceManagerClient(), and iterate over them each sending each device the message in a loop.
Check out this: https://cloud.google.com/iot/docs/samples/device-manager-samples#list_devices_in_a_registry
For fetching the list of devices in a registry. Snippet for python:
# project_id = 'YOUR_PROJECT_ID'
# cloud_region = 'us-central1'
# registry_id = 'your-registry-id'
print("Listing devices")
client = iot_v1.DeviceManagerClient()
registry_path = client.registry_path(project_id, cloud_region, registry_id)
devices = list(client.list_devices(request={"parent": registry_path}))
for device in devices:
print("Device: {} : {}".format(device.num_id, device.id))
return devices
So in that for device in devices loop you can call your code to get the MQTT client and send the message you want to the specified device.

Related

Connection to Google IoT with Hive MQTT client instead of Paho

I have working code similar to this connecting to google IoT with the paho client.
Since I am in a spring boot reactive application, I would like to use Hive MQTT Client, but I can't find the right setup, I keep having the following error message :
com.hivemq.client.mqtt.exceptions.ConnectionClosedException: Server closed connection without DISCONNECT.
The current code I use :
hiveClient = MqttClient.builder()
.identifier(UUID.randomUUID().toString())
.serverHost("mqtt.googleapis.com")
.serverPort(443)
.useMqttVersion3()
.sslWithDefaultConfig()
.simpleAuth(
Mqtt3SimpleAuth.builder()
.username("unused")
.password(StandardCharsets.UTF_8.encode("// a token string generation that works fine with palo"))
.build()
)
.build()
.toBlocking();
hiveClient.connect(); // Error
It looks like the identifier (client ID) should be set to something other than a UUID. The documentation indicates the client ID should be formed as the following path:
projects/PROJECT_ID/locations/REGION/registries/REGISTRY_ID/devices/DEVICE_ID
Note that all of the requirements for the Google Cloud IoT Core MQTT device bridge are strict, so also verify that Hive is configured as follows:
Mqtt 3.1.1
TLS 1.2
Publish to /devices/DEVICE_ID/events or /devices/DEVICE_ID/state
Subscribe to /devices/DEVICE_ID/config or /devices/DEVICE_ID/commands/#
QoS 0 or 1
Note that if you do not adhere to the requirements, your device gets disconnected. Additional information on the disconnect reason may be available in the logging for your registry visible on the Cloud Console for IoT.

Sending UID / Device ID via Greengrass

I'm running several Greengrass Cores and they send Data to a MQTT Stream.
I deployed a Lambda on GGC reading the SerialPort coming in and push it to the Stream.
But now I want to check which device is sending the Data - I tried this one to check out the hostname
import socket
host = socket.gethostname()
but the core sends the value "sandbox" so i think the lambda isn't authorized to read the host name.
The SDK has no Documentation for this:
https://github.com/aws/aws-greengrass-core-sdk-python
I want to push the data to a mqqt stream like this:
response = client.publish(
topic='customer/events/{DEVICE-ID or UID or ARN}/',
payload=jsonData.encode())
I found something useful in another AWS Python Example - ThingNames are registered in the System Env so you can import OS and get the ThingName like this:
import os
device = os.environ['AWS_IOT_THING_NAME']

Google Cloud IOT, block communication via API

I'd like to block communication with a device in a registry in Google Cloud IOT.
The gcloud command that is used to block communication: https://cloud.google.com/iot/docs/gcloud-examples#block_or_allow_communication_from_a_device
The Patch API doesn't make it clear how one can block communication of a device using the API
So how is this achieved?
There is an example snippet for patching a device available that may be helpful for you.
Instead of sending a EC value in the patch body, you could update the device to have communication blocked.
In Python, you would do this as:
client = get_client(service_account_json)
registry_path = 'projects/{}/locations/{}/registries/{}'.format(
project_id, cloud_region, registry_id)
patch = {
'blocked': 'True'
}
device_name = '{}/devices/{}'.format(registry_path, device_id)
return client.projects().locations().registries().devices().patch(
name=device_name, updateMask='blocked', body=patch).execute()

Is it possible to connect to the Google IOTCore MQTT Bridge via Javascript?

I've been trying to use the javacscript version of the Eclipse Paho MQTT client to access the Google IOTCore MQTT Bridge, as suggested here:
https://cloud.google.com/iot/docs/how-tos/mqtt-bridge
However, whatever I do, any attempt to connect with known good credentials (working with other clients) results in this connection error:
errorCode: 7, errorMessage: "AMQJS0007E Socket error:undefined."
Not much to go on there, so I'm wondering if anyone has ever been successful connecting to the MQTT Bridge via Javascript with Eclipse Paho, the client implementation suggested by Google in their documentation.
I've gone through their troubleshooting steps, and things seem to be on the up and up, so no help there either.
https://cloud.google.com/iot/docs/troubleshooting
I have noticed that in their docs they have sample code for Java/Python, etc, but not Javascript, so I'm wondering if it's simply not supported and their documentation just fails to mention as such.
I've simplified my code to just use the 'Hello World' example in the Paho documentation, and as far as I can tell I've done things correctly (including using my device path as the ClientID, the JWT token as the password, specifying an 'unused' userName field and explicitly requiring MQTT v3.1.1).
In the meantime I'm falling back to polling via their HTTP bridge, but that has obvious latency and network traffic shortcomings.
// Create a client instance
client = new Paho.MQTT.Client("mqtt.googleapis.com", Number(8883), "projects/[my-project-id]/locations/us-central1/registries/[my registry name]/devices/[my device id]");
// set callback handlers
client.onConnectionLost = onConnectionLost;
client.onMessageArrived = onMessageArrived;
// connect the client
client.connect({
mqttVersion: 4, // maps to MQTT V3.1.1, required by IOTCore
onSuccess:onConnect,
onFailure: onFailure,
userName: 'unused', // suggested by Google for this field
password: '[My Confirmed Working JWT Token]' // working JWT token
function onFailure(resp) {
console.log(resp);
}
// called when the client connects
function onConnect() {
// Once a connection has been made, make a subscription and send a message.
console.log("onConnect");
client.subscribe("World");
message = new Paho.MQTT.Message("Hello");
message.destinationName = "World";
client.send(message);
}
// called when the client loses its connection
function onConnectionLost(responseObject) {
if (responseObject.errorCode !== 0) {
console.log("onConnectionLost:"+responseObject.errorMessage);
}
}
// called when a message arrives
function onMessageArrived(message) {
console.log("onMessageArrived:"+message.payloadString);
}
I'm a Googler (but I don't work in Cloud IoT).
Your code looks good to me and it should work. I will try it for myself this evening or tomorrow and report back to you.
I've spent the past day working on a Golang version of the samples published on Google's documentation. Like you, I was disappointed to not see all Google's regular languages covered by samples.
Are you running the code from a browser or is it running on Node.JS?
Do you have a package.json (if Node) that you would share too please?
Update
Here's a Node.JS (JavaScript but non-browser) that connects to Cloud IoT, subscribes to /devices/${DEVICE}/config and publishes to /devices/${DEVICE}/events.
https://gist.github.com/DazWilkin/65ad8890d5f58eae9612632d594af2de
Place all the files in the same directory
Replace values in index.js of the location of Google's CA and your key
Replaces [[YOUR-X]] values in config.json
Use "npm install" to pull the packages
Use node index.js
You should be able to pull messages from the Pub/Sub subscription and you should be able to send config messages to the device.
Short answer is no. Google Cloud IoT Core doesn't support WebSockets.
All the JavaScript MQTT libraries use WebSocket because JavaScript is restricted to perform HTTP requests and WebSocket connections only.

Repeated messages mosquitto MQTT broker to amazon IoT Service using persistence

I have a Raspberry with a mosquitto broker on it and bridged with amazon IoT service. https://aws.amazon.com/es/blogs/iot/how-to-bridge-mosquitto-mqtt-broker-to-aws-iot/
This is my mosquitto.conf file:
# Place your local configuration in /etc/mosquitto/conf.d/
#
# A full description of the configuration file is at
# /usr/share/doc/mosquitto/examples/mosquitto.conf.example
pid_file /var/run/mosquitto.pid
persistence true
persistence_location /var/lib/mosquitto/
log_dest file /var/log/mosquitto/mosquitto.log
include_dir /etc/mosquitto/conf.d
This is the bridge.conf that is inside the /etc/mosquitto/conf.d
# =================================================================
# Bridges to AWS IOT
# =================================================================
# AWS IoT endpoint, use AWS CLI 'aws iot describe-endpoint'
connection awsiot
address xxxxxxxxx.iot.eu-central-1.amazonaws.com:8883
# Specifying which topics are bridged
topic awsiot_to_localgateway in 1
topic localgateway_to_awsiot/iot out 1
topic both_directions both 1
# Setting protocol version explicitly
bridge_protocol_version mqttv311
bridge_insecure false
# Bridge connection name and MQTT client Id,
# enabling the connection automatically when the broker starts.
cleansession true
clientid bridgeawsiot
start_type automatic
notifications false
log_type all
# =================================================================
# Certificate based SSL/TLS support
# -----------------------------------------------------------------
#Path to the rootCA
bridge_cafile /etc/mosquitto/certs/rootCA.pem
# Path to the PEM encoded client certificate
bridge_certfile /etc/mosquitto/certs/cert.crt
# Path to the PEM encoded client private key
bridge_keyfile /etc/mosquitto/certs/private.key
All works fine. But, If I remove the ethernet cable to test the pesistence. When the comunications are reestablished. The broker send repeated messages to the amazon IoT service.
This is the message that I'm sending
char dataToSend[] = "Message Id: ";
counter++;
snprintf(dataToSend, sizeof(dataToSend) + 10, "Message Id: %d", counter);
app_mqtt_publish(&dataToSend);
Is it a normal behaviour?
The short version of (part of) the MQTT spec:
QOS 0 - > Messages may be delivered
QOS 1 -> Messages will be delivered at least once
QOS 2 -> Messages will be delivered once and only once.
So if messages have not been acknowledged then there is a chance that QOS 1 messages will be delivered again. They should have the DUP flag set in the header so the receiving broker should know they may have been delivered already.
IIRC AWS-IoT doesn't support QOS 2 so you may have just put up with this.
AWS IoT doesn't support cleansession false. The consequence for the bridge is that, when:
you have an error sending a message
and have persistence true
and didn't effectually disconnect
and the QoS > 0 or even QoS = 0 if queue_qos0_messages true
=> messages are saved in db.
But if the client automatically set to the bridge effectually disconnects, the cleansession true tells the broker to do not persist the data, so it clears the db for this client.
Wish that AWS IoT didn't disconnect when you send cleansession false, so we could keep this local cache...
ps: The "client automatically set to the bridge" is that, when you set a bridge between two brokers, a client is created to subscribe to one and always publish to the other.