I want to connect my KaaIOT cloud and subscribe a topic to show result from terminal. I do not know where to get the topic name to subscribe with. I had read the KaaIOT documentation but still cannot have a clear idea on it. Could someone help me with a sample code for me to reference?
KaaIOT Information
appVersion.name: c184ijqrqa51q5haskp0-v1
appVersion.registeredDate: 2021-03-16T05:59:54.185Z
createdDate: 2021-03-16T05:59:54.186Z
endpointId: fc2c5833-77c5-445a-89a0-9b0e7498c048
model: Raspberry Pi (192.168.0.171)
metadataUpdatedDate: 2021-03-17T09:13:01.809Z
Sample Code
import itertools
import json
import queue
import random
import string
import sys
import time
import paho.mqtt.client as mqtt
KPC_HOST = "mqtt.cloud.kaaiot.com" # Kaa Cloud plain MQTT host
KPC_PORT = 1883 # Kaa Cloud plain MQTT port
APPLICATION_VERSION = "" # Paste your application version
ENDPOINT_TOKEN = "" # Paste your endpoint token
class MetadataClient:
def __init__(self, client):
self.client = client
self.metadata_by_request_id = {}
self.global_request_id = itertools.count()
get_metadata_subscribe_topic = f'kp1/{APPLICATION_VERSION}/epmx/{ENDPOINT_TOKEN}/get/#'
self.client.message_callback_add(get_metadata_subscribe_topic, self.handle_metadata)
def handle_metadata(self, client, userdata, message):
request_id = int(message.topic.split('/')[-2])
if message.topic.split('/')[-1] == 'status' and request_id in self.metadata_by_request_id:
print(f'<--- Received metadata response on topic {message.topic}')
metadata_queue = self.metadata_by_request_id[request_id]
metadata_queue.put_nowait(message.payload)
else:
print(f'<--- Received bad metadata response on topic {message.topic}:\n{str(message.payload.decode("utf-8"))}')
def get_metadata(self):
request_id = next(self.global_request_id)
get_metadata_publish_topic = f'kp1/{APPLICATION_VERSION}/epmx/{ENDPOINT_TOKEN}/get/{request_id}'
metadata_queue = queue.Queue()
self.metadata_by_request_id[request_id] = metadata_queue
print(f'---> Requesting metadata by topic {get_metadata_publish_topic}')
self.client.publish(topic=get_metadata_publish_topic, payload=json.dumps({}))
try:
metadata = metadata_queue.get(True, 5)
del self.metadata_by_request_id[request_id]
return str(metadata.decode("utf-8"))
except queue.Empty:
print('Timed out waiting for metadata response from server')
sys.exit()
def patch_metadata_unconfirmed(self, metadata):
partial_metadata_udpate_publish_topic = f'kp1/{APPLICATION_VERSION}/epmx/{ENDPOINT_TOKEN}/update/keys'
print(f'---> Reporting metadata on topic {partial_metadata_udpate_publish_topic}\nwith payload {metadata}')
self.client.publish(topic=partial_metadata_udpate_publish_topic, payload=metadata)
def main():
# Initiate server connection
print(f'Connecting to Kaa server at {KPC_HOST}:{KPC_PORT} using application version {APPLICATION_VERSION} and endpoint token {ENDPOINT_TOKEN}')
client_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6))
client = mqtt.Client(client_id=client_id)
client.connect(KPC_HOST, KPC_PORT, 60)
client.loop_start()
metadata_client = MetadataClient(client)
# Fetch current endpoint metadata attributes
retrieved_metadata = metadata_client.get_metadata()
print(f'Retrieved metadata from server: {retrieved_metadata}')
# Do a partial endpoint metadata update
metadata_to_report = json.dumps({"model": "BFG 9000", "mac": "00-14-22-01-23-45"})
metadata_client.patch_metadata_unconfirmed(metadata_to_report)
time.sleep(5)
client.disconnect()
if __name__ == '__main__':
main()
I have established a communication with EMQX broker by using MQTT protocol. But I don't know about KaaIoT much but this might help you. As I went through your code, I didn't see the part where you have subscribed to the topic(correct me if I am wrong). You can refer this. I have implemented the sub pub model and below is the subscriber code which runs fine for EMQX broker. You can try it for KaaIoT.
import paho.mqtt.client as mqtt
import time
import logging
def on_connect(client, userdata, flags, rc):
logging.info("Connected flags"+str(flags)+"result code " + str(rc)+ "client1_id")
client.connected_flag=True
def on_message(client, userdata, message):
print("Received message: " ,str(message.payload.decode("utf-8")))
def on_disconnect(client, userdata, rc):
if rc != 0:
print("Unexpected MQTT disconnection. Will auto-reconnect")
client = mqtt.Client('''Your client id string''')
client.connect("mqtt.cloud.kaaiot.com", 1883, 60)
client.subscribe('''Your topic name (mentioned where data is published)''',qos=1)
client.on_connect = on_connect
client.on_message=on_message
client.on_disconnect = on_disconnect
client.loop_forever()
Related
I am having trouble using Twilio's MessagingResponse class to send and receive messages using a webhook. I am using flask and ngrok to create a temporary URL to test my app, but I am getting a 502 error and 11200 warning with this new implementation.
I can confirm that the number I am attempting to message back is verified in my Twilio account. Here is the new implementation that uses the Twilio MessagingResponse to create the response message instead of sending it directly using the Twilio REST API:
import os
from flask import Flask, request, session, make_response
from twilio.twiml.messaging_response import MessagingResponse
from twilio.rest import Client
from chatbot import ask, append_interaction_to_chat_log
from secret_key import secret_key
app = Flask(__name__)
app.config['SECRET_KEY'] = secret_key
account_sid = os.environ.get('ACCOUNT_SID')
auth_token = os.environ.get('AUTH_TOKEN')
client = Client(account_sid, auth_token)
#app.route('/bot', methods=['POST'])
def bot():
incoming_msg = request.values['Body']
print(incoming_msg)
chat_log = session.get('chat_log')
answer = ask(incoming_msg, chat_log)
session['chat_log'] = append_interaction_to_chat_log(incoming_msg, answer, chat_log)
r = MessagingResponse()
r.message = answer
return make_response(r)
I have been able to successfully send and receive messages using a message object and explicitly stating the phone number I am sending to using this implementation:
import os
from flask import Flask, request, session
from twilio.twiml.messaging_response import MessagingResponse
from twilio.rest import Client
from chatbot import ask, append_interaction_to_chat_log
from secret_key import secret_key
app = Flask(__name__)
app.config['SECRET_KEY'] = secret_key
account_sid = os.environ.get('ACCOUNT_SID')
auth_token = os.environ.get('AUTH_TOKEN')
client = Client(account_sid, auth_token)
#app.route('/', methods=['POST'])
def bot():
incoming_msg = request.values['Body']
print(incoming_msg)
chat_log = session.get('chat_log')
answer = ask(incoming_msg, chat_log)
session['chat_log'] = append_interaction_to_chat_log(incoming_msg, answer, chat_log)
# use the incoming message to generate the response here
message = client.messages.create(
body=answer,
from_='+12232107883', #Twilio number you purchased or verified
to='+19143182181' # The phone number you want to send the message to
)
print(message.sid)
return 'message sent'
Attached is a photo of my implementation of the ngrok URL to configure the webhook.
Essentially, I am trying to implement the new structures in hopes of creating a more secure and scalable bot. Any ideas here? I realize it could be something in my Twilio settings, but I haven't found the solution. Thank you all.
I thought my problem was from not having an ngrok account:
But that did not seem to resolve the issue:
hope you are doing it right these days.
To summarize my problem, I think this is not working becuase I am using a free Twilio account instead of a paid one. But that's just my beginner theory. Now, the issue:
I have tried an official Twilio tutorial (https://www.twilio.com/blog/automating-ngrok-python-twilio-applications-pyngrok, I shared the link in case someone finds it interesting or needs it), which allows us to automate SMS webhook (sms_url) configuration by using Client (twilio) and pyngrok (ngrok).
def start_ngrok():
from twilio.rest import Client
from pyngrok import ngrok
url = ngrok.connect(5000)
print(' * Tunnel URL:', url)
client = Client()
client.incoming_phone_numbers.list(
phone_number=os.environ.get('TWILIO_PHONE_NUMBER'))[0].update(
sms_url=url + '/bot')
I can't explain all the things that I tried in the last 4 days, with no success. I keep getting the same error:
client.incoming_phone_numbers.list(phone_number=os.environ.get('TWILIO_PHONE_NUMBER'))[0].update(sms_url=url + '/bot')
IndexError: list index out of range
Something is not working with the list, it comes empty, although environment variables are working properly. I will work with just one phone_number, so there no need for list, indeed, so I started to change that line to avoid different errors and ended up with this:
def start_ngrok():
from twilio.rest import Client
from pyngrok import ngrok
url = ngrok.connect(5000)
print(' * Tunnel URL:', url)
client = Client()
client.incoming_phone_numbers("my_number").update(sms_url=str(url) + '/bot')
Then I got the final error that I can't solve by my self:
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__init__.py", line 442, in update
payload = self._version.update(method='POST', uri=self._uri, data=data, )
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/twilio/base/version.py", line 106, in update
raise self.exception(method, uri, response, 'Unable to update record')
twilio.base.exceptions.TwilioRestException:
HTTP Error Your request was:
POST /Accounts/my_account_SID/IncomingPhoneNumbers/+my_number.json
Twilio returned the following information:
Unable to update record: The requested resource /2010-04-01/Accounts/my_account_SID/IncomingPhoneNumbers/+my_number.json was not found
More information may be available here:
https://www.twilio.com/docs/errors/20404
I tried all different phone numbers combinations/formats: nothing works.
Thanks for your time reading all this!
Looks like something changed since the blog was written or there was a mistake.
Try the below:
The only difference is adding .public_url to the url object. Also allowed a GET to /bot for testing.
from dotenv import load_dotenv
from flask import Flask, request
from twilio.twiml.messaging_response import MessagingResponse
load_dotenv()
app = Flask(__name__)
#app.route('/bot', methods=['POST','GET'])
def bot():
user = request.values.get('From', '')
resp = MessagingResponse()
resp.message(f'Hello, {user}, thank you for your message!')
return str(resp)
def start_ngrok():
from twilio.rest import Client
from pyngrok import ngrok
url = ngrok.connect(5000)
print('This is',url)
print(' * Tunnel URL:', url)
client = Client()
client.incoming_phone_numbers.list(
phone_number=os.environ.get('TWILIO_PHONE_NUMBER'))[0].update(
sms_url=url.public_url + '/bot')
if __name__ == '__main__':
if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
start_ngrok()
app.run(debug=True)
I am new to tweepy. And tried to implement an api which can listen the new tweets for a #mention or screen name.
tweets = api.user_timeline(screen_name=username)
But it is not listening the new tweets. Could you please help me to understand how it can be implemented so that whenever a tweet comes with '#mention'. the api should listen immediately
Creating a StreamListener
The on_data method of Tweepy’s StreamListener conveniently passes data from statuses to the on_status method.
Create class MyStreamListener inheriting from StreamListener and overriding on_status.
import tweepy
override tweepy.StreamListener to add logic to on_status
class MyStreamListener(tweepy.StreamListener):
def on_status(self, status):
print(status.text)
Creating a Stream
Need an API to stream with the right credentials for authentications. Once we have an API and a status listener we can create our stream object.
myStreamListener = MyStreamListener()
myStream = tweepy.Stream(auth = api.auth, listener=myStreamListener)
start stream
myStream.filter(track=['python'])
myStream.filter(follow=["2211149702"])
Async stream
myStream.filter(track=['python'], is_async=True)
handling errors
class MyStreamListener(tweepy.StreamListener):
def on_error(self, status_code):
if status_code == 420:
#returning False in on_error disconnects the stream
return False
# returning non-False reconnects the stream, with backoff.
Firstly, get their twitter user id using this.
Then, use this:
from tweepy import OAuthHandler
from tweepy import Stream
from tweepy import StreamListener
listener = StreamListener()
auth = OAuthHandler(config.API_KEY, config.API_SECRET)
auth.set_access_token(config.ACCESS_TOKEN, config.ACCESS_TOKEN_SECRET)
stream = Stream(auth, listener)
stream.filter(follow=['1102105048253849600']) # user ID for random account
def on_data( self, status ):
print("Entered on_data()")
print(status.text, flush = True)
return True
...
user = api.verify_credentials()
# this is just for getting user. without this also connection can be established
if user:
print(user.id)
print('started')
twitter_stream=Stream(auth,StdOutListener())
print('Stream created...')
twitter_stream.filter(follow=[user.id_str], is_async=True)
I try to receive simple text values from external MQTT broker topics with IoT Gateway.
For this purpose I simplify the existing script (extensions/mqtt/custom_mqtt_uplink_converter.py):
from thingsboard_gateway.connectors.mqtt.mqtt_uplink_converter import MqttUplinkConverter, log
class CustomMqttUplinkConverter(MqttUplinkConverter):
def __init__(self, config):
self.__config = config.get('converter')
self.dict_result = {}
def convert(self, topic, body):
try:
log.debug("New data received: %s: %s" % (topic,body))
# if topic = '/devices/buzzer/controls/volume' device name will be 'buzzer'.
self.dict_result["deviceName"] = topic.split("/")[2]
# just hardcode this
self.dict_result["deviceType"] = "buzzer"
self.dict_result["telemetry"] = {"data": body}
log.debug("Result: %s" % (self.dict_result))
return self.dict_result
except ...
When I start gateway I see in his log that he successfully connected and read the values:
INFO ... MQTT Broker Connector connected to 10.1.1.2:1883 - successfully.'
DEBUG ... Client <paho.mqtt.client.Client object at 0x7fb42d19dd68>, userdata None, flags {'session present': 0}, extra_params ()'
DEBUG ... <module 'CustomMqttUplinkConverter' from '/var/lib/thingsboard_gateway/extensions/mqtt/custom_mqtt_uplink_converter.py'>'
DEBUG ... Import CustomMqttUplinkConverter from /var/lib/thingsboard_gateway/extensions/mqtt.'
DEBUG ... Converter CustomMqttUplinkConverter for topic /devices/buzzer/controls/volume - found!'
INFO ... Connector "MQTT Broker Connector" subscribe to /devices/buzzer/controls/volume'
DEBUG ... Received data: {}'
DEBUG ... (None,)'
INFO ... "MQTT Broker Connector" subscription success to topic /devices/buzzer/controls/volume, subscription message id = 1'
DEBUG ... New data received: /devices/buzzer/controls/volume: 66'
DEBUG ... Result: {'deviceName': 'buzzer', 'deviceType': 'buzzer', 'telemetry': {'data': 66}}'
But this values are the last values he can read. If I change volume one broker new values will not appear neither in the log nor in TB UI. (I control updates with mosquitto_sub.)
Seems this converter will never called again until gateway restarted. Is it correct behaveour?
How can I make sure that my code is correct if I don't see the result?
Hi I have tried your version of the custom converter, it didn't work, but when I changed
self.dict_result["telemetry"] = {"data": body}
to
self.dict_result["telemetry"] = [{"data": body}]
It sent data correctly.
The gateway requires an array of telemetry of attributes from the converter.
i have problem to subscribe 2 (and more) ESP8266 to one RPi3 broker.
I used:
import paho.mqtt.client as mqtt
import datetime
mqtt_topics = ["esp8266-1", "esp8266-2"]
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
for topic in mqtt_topics:
client.subscribe(topic)
def on_message(client, userdata, msg):
print(datetime.datetime.now())
print(str(msg.topic)+" "+str(msg.payload))
client = mqtt.Client()
client.on_connect = on_connect
client.connect('localhost', 1883, 60)
try:
for topic in mqtt_topics:
client.on_message = on_message
client.loop_forever()
except KeyboardInterrupt:
print("CTRL-C: Terminating program.")
It works but ony to 1 esp.
If i connect esp8266-1 it checks it values, then i connect esp8266-2 to have its values and esp8266-1 is no longer available (even if i turn of esp8266-2).
How to subscribe both esp8266?
I only can have one of them at time.
The code needs few changes:
import paho.mqtt.client as mqtt
import datetime
mqtt_topics = [("esp8266-1",0), ("esp8266-2",0)]
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
client.subscribe(mqtt_topics)
def on_message(client, userdata, msg):
print(datetime.datetime.now())
print(str(msg.topic)+" "+str(msg.payload))
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect('localhost', 1883, 60)
try:
client.loop_forever()
except KeyboardInterrupt:
print("CTRL-C: Terminating program.")
But if one client works and a second forces the first to disconnect then it sounds like you have the same clientID for both clients. clientIDs need to be unique for all client.