0

I'm currently trying to send a telemetry message via AMPQ Adaptor to the Hono Sandbox. All though i took over parts of the code sample seen in Hono Noth bridge example (which should work for the south bridge as well) I struggle a bit with the SASL as it seems.

Here is my code

from __future__ import print_function, unicode_literals
from proton import Message
from proton.handlers import MessagingHandler
from proton.reactor import Container

tenantId = 'xxxx'
deviceId = 'yyyyy'
devicePassword = 'my-secret-password'


class AmqpMessageSender(MessagingHandler):
    def __init__(self, server, address):
        super(AmqpMessageSender, self).__init__()
        self.server = server
        self.address = address

    def on_start(self, event):
        conn = event.container.connect(
            self.server,
            sasl_enabled=True,
            allowed_mechs="PLAIN",
            allow_insecure_mechs=True,
            user=f'{deviceId}@{tenantId}',
            password=devicePassword
        )    
        event.container.create_sender(conn, self.address)

    def on_sendable(self, event):   
        msg = Message(
            address=f'{self.address}/{deviceId}',
            content_type='application/json',
            body={"temp": 5, "transport": "amqp"}
        )
        event.sender.send(self.msg)
        event.sender.close()

    def on_connection_error(self, event):
        print("Connection Error")

    def on_link_error(self, event):
        print("Link Error")

    def on_transport_error(self, event):
        print("Transport Error")


Container(AmqpMessageSender(f'amqp://hono.eclipseprojects.io:5671', f'telemetry/{tenantId}')).run()

If I run the code, I get a transport error with the context condition

'Expected SASL protocol header: no protocol header found (connection aborted)'

I tried also with port 5672 which got me a link error and using port 15672 (which actually is the north bridge port) which - to my surprise, didn't cause a SASL error but got me the expected "not authorized" error (as the device is not allowed to connect via the north bridge)

======= update=======

once more thank you for you time.

regarding a) since comments are rather limited here once agian the code as a answer to question. The code i use to simulate the device is as follwoing

from __future__ import print_function, unicode_literals
from proton import Message
from proton.handlers import MessagingHandler
from proton.reactor import Container

tenantId = 'xxx'
deviceId = 'yyy'
devicePassword = 'my-secret-password'


class AmqpMessageSender(MessagingHandler):
    def __init__(self, server):
        super(AmqpMessageSender, self).__init__()
        self.server = server

    def on_start(self, event):
        print("In start")
        conn = event.container.connect(
            self.server,
            sasl_enabled=True,
            allowed_mechs="PLAIN",
            allow_insecure_mechs=True,
            user=f'{deviceId}@{tenantId}',
            password=devicePassword
        )
        print("connection established")
        event.container.create_sender(context=conn, target=None)
        print("sender created")

    def on_sendable(self, event):
        print("In Msg send")
        event.sender.send(Message(
            address=f'telemetry',
            properties={
                'to': 'telemetry',
                'content-type': 'application/json'
            },
            content_type='application/json',
            body={"temp": 5, "transport": "amqp"}
        )) 
        event.sender.close()
        event.connection.close()
        print("Sender & connection closed")

    def on_connection_error(self, event):
        print("Connection Error")

    def on_link_error(self, event):
        print("Link Error")

    def on_transport_error(self, event):
        print("Transport Error")

Container(AmqpMessageSender(f'amqp://hono.eclipseprojects.io:5672')).run()

To simulate a server I do not use the java client, but use the sample code from the python quick start example as well. I have also a client class that does the http call as in the python quick start example an to that the server class reacts and prints the message - so the server implementation as outlined below should be ok from my understanding:

from __future__ import print_function, unicode_literals
import threading
import time
from proton.handlers import MessagingHandler
from proton.reactor import Container

amqpNetworkIp = "hono.eclipseprojects.io"
tenantId = 'xxx'


class AmqpReceiver(MessagingHandler):
    def __init__(self, server, address, name):
        super(AmqpReceiver, self).__init__()
        self.server = server
        self.address = address
        self._name = name

    def on_start(self, event):
        conn = event.container.connect(self.server, user="consumer@HONO", password="verysecret")
        event.container.create_receiver(conn, self.address)

    def on_connection_error(self, event):
        print("Connection Error")

    def on_link_error(self, event):
        print("Link Error")

    def on_message(self, event):
        print(self._name)
        print("Got a message:")
        print(event.message.body)


class CentralServer:
    def listen_telemetry(self, name):
        uri = f'amqp://{amqpNetworkIp}:15672'
        address = f'telemetry/{tenantId}'
        self.container = Container(AmqpReceiver(uri, address, name))

        print("Starting (northbound) AMQP Connection...")
        self.thread = threading.Thread(target=lambda: self.container.run(), daemon=True)
        self.thread.start()
        time.sleep(2)

    def stop(self):
        # Stop container
        print("Stopping (northbound) AMQP Connection...")
        self.container.stop()
        self.thread.join(timeout=5)


CentralServer().listen_telemetry('cs1')

after another day trying i couldn't find what i do wrong i really hope you see where i miss something :)

br Armin

1 Answers1

0

The AMQP protocol adapter requires devices to send messages via an anonymous terminus.

In your code, this means that the on_start method needs to be changed to contain event.container.create_sender(context=conn, target=None).

In any case, the non-TLS port of the AMQP adapter is 5672, so you should use amqp://hono.eclipseprojects.io:5672 as the server address. The second parameter to the constructor (telemetry) is irrelevant and can be removed.

Also make sure that you have a consumer running for your tenant. Otherwise, the sender will not get any credits for actually sending messages ...

Edited Oct. 21st, 2021

This code works for me ...

class AmqpMessageSender(MessagingHandler):
    def __init__(self, server):
        super(AmqpMessageSender, self).__init__()
        self.server = server

    def on_start(self, event):
        print("In start")
        conn = event.container.connect(
            url=self.server,
            sasl_enabled=True,
            allowed_mechs="PLAIN",
            allow_insecure_mechs=True,
            user=f'{deviceId}@{tenantId}',
            password=devicePassword
        )
        print("connection established")
        event.container.create_sender(context=conn, target=None)
        print("sender created")

    def on_sendable(self, event):
        print("In Msg send")
        event.sender.send(Message(
            address=f'telemetry',
            content_type='application/json',
            body="{\"temp\": 5, \"transport\": \"amqp\"}"
        )) 
        event.sender.close()
        event.connection.close()
        print("Sender & connection closed")

    def on_connection_error(self, event):
        print("Connection Error")

    def on_link_error(self, event):
        print("Link Error")

    def on_transport_error(self, event):
        print("Transport Error")
        print(event)

Container(AmqpMessageSender(f'amqp://hono.eclipseprojects.io:5672')).run()
Kai Hudalla
  • 826
  • 1
  • 5
  • 7
  • Hello Kai, first of all thank you for your reply. The main issue seems to be resolved, i do not get any connection errors anymore. However the messages don't seem to go through as the receiver doesn't get any message (Using the quickstart example from the project page i was able to send and receive messages via the http adaptor). If i would have to bet i would say i do something wrong with the addressing. here my new code after the adjustments. I tried to follow the instructions as given in [authenticated device](https://www.eclipse.org/hono/docs/dev/user-guide/amqp-adapter/) – Armin Gruber Oct 18 '21 at 20:13
  • def on_start(self, event): ... event.container.create_sender(context=conn, target=None) def on_sendable(self, event): event.sender.send(Message( address=f'telemetry', properties={ 'to': 'telemetry', }, content_type='application/json', body={"temp": 5, "transport": "amqp"} )) event.sender.close() event.connection.close() – Armin Gruber Oct 18 '21 at 20:25
  • Can you a) post the full code you are using and b) describe the set up you are using, i.e. have you registered your own tenant, device, credentials? Are you running the Hono command line client to consume the messages? If so, what's the command line being used? – Kai Hudalla Oct 20 '21 at 08:43
  • please see the answer i posted (as comments are limited to 600 characters) – Armin Gruber Oct 20 '21 at 21:00
  • Hello Kai, thank you very very much. The body must be a string, this did the trick! looking forward now to proceed with hono which is - an awesome framework for IoT, very well designed :) – Armin Gruber Oct 23 '21 at 16:12
  • Thanks, @ArminGruber, would you mind sharing some information about the context you are using Hono in? – Kai Hudalla Oct 25 '21 at 11:28
  • Foremost we are trying to capture machine sensors and environment sensors within the factory. however as they do not speak "the hono protocol", require a gateway (I already figured out as well how to do that). However understanding the concept of the command pattern I already see use cases on the horizon to use them as well (in the most simple case, turn on a warning lamp if a machine run below planned speed)... there however i still have struggle to get this working, maybe something for another stackoverflow question ;) – Armin Gruber Oct 26 '21 at 16:51
  • Maybe I add some more context so that the use of hono makes sense. we have multiple factories with a similar setup and one central business application host. while the sensors talk on modbus and the like, the communication to the central business application and the organisation of the sensors in different tenants (one per factory) will now be preferably be implemented via hono (at least that's the idea). – Armin Gruber Oct 26 '21 at 17:33