Intro:
was created a Google Smart Home project
was configured a proxy server via ngrok to redirect the Google request to my local machine
I develop an IoT project that has the ability to open/close a lock. I need to implement Google integration to use the Google Assistant to control the user locks. I have implemented OAuth Server for Google. Also I have implemented some controllers to handle Google Action Intents: SYNC, QUERY and EXECUTE. Google send a request with the SYNC intent and App response a payload that contain devices list with specific settings. Instance:
{
requestId: 'requestIdOfGoogle', // contains in the request body
payload: {
agentUserId: 'userId123', // matches user id inside app system
devices: [
{
id: 1,
type: 'action.devices.types.LOCK', // device type
traits: ['action.devices.traits.LockUnlock'], // feature that has a device
name: {
name: 'Kos Lock'
},
willReportState: true,
roomHint: 'Main Door',
deviceInfo: { // Test data
manufacturer: 'smart-home-inc',
model: 'hs1234',
hwVersion: '3.2',
swVersion: '11.4'
}
}
]
}
}
Then Google send requests to my server with QUERY intent to get info about state of a devices, instance
{
requestId: 'requestIdOfGoogle', // contains in the request body
payload: {
devices: {
1: {
status: 'SUCCESS',
online: true,
isLocked: true,
// isJammed - Boolean. Whether the device is currently jammed and therefore its
// locked state cannot be determined.
isJammed: false
}
}
}
}
But after sending a response a test lock isn't configured and a user can't control one with Google Assistant.
enter image description here
I have tried to add other traits for a lock but it didn't help me. Also I have the same problem when I try to configure a Door device. But when I send to Google a Light device it works successfully. When you use the LockUnlock trait then Google Doc recommends to setup secondary user verification but it's optional.
I don't understand that do incorrect. If someone faced such a problem and solved it then could you help me, please
Prerequisites:
use node ^14.0.0
programming language - js
Touch controls are not supported for every device, and locks are not a device type that can be controlled directly. But they will still respond to voice commands.
Related
I am attempting to use navigator.share in a file running in WKWebView on iOS 16. The file is loaded into the web view using a file: protocol path like so:
uiView.loadFileURL(Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "web")!, allowingReadAccessTo: Bundle.main.bundleURL)
I have this setting on:
configuration.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
So I am able to load file: protocol files into the web view, but I don't seem to be able to use the Web Share API. I get this error:
NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.
However, when I point the web view at this HTTPS URL, it works:
https://mdn.github.io/dom-examples/web-share
This leads me to believe the Web Share API is not working because the HTML file loaded over the file: protocol is not viewed as secure and thus the JavaScript runtime don't treat the code on it as if it was running in a secure context.
As a result, I believe the access to the navigator.share API is forbidden.
Is there a way to configure WKWebView to allow access to secure context JS/DOM/web APIs without using a custom scheme (and adding an extra layer atop just loading my file from the bundle)?
The problem was not with the file: protocol or secure context.
I looked through WebKit source code for navigator.share and found that NotAllowedError gets returned whenever there don't seem to be conditions met for transient activation.
https://github.com/WebKit/WebKit/blob/d9bcb08521ddb86af9a713213d15488712919143/Source/WebCore/page/Navigator.cpp#L172
void Navigator::share(Document& document, const ShareData& data, Ref<DeferredPromise>&& promise)
{
if (!document.isFullyActive()) {
promise->reject(InvalidStateError);
return;
}
if (!validateWebSharePolicy(document)) {
promise->reject(NotAllowedError, "Third-party iframes are not allowed to call share() unless explicitly allowed via Feature-Policy (web-share)"_s);
return;
}
if (m_hasPendingShare) {
promise->reject(InvalidStateError, "share() is already in progress"_s);
return;
}
auto* window = this->window();
if (!window || !window->consumeTransientActivation()) {
promise->reject(NotAllowedError);
return;
}
if (!canShare(document, data)) {
promise->reject(TypeError);
return;
}
Transient activation is the thing where some web APIs will only run in response to a user gesture; the user gesture is either a part of the call stack leading up to the API call invocation or a user gesture has happened recently.
More about transient activation here:
https://developer.mozilla.org/en-US/docs/Glossary/Transient_activation
In my case I did have a user gesture there but also a WebRTC peer connection ICE gathering flow which while seemingly quick I guess lasted too long and it reset the transient activation flag preventing me from using the Web Share API at the end of the ICE gathering phase.
The fix is to prepare whatever you need for the Web Share API data payload in advance and in response to the user gesture, such as a button click, call the web API straight away.
I am trying to submit a form but I get an "internal" error after submit on IOS Safari. Happened on two separate devices. I'm using Firebase functions onCall function. Client code:
var contactForm =
window.firebase.functions().httpsCallable('contactForm');
let result = await contactForm({ accountUID, foldersFilter,
firstName, lastName, email, cellNumber, dobDay, dobMonth })
And server code:
exports.contactForm = functions.https.onCall(( data, context ) => {
return contactForm.contactForm( data, context )
});
This function is called via form. The form works great on chrome, safari desktop, but for some reason it gets an internal errror sometimes when testing on IOS device. At first I thought it only happened when I was using autofill, but I've tested more and I get the same error when not using autofill too.
The confusing thing is my function code is actually never being called (I don't see any firebase function logs). Here is my console in safari:
The network connection was lost.
Fetch API cannot load https://us-central1-projectId.cloudfunctions.net/contactForm due to access control checks
Failed to load resource: The network connection was lost.
internal
Why won't this form submit on ios safari?
I fixed the issue. Turns out it has something to do with Google Cloud Functions being IPv4, and Safari requiring IPv6. I suspect this will become a bigger issue moving forward. I'm having to move all onCall Firebase functions to https triggers. In order to make https triggers work, you have to use a custom domain in Firebase hosting and rewrite to your function endpoint.
{
"hosting": {
...
"rewrites": [
{
"source": "/api/contactForm",
"function": "contactForm"
}
}
and so now instead of calling https://us-central1-projectId.cloudfunctions.net/contactForm to trigger my api. I call https://customdomain.com/api/contactForm
In my App there is PubNub used for Chat function,and for delete chat history,there is Web service,In app chat history clear successfully,but when application in background state and another user send message to me,notification is received and if I open that notification, it redirect to Chat Controller, but my problem is that when chat history clear successfully,still on my chat page all deleted message are present,is there is solution to delete message from pub-nub?
PubNub Message Update & Delete Features - NEW
Update Published Messages using the Message Actions feature.
pubnub.addMessageAction(
{
channel: 'chats.room123'
messageTimetoken: '15610547826970040',
action: {
type: 'updated',
value: 'Hello World! (edited)',
},
},
function(status, response) {
}
);
Delete Messages using the deleteMessages API.
There is a setting to accept delete from history requests for a key, which you must enable by checking the Enable Delete-From-History checkbox in the key settings for your key in the Admin Portal.
Requires Initialization with the secret key.
pubnub.deleteMessages(
{
channels: 'chats.room1',
start: "15526611838554309",
end: "15526611838554310",
},
function (status, response) {
console.log(status, response);
}
);
Soft Delete a Message, using the Message Actions feature.
"action": {
"type": "deleted",
"value": "."
}
UPDATE: THIS DESIGN PATTERN IS STILL LEGITIMATE BUT IS OUTDATED. PLEASE REFER TO THE ABOVE FOR BEST PRACTICES.
PubNub Storage/History Update & Delete Design Pattern
Although PubNub's API does not directly support the editing of stored messages, you can use a design pattern authored by one of our very own Solution Architects.
Message Updates and Deletes Design Pattern
I'm working on a Safari Content Blocking extension. I intend to show setup instructions if the extension is disabled and to show settings if it is conversely enabled. How can I determine if the extension is enabled by the user?
I've seen this method to detect if a custom keyboard is activated but there's no key on NSUserDefaults that relates to Safari Content Blockers.
As of iOS 10, there is a new method in SFContentBlockerManager to support this:
getStateOfContentBlocker(withIdentifier:completionHandler:)
And you call it like this (Swift 3):
SFContentBlockerManager.getStateOfContentBlocker(withIdentifier: "your.identifier.here", completionHandler: { (state, error) in
if let error = error {
// TODO: handle the error
}
if let state = state {
let contentBlockerIsEnabled = state.isEnabled
// TODO: do something with this value
}
})
You could utilize a SFSafariViewController to load a custom website. This website checks whether it is able to show something that your content blocker should block. Then redirect to the respective custom url (success/failure) that your app previously registered for. You could even use a hidden Safari View Controller without animation to avoid any distraction from the user's perspective (as shown here). (I guess this technique is used by former content blocker Peace)
Steps
App
Register custom URLs for success/failure
Register for notification callback using the NotificationCenter (e.g.
contentBlockerEnabled)
Use SFSafariViewController to show a custom website and include the following rule in blockerList.json:
{
"action": {
"type": "css-display-none",
"selector": ".blocked_selector"
},
"trigger": {
"url-filter": ".*"
}
}
Website
Check for blocked content:
if($('.blocked_selector').css('display') == "none") {
// Content blocker enabled
}
Redirect to custom URL (success/failure)
App
Post notification from
application:openURL:options: (success/failure based on called url)
Update 18/01
Following on from Tilo's hypothesis, I built the proposed solution. I wrote about what I learnt on Medium and you can grab the source files from GitHub.
TL;DR It works but only temperamentally due to the latency incurred of the content blocking rules database to update. A potential workaround is redirecting the test page to create an artificial delay.
What is the best way to test if a Worklight app is off-line?
After I use the WL.Device.startAcquisition( ... ) api to start stuff off, I am currently using:
WL.Device.Geo.acquirePosition(function(pos) {
console.log("***** Aquired position ***** " + JSON.stringify(pos));
}, function(error) {
console.log(JSON.stringify("***** Unable to aquire position ***** " + error.code + ' : ' + error.message));
// call method to asynchronously - periodicallyCheckIfOnline( ... );
}, {timeout: 5000});
And if I determine that I am offline, I then use the watchPosition api to periodically test for a new connection.
navHandle = navigator.geolocation.watchPosition(onSuccess, onError, { timeout: 5000 });
Once I get the connection back I then clear the watch.
navigator.geolocation.clearWatch(navHandle);
Is this the best way of doing it or are there better Worklight APIs to use for this.
Note: I am trying to test this in a Mobile Browser Simulator scenario hence the short timeouts.
On startup, use something like
WL.Client.connect({onSuccess:onConnectSuccess,onFailure:onConnectFailure,timeout:number_of_ms});
to check if you have initial connectivity.
To detect any further changes in connectivity, you can use the
WL.Client.setHeartBeatInterval(number_of_s) API.
This will 'ping' the worklight server every number_of_s seconds and fire the WL.Events.WORKLIGHT_IS_DISCONNECTED and WL.Events.WORKLIGHT_IS_CONNECTED events, to which you attach callbacks to as described in the reading-worthy tutorial linked to by #Leandro David.
NOTE : if you need to use network to transfer heavy data, do a double check : once you know you have connectivity to the worklight server, use the WL.Device.getNetworkInfo API to check the connection quality before sending/receiving data.
There is a tutorial about dealing with online/offline mode in the worklight geting started material ("Working Offline" link):
http://www.ibm.com/developerworks/mobile/worklight/getting-started.html#GS_work_offline
It tells the best way to use Worklight API to deal with online/offline connection
To summarize, I believe this is the most important part:
Active detection of connectivity
Connectivity loss can be detected in two locations in your application code:
– Application initialization – WL.Client.init() method, typically called from initOptions.js file
– Adapter procedure invocation – WL.Client.invokeProcedure() method
-To add connectivity failure detection in either location, add the onConnectionFailure property and specify a callback function to be invoked if connectivity fails
var wlInitOptions = {
onConnectionFailure: function (data){
connectionFailure(data);
},
or
WL.Client.invokeProcedure(invocationData, {
onSuccess: successHandlerFunction,
onConnectionFailure: connectionFailure,
timeout: 1000
});
Passive detection – Offline and online events
Each time the Worklight framework attempts to access the Worklight Server, it might detect that the application switched from offline to online status or vice versa.
In both cases, JavaScript events are fired:
– WL.Events.WORKLIGHT_IS_DISCONNECTED event is fired when connectivity to the Worklight Server fails
– WL.Events.WORKLIGHT_IS_CONNECTED event is fired when connectivity to the Worklight Server is restored
You can add event listeners to these events and specify the callback functions to handle them.
document.addEventListener(WL.Events.WORKLIGHT_IS_CONNECTED, connectDetected, false);
document.addEventListener(WL.Events.WORKLIGHT_IS_DISCONNECTED, disconnectDetected, false);
Note: WL.Events.WORKLIGHT_IS_DISCONNECTED and WL.Events.WORKLIGHT_IS_CONNECTED are namespace constants, not strings
There are more details available in the tutorial above