SocketScan Getting the Battery Level in Swift - ios

Whatever I seem to try I cannot currently get back the Battery level from the iOS/SocketScan API. I am using version 10.3.36, here is my code so far:
func onDeviceArrival(result: SKTRESULT, device deviceInfo: DeviceInfo!) {
print("onDeviceArrival:\(deviceInfo.getName())")
scanApiHelper.postGetBattery(deviceInfo, target: self, response: #selector(onGetBatteryInfo))
}
func onGetBatteryInfo(scanObj: ISktScanObject) {
let result:SKTRESULT = scanObj.Msg().Result()
print("GetBatteryInfo status:\(result)")
if (result == ESKT_NOERROR) {
let batterylevel = scanObj.Property().getUlong()
print("Battery is:\(batterylevel)")
} else {
print("Error GetBatteryInfo status:\(result)")
}
However, the values I get back are:
GetBatteryInfo status:0
Battery is:1677741312
If my code is correct then how do I make the Battery result I get back a meaningful result, like a percentage? If I'm way off then how do I get back info like the battery level, firmware version etc?
Thanks
David

EDIT: SKTBATTERY_GETCURLEVEL isn't supported in Swift. However, the docs explain that the battery level response includes the min, current and max levels encoded in the first, second and third bytes, respectively.
The following is equivalent to using SKTBATTERY_GETCURLEVEL
Swift
func onGetBatteryInfo(scanObj: ISktScanObject) {
let result:SKTRESULT = scanObj.Msg().Result()
if(SKTSUCCESS(result)){
let batteryInfo = scanObj.Property().getUlong();
let batteryMin = ((batteryInfo >> 4) & 0xff);
let batteryCurrent = ((batteryInfo >> 8) & 0xff);
let batteryMax = ((batteryInfo >> 12) & 0xff);
let batteryPercentage = batteryCurrent / (batteryMax - batteryMin);
print("Battery is:\(batteryPercentage)")
self.setBatteryLevel = batteryPercentage
self.tableView.reloadData
} else {
print("Error GetBatteryInfo status:\(result)")
}
}
Objective-C
-(void) onGetBatteryInfo:(ISktScanObject*)scanObj {
SKTRESULT result=[[scanObj Msg]Result];
if(SKTSUCCESS(result)){
long batteryLevel = SKTBATTERY_GETCURLEVEL([[scanObj Property] getUlong]);
NSLog(#"BatteryInfo %ld", batteryLevel);
[self setBatteryLevel:batteryLevel];
[self.tableView reloadData];
} else {
NSLog(#"Error GetBatteryInfo status: %ld",result);
}
}

Here's code I use. Theres a variable defined in appDelegate for the batteryPercentage, and that is read when the v value is needed. The value is updated each 120 seconds by a timer, this way actions can occur as the level drops etc.
func onBatteryLevel (scanObj: ISktScanObject) {
let result: SKTRESULT = scanObj.Msg().Result()
if (SKTRESULT(result) > -1) {
let property: ISktScanProperty = scanObj.Property()
var batteryLevel = property.getUlong()
#if arch(x86_64) || arch(arm64)
batteryLevel = (batteryLevel<<(48))>>(56)
#else
batteryLevel = (batteryLevel<<(48-32))>>(56-32)
#endif
batteryPercentage = Int(batteryLevel)
} else {
debug ("data error \(result)")
}
}

For Swift 4 I just came across this problem and came up with the following solution.
var lastDeviceConnected : CaptureHelperDevice? {
didSet {
guard let lastDevice = self.lastDeviceConnected else { return }
lastDevice.getBatteryLevelWithCompletionHandler { result, batteryLevel in
guard result == SKTResult.E_NOERROR, let batteryLevel = batteryLevel else { return }
let minimum = SKTHelper.getMinimumLevel(fromBatteryLevel: Int(batteryLevel))
let maximum = SKTHelper.getMaximumLevel(fromBatteryLevel: Int(batteryLevel))
let current = SKTHelper.getCurrentLevel(fromBatteryLevel: Int(batteryLevel))
print("minimum: \(minimum)")
print("maximum: \(maximum)")
print("current: \(current)")
// current is out battery level in %
// minimum and maximum could for example be used for
// for a slider or something that visually presents
// the battery status
}
}
}
In my example I'm not handling the case that there could be no device or that the battery status might not have been retrieved as expected. I simply guard / return. In your example you might want to handle the issue.

Related

Invalid callback id received by sendPluginResult - Cordova Plugin iOS

I am trying to implement cordova plugin for ARKit in my ionic app which measures distance between two points. I am able to successfully take the measurements but I am not able to get the measured data back as a result in my ionic app. Below is how my code looks like:
MeasurePlugin+ViewControllerDelegate.swift
extension MeasurePlugin: ViewControllerDelegate {
func allowMultiple() -> Bool {
return allowMultiplePoints ?? false;
}
//Dismiss View
func closeView() {
let data = myViewController.getMeasures();
var result: CDVPluginResult
if (!allowMultiple() && data.count > 0) {
result = CDVPluginResult(status: CDVCommandStatus_OK, messageAs: data[0])
} else {
result = CDVPluginResult(status: CDVCommandStatus_OK, messageAs: data)
}
NSLog(result.message as! String); //getting measured value here
myViewController.view.removeFromSuperview()
self.myViewController = nil
result.setKeepCallbackAs(true)
commandDelegate!.send(result, callbackId: finishListenerCallbackId)
}
#objc func setFinishListener(_ command: CDVInvokedUrlCommand) {
finishListenerCallbackId = command.callbackId
}
}
My www/cordova-plugin-measure.js file looks like:
var exec = require('cordova/exec');
var pluginName = 'MeasurePlugin';
/**
* Callback when the view is dismissed
*/
exports.onFinish = function (success = function(){
console.log("OnFinish Success");
}, error = function(){}) {
exec(success, error, pluginName, 'setFinishListener');
};
So when i try to dismiss the view by calling closeView() function, I am getting error in xcode as "Invalid callback id received by sendPluginResult". Please note, I do get my measure value inside result (notice NSLog(result.message as! String); code) but it is not sending that value back to my ionic component. Any idea what am I doing wrong here?

Driving activity confidence is low all time in iOS 11

I've an application which detects user driving and do some process internally. After the iOS update from iOS-10 to iOS-11, the application stops working. Still the application works great in iOS 10, but fails in iOS 11.
After debugging I found that the CMMotionActivityManager had the issue. The confidence of the automotive activity is low (0) at most of the time even though the user is actually driving.
Since this I can't identify the complete driving behaviour of the user.
Here is the sample code I'm using.
if CMMotionActivityManager.isActivityAvailable() {
motion.startActivityUpdates(to: OperationQueue.current!, withHandler: {
activityData
in
self.handleActivityData(activityData!)
})
}
func handleActivityData(_ activityData: CMMotionActivity) {
let rawConfidence = activityData.confidence.rawValue
// Confidence = 2 -> high
// Confidence = 0 -> low
if rawConfidence > 0 {
if activityData.automotive {
drivingActivity = true
Log.info("Activity : Automotive")
} else {
drivingActivity = false
var text = "Empty"
if activityData.walking {
text = "Walking"
} else if activityData.running {
text = "Running"
} else if activityData.stationary {
text = "Stationary"
} else if activityData.cycling {
text = "Cycling"
} else if activityData.unknown {
text = "Unknown"
}
}
}
}
Do anyone face the same issue?
Any help is appreciated.

Create a moving average (and other FIR filters) using ReactiveCocoa

I'm still getting started with ReactiveCocoa and functional reactive programming concepts, so maybe this is a dumb question.
ReactiveCocoa seem naturally designed to react to streams of live data, touch events or accelerometer sensor input etc.
Is it possible to apply finite impulse response filters in ReactiveCocoa in an easy, reactive fashion? Or if not, what would be the least-ugly hacky way of doing this? How would one go about implementing something like a simple moving average?
Ideally looking for an Swift 2 + RA4 solution but also interested in if this is possible at all in Objective C and RA2/RA3.
What you actually need is a some sort of period buffer, which will keep a period of values buffered and only start sending out when the buffer has reached capacity (the code below is heavenly inspired on takeLast operator)
extension SignalType {
func periodBuffer(period:Int) -> Signal<[Value], Error> {
return Signal { observer in
var buffer: [Value] = []
buffer.reserveCapacity(period)
return self.observe { event in
switch event {
case let .Next(value):
// To avoid exceeding the reserved capacity of the buffer, we remove then add.
// Remove elements until we have room to add one more.
while (buffer.count + 1) > period {
buffer.removeAtIndex(0)
}
buffer.append(value)
if buffer.count == period {
observer.sendNext(buffer)
}
case let .Failed(error):
observer.sendFailed(error)
case .Completed:
observer.sendCompleted()
case .Interrupted:
observer.sendInterrupted()
}
}
}
}
}
based on that you can map it to any algorithm you want
let pipe = Signal<Int,NoError>.pipe()
pipe.0
.periodBuffer(3)
.map { Double($0.reduce(0, combine: +))/Double($0.count) } // simple moving average
.observeNext { print($0) }
pipe.1.sendNext(10) // does nothing
pipe.1.sendNext(11) // does nothing
pipe.1.sendNext(15) // prints 12
pipe.1.sendNext(7) // prints 11
pipe.1.sendNext(9) // prints 10.3333
pipe.1.sendNext(6) // prints 7.3333
Probably the scan signal operator is what you're looking for. Inspired by Andy Jacobs' answer, I came up with something like this (a simple moving average implementation):
let (signal, observer) = Signal<Int,NoError>.pipe()
let maxSamples = 3
let movingAverage = signal.scan( [Int]() ) { (previousSamples, nextValue) in
let samples : [Int] = previousSamples.count < maxSamples ? previousSamples : Array(previousSamples.dropFirst())
return samples + [nextValue]
}
.filter { $0.count >= maxSamples }
.map { $0.average }
movingAverage.observeNext { (next) -> () in
print("Next: \(next)")
}
observer.sendNext(1)
observer.sendNext(2)
observer.sendNext(3)
observer.sendNext(4)
observer.sendNext(42)
Note: I had to move average method into a protocol extension, otherwise the compiler would complain that the expression was too complex. I used a nice solution from this answer:
extension Array where Element: IntegerType {
var total: Element {
guard !isEmpty else { return 0 }
return reduce(0){$0 + $1}
}
var average: Double {
guard let total = total as? Int where !isEmpty else { return 0 }
return Double(total)/Double(count)
}
}

alternative to QOS_CLASS_UTILITY on ios7 using swift

I'm using QOS_CLASS_UTILITY in dispatch_async to perform some async operations.Sample code is below :
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.value), 0)) {
var ip = "163.289.2." + "\(i)"
let foundThisOne = Ping.getIPAddress(ip)
dispatch_async(serialQueue) {
if !resultFound {
resultFound = foundThisOne
numProcessed++
if resultFound {
completion(existingIP:ip)
} else if numProcessed == 256 {
completion(existingIP: nil)
}
}
}
}
}
But "QOS_CLASS_UTILITY" is available on iOS8 onwards and now i need to support the same logic on iOS7 also.So what is better alternative to
QOS_CLASS_UTILITY which works on iOS7 using swift.
According to documentation (see Global Concurrent Queues) I'd say you can replace it with:
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)

iOS swift while loop freeze my app

I'm developing an app where a user choose his accommodation and pick a time from date picker so the user can know what time the buss will arrive to his accommodation , on the simulator and iPad I'm having the appropriate results however I'm facing this issue on iPhone real device, this is the code :
mTimeString = "5:07 AM"
it will be searched in the array if not found , minus seconds until it matches results in the array . but while loop is freezing my app , i tried to surround it by :
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT ,0)
dispatch_async(queue){}
its not freezing anymore but the I'm not getting the results according to the array
if(defaults.stringForKey(accommodationChoiceKey)?.toInt() == 40) {
var array = ["12:05 AM","12:25 AM","12:45 AM","1:05 AM","1:25 AM","1:45 AM","2:05 AM","2:25 AM","2:45 AM","3:05 AM","3:25 AM","3:45 AM","4:05 AM","4:25 AM","4:45 AM","5:05 AM","5:25 AM","5:45 AM","6:05 AM","6:25 AM","6:45 AM","7:05 AM","7:25 AM","7:45 AM","8:05 AM","8:25 AM","8:45 AM","9:05 AM","9:25 AM","9:45 AM","10:05 AM","10:25 AM","10:45 AM","11:05 AM","11:25 AM","11:45 AM","12:05 PM","12:25 PM","12:45 PM","1:05 PM","1:25 PM","1:45 PM","2:05 PM","2:25 PM","2:45 PM","3:05 PM","3:25 PM","3:45 PM","4:05 PM","4:25 PM","4:45 PM","5:05 PM","5:25 PM","5:45 PM","6:05 PM","6:25 PM","6:45 PM","7:05 PM","7:25 PM","7:45 PM","8:05 PM","8:25 PM","8:45 PM","9:05 PM","9:25 PM","9:45 PM","10:05 PM","10:25 PM","10:45 PM","11:05 PM","11:25 PM","11:45 PM"]
while (find(array, mTimeString) == nil) {
choiceToSeconds--
var newmTime = x.dateByAddingTimeInterval(Double(choiceToSeconds))
var mTimeString = dateFormatter.stringFromDate(newmTime)
if (find(array, mTimeString) != nil) {
transportationLabel.text = mTimeString
break
}
getReady(newmTime)
}
}
You are calculating on Background thread which is okay , but you should NEVER update UI element from background thread ( transportationLabel.text = mTimeString ) , this should be done from main Thread , for example
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
if(defaults.stringForKey(accommodationChoiceKey)?.toInt() == 40) {
var array = ["12:05 AM","12:25 AM","12:45 AM","1:05 AM","1:25 AM","1:45 AM","2:05 AM","2:25 AM","2:45 AM","3:05 AM","3:25 AM","3:45 AM","4:05 AM","4:25 AM","4:45 AM","5:05 AM","5:25 AM","5:45 AM","6:05 AM","6:25 AM","6:45 AM","7:05 AM","7:25 AM","7:45 AM","8:05 AM","8:25 AM","8:45 AM","9:05 AM","9:25 AM","9:45 AM","10:05 AM","10:25 AM","10:45 AM","11:05 AM","11:25 AM","11:45 AM","12:05 PM","12:25 PM","12:45 PM","1:05 PM","1:25 PM","1:45 PM","2:05 PM","2:25 PM","2:45 PM","3:05 PM","3:25 PM","3:45 PM","4:05 PM","4:25 PM","4:45 PM","5:05 PM","5:25 PM","5:45 PM","6:05 PM","6:25 PM","6:45 PM","7:05 PM","7:25 PM","7:45 PM","8:05 PM","8:25 PM","8:45 PM","9:05 PM","9:25 PM","9:45 PM","10:05 PM","10:25 PM","10:45 PM","11:05 PM","11:25 PM","11:45 PM"]
while (find(array, mTimeString) == nil) {
choiceToSeconds--
var newmTime = x.dateByAddingTimeInterval(Double(choiceToSeconds))
var mTimeString = dateFormatter.stringFromDate(newmTime)
if (find(array, mTimeString) != nil) {
// This should be done on Main Thread
dispatch_async(dispatch_get_main_queue()) {
transportationLabel.text = mTimeString
}
break
}
getReady(newmTime)
}
}
}
Try replacing
transportationLabel.text = mTimeString
by
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
transportationLabel.text = mTimeString
}];
UI operations must be executed from main thread
EDIT:
In Swift:
NSOperationQueue.mainQueue().addOperationWithBlock({
transportationLabel.text = mTimeString
})

Resources