Local Fulfilment with multiple devices - actions-on-google

I try to create a Google Home Action with Local Fulfilment and with multiple devices that are detected through mDNS.
As such, I have a GCloud Fulfillment Function that returns the required sync:
response.payload.devices.push({
id: "elgato-device-id",
name: {
name: "Elgato",
},
"otherDeviceIds": [{
"deviceId": "local-elgato-device-id"
}],
// ...
});
As well as my Javascript file to handle the local part:
.onIdentify((request) => {
const device = request.inputs[0].payload.device;
return new Promise((resolve, reject) => {
const response = {
intent: smarthome.Intents.IDENTIFY,
requestId: request.requestId,
payload: {
device: {
id: device.id || "",
verificationId: "local-elgato-device-id"
},
},
};
resolve(response);
});
})
(I am using https://github.com/sirdarckcat/sirdarckcat.github.io/tree/master/fakeauth as a starting point.)
When debugging my app, I can see that onIdentify is correctly called twice, once with each of my two devices. Still, only one device appears in the Google Home app and manipulating that one will randomly control either of both physical devices.
https://developers.home.google.com/local-home/fulfillment-app says:
Note: The verificationId must match one of the otherDeviceIds in your SYNC response, in order for the Local Home platform to know it’s the same device (this process is called deduplication).
Is it possible to have multiple devices with the same deviceId? Or do I have to implement this differently? If so, how?
(I didn't consider to implement a hub yet, since https://stackoverflow.com/a/66031681 states that the Local Fulfilment SDK "uniquely identify the device by their network address" which is true for the two devices.)

When integrating Google Home you will need to have a unique device id for each of your devices. You can learn more about Local Fulfillment by going through Enable Local Fulfillment for Smart Home Actions Codelab.

Related

web bluetooth - `getPrimaryService` call gets stuck

I'm trying to print from a device from a PWA - the device is https://www.sunmi.com/en/v2-pro/ - I can connect to the device, but when I try to get the service, the promise gets stuck i.e. neither the "catch" nor the "finally" callbacks are called.
navigator.bluetooth.requestDevice({
acceptAllDevices: true,
optionalServices: ['00001101-0000-1000-8000-00805f9b34fb'],
})
.then((device) => {
console.log('> Found ' + device.name);
console.log('Connecting to GATT Server...');
return device.gatt.connect();
})
.then((server) => {
console.log('> Connected ' + server.connected);
console.log('getPrimaryService...');
return server.getPrimaryService('00001101-0000-1000-8000-00805f9b34fb');
})
.then((service) => {
console.log('getCharacteristic...');
return service.getCharacteristic('00001101-0000-1000-8000-00805f9b34fb');
})
.then((characteristic) => {
console.log('> Characteristic UUID: ' + characteristic.uuid);
})
.catch((error) => {
console.log('Argh! ' + error);
})
.finally(() => {
console.log('> Finally');
});
console.log('whatsup?');
https://share.getcloudapp.com/5zuLxBE4 - the logs until "getPrimaryService...". The device does not show any dialog or requests any interaction. So not sure why this would get stuck.
I've used the nRF connect app to get the uuid - https://share.getcloudapp.com/GGulYQYz
Not sure if I'm doing something wrong here, any help would be appreciated.
This is probably an implementation issue, as the browser needs to call out to the operating system to get the list of primary services and this can fail for a myriad of reasons. Can you file an bug report on the issue tracker for the browser you are using (e.g. crbug.com for Google Chrome) with details about the device and operating system on which you are encountering this issue? It would also be useful information to know if this issue occured on multiple platforms (e.g. Android and Windows).
The "00001101-0000-1000-8000-00805f9b34fb" UUID you use is the one used for the standardised Serial Port Profile (SPP).
According to https://social.msdn.microsoft.com/Forums/azure/en-US/e3466b1f-b67a-4217-ab53-c64921493ec1/what-bluetooth-service-is-needed-to-connect-a-remote-device-to-a-windows-10-virtual-port?forum=wdk, SPP is not supported on BLE devices.
I'm surprised though the getPrimaryService() promise does not reject. As suggested by Reilly Grant, please file a bug. See https://sites.google.com/a/chromium.org/dev/developers/how-tos/file-web-bluetooth-bugs

How to connect to Actions builder project to update type entities?

I've got a project build migrated from AOG + Dialogflow to Actions Builder. I need to update (or insert new) type entries with the REST API.
To do that action I've found an endpoint, that gives the ability to update the whole project along with entities:
https://developers.google.com/assistant/actions/api/reference/rest/v2/projects.draft/write
However, I can't connect to that endpoint due to 401 error. Before that I've tried to emulate a similar request to another endpoint, which allows reading the project:
https://developers.google.com/assistant/actions/api/reference/rest/v2/projects.draft/read
Obviously, I've got the same error here.
Also, I've found this repo - https://github.com/actions-on-google/assistant-actions-nodejs
which adds a wrapper for easier manipulation with the REST API, but it also doesn't contain any information on how to properly authorize to get access to an app.
Can please somebody suggest how authorization should be done to start using this REST API?
Using Actions Builder, the type entities can be updated directly in your webhook calls rather than calling an API in parallel.
Look up the names of the types you want to override in Actions Builder and set the conv.session.typeOverrides field in your response.
Here's a code example of how it might be done:
const app = conversation()
// `app.middleware` will run on every invocation call
app.middleware(conv => {
// ...
// Obtain `trackTitles` and `trackGenres`
// ...
conv.session.typeOverrides = [{
name: 'track',
mode: Mode.TypeReplace,
synonym: {
entries: Array.from(trackTitles).map(title => ({
name: title,
synonyms: [title],
})),
},
}, {
name: 'genre',
mode: Mode.TypeReplace,
synonym: {
entries: Array.from(trackGenres).map(genre => ({
name: genre,
synonyms: [genre]
}))
}
}]
})

For PWA, what is the easiest way to get per-device settings (as in reading a .ini file or environment variables)?

For PWA, what is the easiest way to get per-device settings (as in reading a .ini file or environment variables)?
I'm making a very simple in-company react PWA for andriod-based tablets (only). I just want to store a couple of settings (the room number where the device is being used, and a device id) and read those in upon startup.
My experience in recently in Windows, and so I'm imaging a text file that I could place on each tablet with the settings. Does that make sense for our PWA?
Or is there a better/easier way to do app settings?
Thank you.
The answer depends on how that data is initially provisioned and what kind of guarantees you need about it being "tamper-proof."
Assuming you can provision the information during the web app's initial launch, and you're fine using storage that's exposed via a browser's Developer Tools (i.e. your threat model doesn't include a motivated user using DevTools to erase/modify the data), a simple approach would be to a) use the Cache Storage API to read/write that data as JSON, using a synthetic URL as the key and b) requesting persistent storage just for an added guarantee that it won't be purged if the device ends up running low on storage.
This could look like:
// Just use any URL that doesn't exist on your server.
const SETTINGS_KEY = '/_settings';
const SETTINGS_CACHE_NAME = 'settings';
async function getSettings() {
const cache = await caches.open(SETTINGS_CACHE_NAME);
const cachedSettingsResponse = await cache.match(SETTINGS_KEY);
if (cachedSettingsResponse) {
return await cachedSettingsResponse.json();
}
// This assumes a generateInitialSettings function that does provisioning
// and returns an object that can be stringified.
const newSettings = await generateInitialSettings();
await cache.put(SETTINGS_KEY, JSON.stringify(newSettings), {
headers: {
'content-type': 'application/json',
}
});
// Optional: request persistent storage.
// This call may trigger a permissions dialog in the local browser, so it is
// a good idea to explain to the user what's being stored first.
// See https://web.dev/persistent-storage/#when-should-i-ask-for-persistent-storage
if (navigator.storage && navigator.storage.persist) {
// This returns a promise, but we don't have to delay the
// rest of the program execution by await-ing it.
navigator.storage.persist();
}
return newSettings;
}

How to link BluetoothSerial setDataAvailableCallback() to update text display

I'm very new to react-native. I'm using https://github.com/rusel1989/react-native-bluetooth-serial to connect my Android app to a BT device. This GitHub repo has an example, which works. I was able to send commands from app to device.
Now I want my device to send data back to the app. Think of it as IoT data, just a few bytes sent every few seconds. I know that setDataAvailableCallback() can be used. My question is that if I create a <Text /> element in JSX, how can I link that with the callback. Thanks.
API: https://github.com/derektu/react-native-bluetooth-serial
I figured it out by trying out a few changes on the example I had. Within render(), I included the following: <Text>{this.state.myText}</Text>
In the constructor of that class, I added a new field to this.state that is initially set to an empty string:
this.state = {
...
myText: ''
}
In the same constructor, I set up this polling at 100 ms periodicity:
setInterval(() => {
BluetoothSerial.read()
.then((res) => {
this.setState({ myText: res })
})
}, 1000);
I will enhance this to read only when bytes are available. Also, my installation did not have setDataAvailableCallback(), which is why I couldn't use it.

how to maintain a connexion to send realtime data to distant client without request

i'm developing a mobile project the can control a home an receve information from it either from inside or outside of the house. im setting up a port forwarding mechanism on my router to connect to my server if i'm using my application from the outside.
i think that the fact of receving data from the server should be initiated by the client (android app) witch is in an other notwork or it will be blocked.
* is there a solution to receve data (temperature) in real time from the outside simply by just requesting once or i should send a request like evrey minute? * i'm confused because some applications like video streaming receve data from servers by just starting the video player then it receve udp packets automaticly
more spicificlly can i send data from a server to a distant client by just sending a request once (maybe by letting the socket open for every client, VPN , SIP)???
Sending Data to Distant Clients: Securely & Reliably
There is only one reasonably-reliable method for sending a signal to distant clients. I want to confirm your original assumption of sending to clients without request, this is theoretically impossible as the device needs to call home somewhere and notify a broker gateway (SIP) or otherwise of it's address. However please ignore that because the right way will be relayed here with source code.
Security Importance
It is important to realize that opening your firewall is outside of good practices. It is better to hold onto the firewall's lockdown rules for inbound traffic. The following section is a secure method that will allow you to open an outbound connection with maximum security preventing snooping and other unsafe security holes.
Download Android Source Libraries
See instructions here for downloading Android Source Library Files: https://github.com/pubnub/java/tree/master/android
Receiving Data On-Demand from a Distance on Android
You'll need to copy/paste the following code to get things moving forward quickly. Start by taking this Java code and pasting it into your app. Then follow by downloading the library files.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// ANDROID PHONE
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Pubnub pubnub = new Pubnub(
"", // PUBLISH_KEY (Optional, supply "" to disable)
"demo", // SUBSCRIBE_KEY (REQUIRED)
"", // SECRET_KEY (Optional, supply "" to disable)
"", // CIPHER_KEY (Optional, supply "" to disable)
true // SSL_ON?
);
Hashtable args = new Hashtable(1);
args.put( "channel", "distant-client-ABC-DEF" );
pubnub.subscribe(args, new Callback() {
public void connectCallback(String channel) {
System.out.println("CONNECT on channel:" + channel);
}
public void disconnectCallback(String channel) {
System.out.println("DISCONNECT on channel:" + channel);
}
public void reconnectCallback(String channel) {
System.out.println("RECONNECT on channel:" + channel);
}
public void successCallback(String channel, Object message) {
System.out.println(channel + " " + message.toString());
}
public void errorCallback(String channel, Object message) {
System.out.println(channel + " " + message.toString());
}
});
Send Data On-demand to the Distant Client
On a Java VM on your home computer/systems, you may use the same code to send data securely to the remote distant client. Use the following code to do this:
Download JVM Server Source Libraries
See instructions here for downloading/using JVM Server Source Library Files: https://github.com/pubnub/java/tree/master/java
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// HOME SERVER
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Pubnub pubnub = new Pubnub(
"demo", // PUBLISH_KEY (REQUIRED on Server)
"demo", // SUBSCRIBE_KEY (REQUIRED)
"", // SECRET_KEY (Optional, supply "" to disable)
"", // CIPHER_KEY (Optional, supply "" to disable)
true // SSL_ON?
);
Hashtable args = new Hashtable(1);
args.put( "channel", "distant-client-ABC-DEF" ); // SEND TO CLIENT ABC-DEF
pubnub.publish(args, new Callback() {
public void successCallback(String channel, Object message) {
System.out.println("PUBLISH : " + message);
}
public void errorCallback(String channel, Object message) {
System.out.println("PUBLISH : " + message);
}
});
I'm not into Android world, but isn't it the service you're looking for ? : http://developer.android.com/google/gcm/index.html