AWSIoT: AWSIoTShadowClient vs AWSIoTMQTTClient - javascript

I have an IoT Project which is:
2 bulbs, connect to 2 Raspberry Pi (python)
web app (Javascript) with 3 buttons: one to turn on/off bulb 1, one to turn on/off bulb 2, one to turn on/off both bulbs.
I approached 2 different ways
using AWSIoTMQTTClient:
Pi:
class CallbackContainer(object):
def __init__(self, client):
self._client = client
def messagePrint(self, client, userdata, message):
print("Received a new message: ")
print(message.payload)
print("from topic: ")
print(message.topic)
print("--------------\n\n")
myAWSIoTMQTTClient = AWSIoTMQTTClient("myClientID")
myAWSIoTMQTTClient.configureEndpoint("xxxxx.iot.eu-west-1.amazonaws.com", 8883)
myAWSIoTMQTTClient.configureCredentials("./certs/rootCA.pem", "./certs/xxxxxxx-private.pem.key", "./certs/xxxxxxx-certificate.pem.crt")
myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10) # 10 sec
myAWSIoTMQTTClient.configureMQTTOperationTimeout(5) # 5 sec
myCallbackContainer = CallbackContainer(myAWSIoTMQTTClient)
myAWSIoTMQTTClient.connect()
myAWSIoTMQTTClient.subscribe("topic_both", 0, myCallbackContainer.messagePrint)
myAWSIoTMQTTClient.subscribe("topic_bulb1", 0, myCallbackContainer.messagePrint)
while True:
time.sleep(1)
Javascript:
var params = {
payload: JSON.stringify(body),
topic: myTopic, //"topic_both" or "topic_bulb1"
qos: 0
};
var iotPromise = iotData.publish(params).promise();
using AWSIoTShadowClient:
Pi:
def customShadowCallback_Delta(payload, responseStatus, token):
print(responseStatus)
payloadDict = json.loads(payload)
print("++++++++DELTA++++++++++")
print("property: " + str(payloadDict["state"]))
print("+++++++++++++++++++++++\n\n")
#Need to handle JSON to control bulbs
thingName = "control_bulb"
myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("myClientID")
myAWSIoTMQTTShadowClient.configureEndpoint("xxxxxx.iot.eu-west-1.amazonaws.com", 8883)
myAWSIoTMQTTShadowClient.configureCredentials("/certs/rootCA.pem", "/certs/xxxxx-private.pem.key", "/certs/xxxxx-certificate.pem.crt")
myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(10) # 10 sec
myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5) # 5 sec
myAWSIoTMQTTShadowClient.connect()
deviceShadowHandler = myAWSIoTMQTTShadowClient.createShadowHandlerWithName(thingName, True)
deviceShadowHandler.shadowRegisterDeltaCallback(customShadowCallback_Delta)
while True:
time.sleep(1)
JavaScript:
var params = {
payload: '{"state":{"desired":' + JSON.stringify(body) + '}}',
thingName: 'control_bulb'
};
iotData.updateThingShadow(params, function(err, data) {
}
Both approaches manage to achieve the purpose of the project. However, my questions :
What different between AWSIoTMQTTClient vs AWSIoTShadowClient in terms of performance, security, maintenance?
In which use-case, is AWSIoTShadowClient or AWSIoTMQTTClient used?
Thanks

The two different clients represent two different (albeit superficially similar) features of AWS IoT:
AWSIoTMQTTClient provides a general interface to AWS IoT's MQTT broker. All it does is allow you to send and receive messages across topics. In fact, if you really wanted to you could use any MQTT client for this (for example Paho MQTT), but I would stick to AWS' as it is ready-configured for their broker.
AWSIoTShadowClient is an interface specifically for the AWS IoT Device Shadow. The device shadow is an AWS-managed, per-decice, two-way synchronized 'state'. It just so happens that one way of interacting with the shadow is MQTT. But the aim isn't just to send/receive messages, it's to provide a mechanism for devices to persist and take their state from the cloud. (more specifics here)
So to address your question:
Performance: both use the same underlying protocol and so have similar performance. At a push AWSIoTMQTTClient could perform better if you specialised it to your own usage, rather than following the Device Shadow pattern; but I would expect any gains to be negligible.
Security: once again both are secured in exactly the same way using AWS IoT's client/certificate security model. AWSIoTShadowClient is likely more secure by default as it is already configured to ensure that only a device can set it's reported state.
Maintenance: This depends a bit. If your use case (discussed next) is to have device report its state, and have that state inspectable and updatable from the cloud, then the AWSIoTShadowClient is much more maintainable; simply because that is what it's designed and tested to do- you'll have to write and maintain less of your own code!
Picking an approach:
Device Shadow: If you don't have a reason no to, use the shadow. It's a managed and well tested pattern (that includes edge cases, such as the blub being offline when you request it to change) for having your devices send send/receive/synchronize state from the cloud. It's built into AWS so you can easily view/change this state in the console. It's also got built-in persistence, so you can always inspect that latest state even if you aren't constantly listening for changes on the broker.
Your own MQTT topics: There's a few reasons not to use the shadow. The shadow requires you to send JSON payloads, and in highly battery/network constrained situations you might want to use your own binary protocol to save bytes. Similarly it's about double the cost (although still really cheap). Finally if you readings are fast moving, write-only, telemetries that you do not need to persist and can subscribe to when you're interested, you might skip the shadow because you don't need any of its features.
So in your case, I would say you want to use the shadow.

Related

Exception from WebAuthn Authentication API with Yubikey

I'm working on adding WebAuthn support to a newly-minted web site and am running into a problem during the navigator.credentials.get() call. The client is Firefox 85.0 on Fedora 33. In case it matters, the server is Apache httpd on Fedora 33. The token is either a Yubikey 4 or a Yubikey 5NFC (the results are the same). This is the function making the API call. Obviously the credential IDs hard-coded here are for testing, not part of the final product:
function handleUserAuthenticationResponse(r) {
var cid1 = {type: "public-key", id: base64ToArrayBuffer("gL0Ig10uA2tn8L0kn2L9FoGqIPSrqvc1lLBwgQhcVDa200b1P94kPv94T6O1bDZyYRrfLbTrLRsubDxuYUxHCg==")};
var cid2 = {type: "public-key", id: base64ToArrayBuffer("tjW1RPqtAJm69I/qeV7eRFJx6h87J3NPeJ/hhbkjttcCc2BWHQ2v2nueoKBSGabw1eYsT8S+lhJv1l1mYWX+Uw==")};
var options = {
rpID: "http://localhost",
challenge: base64ToArrayBuffer(r.challenge),
allowCredentials: [cid1,cid2],
timeout: 60000
};
if (!window.PublicKeyCredential) {
throw new Error("Unable to access credentials interface");
}
navigator.credentials.get({"publicKey":options})
.then(assertion => handleTokenAssertion(assertion))
.catch(e => {console.log("Error fetching token assertion:",e);});
}
function base64ToArrayBuffer(base64) {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}
function handleTokenAssertion(a) {
alert("Got an assertion!");
}
Everything seems to work, the Yubikey LED blinks, I press the touchpad, but then I get back an exception:
Error fetching token assertion: DOMException: An attempt was made to use an object that is not, or is no longer, usable
This seems to be a bit of a Firefox catch-all. It could indicate that the token doesn't match one of the allowedCredentials[], or perhaps other things. It's hard to tell. The FIDO2 credential on the Yubikey was created with fido2-cred(1) tool packaged with the libfido2 source. In this case the credentialId is from the fido2-cred -M output:
CuCEGL10uPhBmNCY4NsGaAz0gir/68UMGFQn0pfb6tc=
http://localhost
fido-u2f
WMSGBbi6CMINQvnkVRUYcYltDg3pgFlihvtzbRHuwBPipEEAAAAAAAAAAAAAAAAAAAAAAAAAAABAgL0Ig10uA2tn8L0kn2L9FoGqIPSrqvc1lLBwgQhcVDa200b1P94kPv94T6O1bDZyYRrfLbTrLRsubDxuYUxHCqUBAgMmIAEhWCA5itRRCBO0lnsztvPvI1waVZLBCZ1XMJjOvlN2oZmBCyJYILFaRjThs5Paj1sOp81iID1LpUBYHJhp4dizC0eI/RrE
gL0Ig10uA2tn8L0kn2L9FoGqIPSrqvc1lLBwgQhcVDa200b1P94kPv94T6O1bDZyYRrfLbTrLRsubDxuYUxHCg==
MEQCIFfs8PagKhNnDgzxfurVzdkTDVTT6ixKk0ak/2qrbSPUAiAf64w390rX1cyY58JgSC/Ac97w6TLcYKuqxOSn5lxV0g==
<long assertion certificate>
You can see the credentialId on line 5, and that it matches cid1 in the Javascript function. Furthermore, if I request an assertion from the token using this credentialId and all else identical (except the challenge) with fido2-assert -G, everything works fine: I get the assertion and it verifies correctly using fido2-assert -V.
Without a more meaningful diagnostic it's hard to know what to try, so I thought I would ask here and see if anyone has any hints. Perhaps I've made some basic error with either Javascript or the credentials API?
Thanks!
UPDATE: One possibility I thought might be worth trying was removing the scheme from the RP ID but that made no difference.
UPDATE: Looking at the firefox source code, the error is apparently NS_ERROR_DOM_INVALID_STATE_ERR, which covers several different situations but in this case is most likely a translation of U2F_ERROR_INVALID_STATE (in dom/webauthn/U2FHIDTokenManager.h). U2F_ERROR_INVALID_STATE, in turn, is defined in third_party/rust/authenticator/src/u2fhid-capi.h as a simple numerical value (3), with no indication of where the value came from. Perhaps it's defined by the underlying HID driver for the Yubikey, but it's not clear what driver that corresponds to. The hunt continues...
It turns out that the problem was indeed the format of the relying party ID. Based on example code from the net (which may have worked with other browsers or versions of the code?), I initially used the full scheme://domain format for the rpID (so in my code above, http://localhost), but it turns out that what's needed is just the domain (localhost). Modifying the rpID in this way allows the assertion process to succeed.
Initially I thought this did not work, but it turned out that I'd simply forgotten to commit the change. Having belatedly done that, it works.

Pushwoosh Cordova API: Can two different devices ever generate the same HWID?

Our app uses HWIDs generated by Pushwoosh as a key to differentiate devices. Looking over traffic logs, I am seeing what looks like the same device submitting HTTP requests from several different ISPs over short timeframes.
It appears that different devices from all over the internet are generating the same HWID, which our app is treating as the same device causing issues with users interfering with each other. Our data is showing about 50 requests appear to be from different devices, but using the same HWID.
This makes no sense to me -- from what I've read about HWIDs, they are based on the device serial number, so they should always be unique.
Our mobile app is written in Cordova, and we are getting HWIDs with this code:
get_hwid: (evt) =>
_this = #
regid = device.uuid
if evt? && evt.detail?
push_notification_id = evt.detail.deviceToken
else
push_notification_id = ""
pushNotification = cordova.require("pushwoosh-cordova- plugin.PushNotification")
pushNotification.getPushwooshHWID (hwid) ->
_this.debug 'in getPushwooshHWID callback'
_this.debug ' Pushwoosh HWID: ', hwid
_this.debug ' push_notification_id: ', push_notification_id
_this.debug ' regid: ', regid
_this.emit 'retrieved-hwid',
regid: regid
push_notification_id: push_notification_id
hwid: hwid
Has anyone observed the PushWoosh API generate HWIDs that weren't always unique?
The PushWoosh docs say that sometimes HWIDs can change on the same device, but I can't find anything that suggests that they can't be expected to be unique.
Thanks!
HWID's (which are IDFV/IDFA) are unique. The only way they may change (to another unique value) is when user restores a backup on a device.
If you see the same HWID's make sure your Pushwoosh SDK are >= 4.1.2
as outlined here
https://www.pushwoosh.com/blog/pushwoosh-sdk-update-ios-10-makes-difference/

Find Phillips Hue Bridge in Production Web App on Other Network

I am using the node-hue-api package on an Node.js/Express server to work with the Hue API. I've built an admin section of a website that only I can access that I'd like to use to control my Hue lights. This works fine in my dev environment as my localhost is obviously running on the same network/IP as the Bridge.
I think the problem is that when I push my changes to my production environment -- which is running on a Digital Ocean droplet of a different IP that the Bridge isn't connected to or aware of, it obviously can't find the Bridge -- returns empty array as response: Hue Bridges Found: []
I can't be the first to have this problem, but the documentation for Hue in general is sparse. I'm already doing both UPnP and N-UPnP searches, but I've seen mentions of doing an IP scan where you can set a specific IP to look for (I know the IP), but documentation for that is practically non-existent. Any ideas?
Since docs are sparse and this is close to working, here's the useful parts of my code in case it helps others or shows I'm doing something wrong.
hue = require("node-hue-api");
//===== Hue =====
var HueApi = require("node-hue-api").HueApi,
lightState = hue.lightState,
timeout = 5000;
var displayResult = function(result) {
console.log(JSON.stringify(result, null, 2));
};
var displayError = function(err) {
console.error(err);
};
var displayBridges = function(bridge) {
console.log("Hue Bridges Found: " + JSON.stringify(bridge));
};
hue.nupnpSearch().then(displayBridges).done();
hue.upnpSearch(timeout).then(displayBridges).done();
var hostname = "my-ip-address",
username = "my-registered-user-hash",
api = new HueApi(hostname, username),
state = lightState.create(),
lightsObject;
//Get all lights attached to the bridge
api.lights(function(err, lights) {
if (err) throw err;
lightsObject = lights;
displayResult(lightsObject);
console.log(lightsObject);
});
I then pass the lightsObject to my admin page through the render function, do a for loop to loop through each light in the returned object, then show some toggle switches based on the state in the object. Then onchange, I run a jQuery AJAX call to the app.put method here, which runs the node-hue-api code to set the light's state, passing the lightId from the value attribute of the toggle from the admin page. Here's a pic to show that working.
And the app.put code. Could probably combine these, but I wanted separate calls in case I want to do something more creative with the on state.
app.put('/lighton', function(req, res) {
//Set the state of the light
api.setLightState(req.body.lightId, state.on())
.fail(displayError)
.done();
});
app.put('/lightoff', function(req, res) {
//Set the state of the light
api.setLightState(req.body.lightId, state.off())
.fail(displayError)
.done();
});
According to the FAQs in the Phillips Hue API documentation pages:
Where is the API located?
The hue API in the bridge is accessible on your local network. That is
to say, it is accessible on your local WiFi or wired network and
cannot be accessed directly outside that network. This also makes your
hue system secure. The hue bridge, once installed on your network,
will have its own IP address set up by your local router. This is the
IP address you will use when sending commands to the RESTful API.
...
Can I get remote access to hue (e.g. other than IFTTT)?
It is planned that we will have a remote API for accessing hue across
the Internet. For now the only option available is to use the IFTTT
interface.
(emphasis added)
So you'll either need to expose some custom API of your creation on your home server, or use IFTTT (I believe a maker channel could do what you want here, but I've not worked with one before so am not really sure), if this FAQ is to be believed.

How to stream audio from browser to WebRTC native C++ application

I have so far managed to run the following sample:
WebRTC native c++ to browser video streaming example
The sample shows how to stream video from a native C++ application (peerconnection_client.exe) to the browser (I am using Chrome). This works fine and I can see myself in the browser.
What I would like to do is to stream audio from the browser to the native application but I am not sure how. Can anyone give me some pointers please?
I'm trying to find a way to stream both video and audio from browser to my native program. and here is my way so far.
To stream video from browser to your native program without gui, just follow the example here. https://chromium.googlesource.com/external/webrtc/+/refs/heads/master/examples/peerconnection/client/
use AddOrUpdateSink to add your own VideoSinkInterface and you will receive your frame data in callback void OnFrame(const cricket::VideoFrame& frame). Instead of render the frame to GUI as the example does, you can do whatever you want.
To stream audio from browser to your native program without real audio device. you can use a fake audio device.
modify variable rtc_use_dummy_audio_file_devices to true in file https://chromium.googlesource.com/external/webrtc/+/master/webrtc/build/webrtc.gni
invoke the global static function to specify the filename webrtc::FileAudioDeviceFactory::SetFilenamesToUse("", "file_to_save_audio");
patch file_audio_device.cc with the code blew. (as I write this answer, FileAudioDevice has some issues, may already be fixed)
recompile your program, touch file_to_save_audio and you will see pcm data in file_to_save_audio after webrtc connection is established.
patch:
diff --git a/webrtc/modules/audio_device/dummy/file_audio_device.cc b/webrtc/modules/audio_device/dummy/file_audio_device.cc
index 8b3fa5e..2717cda 100644
--- a/webrtc/modules/audio_device/dummy/file_audio_device.cc
+++ b/webrtc/modules/audio_device/dummy/file_audio_device.cc
## -35,6 +35,7 ## FileAudioDevice::FileAudioDevice(const int32_t id,
_recordingBufferSizeIn10MS(0),
_recordingFramesIn10MS(0),
_playoutFramesIn10MS(0),
+ _initialized(false),
_playing(false),
_recording(false),
_lastCallPlayoutMillis(0),
## -135,12 +136,13 ## int32_t FileAudioDevice::InitPlayout() {
// Update webrtc audio buffer with the selected parameters
_ptrAudioBuffer->SetPlayoutSampleRate(kPlayoutFixedSampleRate);
_ptrAudioBuffer->SetPlayoutChannels(kPlayoutNumChannels);
+ _initialized = true;
}
return 0;
}
bool FileAudioDevice::PlayoutIsInitialized() const {
- return true;
+ return _initialized;
}
int32_t FileAudioDevice::RecordingIsAvailable(bool& available) {
## -236,7 +238,7 ## int32_t FileAudioDevice::StopPlayout() {
}
bool FileAudioDevice::Playing() const {
- return true;
+ return _playing;
}
int32_t FileAudioDevice::StartRecording() {
diff --git a/webrtc/modules/audio_device/dummy/file_audio_device.h b/webrtc/modules/audio_device/dummy/file_audio_device.h
index a69b47e..3f3c841 100644
--- a/webrtc/modules/audio_device/dummy/file_audio_device.h
+++ b/webrtc/modules/audio_device/dummy/file_audio_device.h
## -185,6 +185,7 ## class FileAudioDevice : public AudioDeviceGeneric {
std::unique_ptr<rtc::PlatformThread> _ptrThreadRec;
std::unique_ptr<rtc::PlatformThread> _ptrThreadPlay;
+ bool _initialized;;
bool _playing;
bool _recording;
uint64_t _lastCallPlayoutMillis;
I know this is an old question, but I struggled myself to find a solution currently so I thought sharing is appreciated.
There's is one more or less simple way to get an example running which streams from the browser to native code.You need the webrtc source http://www.webrtc.org/native-code/development
The two tools you need are the peerconnection server and client. Both can be found in the folder talk/example/peerconnection
To get it working you need to patch it to enable DTLS for the peerconnection client. So patch it with the patch provided here https://code.google.com/p/webrtc/issues/detail?id=3872 and rebuild the client. Now you are set up on the native site!
For the browser I recommend the peer2peer example from here https://github.com/GoogleChrome/webrtc after starting the peerconnection_server and connection the peerconnection_client try to connect with the peer2peer example.
Maybe a connection constraint is necessary:
{
"DtlsSrtpKeyAgreement": true
}
you could use the following example which implement a desktop client for appRTC.
https://github.com/TemasysCommunications/appRTCDesk
this completes and interop with the web client, android client and iOs client provided by the open source implementation at webrtc.org, giving you a full suite of clients to work with their free server. peer connection_{client|server} is an old example from the lib jingle time (pre webrtc) and does not interop with anything else.

Issuing MySQL queries from standalone Javascript (no, I'm not crazy, my vendor is)

Our lab recently got an Agilent Bravo pipetting robot (it precisely dispenses tiny quantities of liquid for doing rapidly doing many biology or chemistry experiments). Apparently the glue language for extending the software that controls the robot is Javascript! I know, right?
Anyway, for the robot to be useful, we have to be able to retrieve information about the samples it's handling but every example I can find for sending queries in Javascript depends on PHP and usually the assumption that the script is running in a web-browser.
Is there some way to wrap a command-line mysql or is there already some library or utility that does this? The OS we're running is Windows 7.
Wow, thanks for the quick and useful answers.
In addition, I found a platform-specific answer: http://www.velocity11.com/techdocs/helpsystem/vworks_ug/usingjavascriptinvworks.html
Long story short, VWorks (control software for Agilent's equipment) has a run() global function that does exactly that. But, the above answers are probably more useful to this site than my own is, because they are relevant to a broader range of problems, so thanks again.
"sending queries in Javascript depends on PHP"
no it doesn't.
Just send retreive data(json) using ajax, I'd use http://api.jquery.com/jQuery.ajax/.
Yes, you can use ADO with Javascript on Windows to access various data sources. Search for "jscript ado" and you will get lots of information on this, e.g.:
// path to database
var DBpath="\\\\Server\\Path\\myDB.mdb"
// set up a few object constants
var adLockReadOnly=1
var adOpenForwardOnly=0
var adCmdText=1
// create and open a new connection (MSAccess)
var cnn=new ActiveXObject("ADODB.connection")
cnn.Provider = "Microsoft.Jet.OLEDB.4.0;Data Source=" + DBpath
try
{
cnn.open
}
catch(err)
{
// could not open connection
// view details in err.Description and err.Number
return 0
}
//open a read only recordset
var rs = new ActiveXObject("ADODB.Recordset")
try
{
rs.Open("Select * from myTable", cnn, adOpenForwardOnly, adLockReadOnly)
}
catch(err)
{
// could not open recordset
return 0
}
while(!rs.EOF)
{
// do something
rs.movenext
}
rs.close
Update:
According to info here, you can develop plugins using Visual Studio/C#. Maybe that is of some use? You could write a plugin to send the data somewhere...

Categories

Resources