I have a rule chain in ThingsBoard that does a Create Alarm when temperature is outside threshold and does a Clear Alarm otherwise. I receive a message using a Telegram bot when these events occur. That all works fine.
However when the temperature is hovering around the threshold, I can get many notifications as it goes in and out of the threshold temperature. That is somewhat annoying.
I would like to have the Clear Alarm activity only trigger if it is more than 5 minutes (say) since the last Create Alarm event was triggered.
Any tips on how to achieve this?
I finally worked out how to do this.
I added some server attributes to my device that define the temperatures that trigger alarms. I have a rule chain for controlling these alarms with the following nodes:
Enrichment - originator attributes to add the relevant attributes into the metadata associated with this message
Filter - script to detect if the temperature is outside the expected range
Filter - script to detect if the delay period has expired since the last time the alarm was triggered
Action - create alarm when script detects that temp is out of range
Action - clear alarm when script detects that delay period has expired
Transformation - script to update last alarm time attribute
Action - save attributes to persist updated alarm time attribute
Transformation - script to create a message about alarm set or cleared
Rule chain to handle sending the message to a Telegram bot
As an example, here is the script for checking if the delay period has expired before clearing the alarm:
var alarmTime = Number(metadata.ss_lastWaterTempAlarmTime);
var alarmDelay = Number(metadata.ss_clearAlarmTimeDelay);
return metadata.ts >= alarmDelay + alarmTime;
ss is the prefix added for server side attributes that have been added to metadata.
You can see the complete rule chain json in my Aquamon repo.
Related
Problem
I need to get a callback when at least X amount of time has passed since the date for the callback has been set.
Example 1:
This would have worked great, but it's possible to trigger an execution of the block by setting the date earlier than the correct time right now:
let responseDate = Date().advanced(by: 60) // 1 min
OperationQueue.current.schedule(after: .init(responseDate), {
print("the time is now!") // possible to set the current date 1 min before
}
On the other hand, the solution for getting a current uptime from this answer works great, but it requires timer constantly running to check if we're close to date.
Is it possible to combine these two approaches and somehow "attach" a callback to KERN_BOOTTIME, so that the OS will call my method when the boottime reaches a certain value?
I'm looking as well to alternative engineering solutions that satisfy two criterias:
It should not be possible to trigger the callback by resetting the device date to some arbitrary value in the past
If the device has been put to sleep (e.g. by pressing the on/off side button), the clock should still be "ticking", so that the method will be called back while the app is running in the background.
More details:
Backgrounding / app termination is out of scope
The main point is to prevent a bypass by switching the date backwards in the settings.
Flux<Integer> shared = Flux.just(1, 2).share();
shared.subscribe(System.out::println);
shared.subscribe(System.out::println);
Since share() turns the flux into a hot one, I expect the first subscriber to get all values and the second one to get none, since the stream has completed at the time of subscription. But the output is the same as without share: 1 2 1 2, but it should be just 1 2.
When I replace share() with publish.autoconnect() it works as expected. Why is that?
The answer is simple, but it took me a while to figure it out.
share() is a shortcut for publish().refCount(). refCount() is like autoConnect() except for one additional feature: It disconnects when all subscribers have cancelled or - and that's the situation here - the stream has completed.
The first shared.subscribe creates a subscription (via share) to the original flux. All values are emitted immediately, the stream completes, the subscription is cancelled.
Since there is no subscription now, the second shared.subscribe again creates a subscription and the stream starts again, from the beginning.
autoConnect, however, does not cancel the subscription. If you use it instead of refCount the subscription to the original flux remains, but because the stream has completed, any additional subscriber won't receive any values.
I have a reminder functionality using signal R in asp.net mvc
I have userinterface to set the reminder time, If the current time matches the reminder time , it invokes a popup.
I successfully implemented this functionality with Signal R by checking the database once in every 30 seconds by using javascript timer. If current time does not match, it gives '0'.If it matches, it return '1' and the popup is shown across all browsers. But can this checking the db for every 30 seconds can be replaced by signal R ? is there any way to bring this whole thing to signal R?
You can use System.Threading.Timer to create a periodical method call to both client and server side. According to Sample project created for stocks
_timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
It creates and Event-Delegate and calls UpdateStockPrices event timely with period of __updateInterval.
In This event(code given below) you can broadcast the remainder message from server to all clients or clients who are associated with that remainder.
You can write code as :-
Clients.All.updateStockPrice(stock);
You can refer to Timer from link:-
http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx
Yes, you can use Timer in the appdomain scope, application scope or at the hub level. Just get the sample from nuget, called "Microsoft.AspNet.SignalR.Sample". It implements stock timer that periodically broadcasts changes to all clients.
I'm making an iOS app which lets you remotely control music in an app playing on your desktop.
One of the hardest problems is being able to update the position of the "tracker" (which shows the time position and duration of the currently playing song) correctly. There are several sources of input here:
At launch, the remote sends a network request to get the initial position and duration of the currently playing song.
When the user adjusts the position of the tracker using the remote, it sends a network request to the music app to change the position of the song.
If the user uses the app on the desktop to change the position of the tracker, the app sends a network request to the remote with the new position of the tracker.
If the song is currently playing, the position of the tracker is updated every 0.5 seconds or so.
At the moment, the tracker is a UISlider which is backed by a "Player" model. Whenever the user changes the position on the slider, it updates the model and sends a network request, like so:
In NowPlayingViewController.m
[[slider rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UISlider *x) {
[playerModel seekToPosition:x.value];
}];
[RACObserve(playerModel, position) subscribeNext:^(id x) {
slider.value = player.position;
}];
In PlayerModel.m:
#property (nonatomic) NSTimeInterval position;
- (void)seekToPosition:(NSTimeInterval)position
{
self.position = position;
[self.client newRequestWithMethod:#"seekTo" params:#[positionArg] callback:NULL];
}
- (void)receivedPlayerUpdate:(NSDictionary *)json
{
self.position = [json objectForKey:#"position"]
}
The problem is when a user "fiddles" with the slider, and queues up a number of network requests which all come back at different times. The user could be have moved the slider again when a response is received, moving the slider back to a previous value.
My question: How do I use ReactiveCocoa correctly in this example, ensuring that updates from the network are dealt with, but only if the user hasn't moved the slider since?
In your GitHub thread about this you say that you want to consider the remote's updates as canonical. That's good, because (as Josh Abernathy suggested there), RAC or not, you need to pick one of the two sources to take priority (or you need timestamps, but then you need a reference clock...).
Given your code and disregarding RAC, the solution is just setting a flag in seekToPosition: and unsetting it using a timer. Check the flag in recievedPlayerUpdate:, ignoring the update if it's set.
By the way, you should use the RAC() macro to bind your slider's value, rather than the subscribeNext: that you've got:
RAC(slider, value) = RACObserve(playerModel, position);
You can definitely construct a signal chain to do what you want, though. You've got four signals you need to combine.
For the last item, the periodic update, you can use interval:onScheduler::
[[RACSignal interval:kPositionFetchSeconds
onScheduler:[RACScheduler scheduler]] map:^(id _){
return /* Request position over network */;
}];
The map: just ignores the date that the interval:... signal produces, and fetches the position. Since your requests and messages from the desktop have equal priority, merge: those together:
[RACSignal merge:#[desktopPositionSignal, timedRequestSignal]];
You decided that you don't want either of those signals going through if the user has touched the slider, though. This can be accomplished in one of two ways. Using the flag I suggested, you could filter: that merged signal:
[mergedSignal filter:^BOOL (id _){ return userFiddlingWithSlider; }];
Better than that -- avoiding extra state -- would be to build an operation out of a combination of throttle: and sample: that passes a value from a signal at a certain interval after another signal has not sent anything:
[mergedSignal sample:
[sliderSignal throttle:kUserFiddlingWithSliderInterval]];
(And you might, of course, want to throttle/sample the interval:onScheduler: signal in the same way -- before the merge -- in order to avoid unncessary network requests.)
You can put this all together in PlayerModel, binding it to position. You'll just need to give the PlayerModel the slider's rac_signalForControlEvents:, and then merge in the slider value. Since you're using the same signal multiple places in one chain, I believe that you want to "multicast" it.
Finally, use startWith: to get your first item above, the inital position from the desktop app, into the stream.
RAC(self, position) =
[[RACSignal merge:#[sampledSignal,
[sliderSignal map:^id(UISlider * slider){
return [slider value];
}]]
] startWith:/* Request position over network */];
The decision to break each signal out into its own variable or string them all together Lisp-style I'll leave to you.
Incidentally, I've found it helpful to actually draw out the signal chains when working on problems like this. I made a quick diagram for your scenario. It helps with thinking of the signals as entities in their own right, as opposed to worrying about the values that they carry.
Is it possible to get the last added value or previous values from a StreamController.broadcast / Stream.
Basically I have some subscriptions to streams like on('data') and on('ready') and I want for example that if someone listens to on('ready') after the event was triggered they can get the last value from the broadcast stream, eg. 'I\'m ready'.
Something like:
StreamController x = StreamController.broadcast();
first = x.first.then(doSomething);
x.add('oneOffEvent');
// first ~ 'oneOffEvent'
x.close()
other = x.last.then(doSomethingElse);
// other ~ 'oneOffEvent'
Is this even possible without caching the value somewhere else?
Broadcast Streams, by design, do not wait for subscriptions to fire events.
You can, however, do both a first and last subscription before adding events.
Add many subscriptions. That's the upside of broadcast subscriptions.