Accessing the LED flashlight
Is it possible to access the LED flashlight of an iPhone using Pythonista?
@ccc That's a great idea! Just tried it out, and yes, this works (in the current beta):
from ctypes import c_void_p, c_char_p, c_int, c_bool, cdll objc = cdll.LoadLibrary(None) objc.sel_getName.restype = c_char_p objc.sel_getName.argtypes = [c_void_p] objc.sel_registerName.restype = c_void_p objc.sel_registerName.argtypes = [c_char_p] objc.objc_getClass.argtypes = [c_char_p] objc.objc_getClass.restype = c_void_p # Some helper methods: def msg(obj, restype, sel, argtypes=None, *args): if argtypes is None: argtypes =  objc.objc_msgSend.argtypes = [c_void_p, c_void_p] + argtypes objc.objc_msgSend.restype = restype res = objc.objc_msgSend(obj, objc.sel_registerName(sel), *args) return res def cls(cls_name): return objc.objc_getClass(cls_name) def nsstr(s): return msg(cls('NSString'), c_void_p, 'stringWithUTF8String:', [c_char_p], s) def toggle_flashlight(): AVCaptureDevice = cls('AVCaptureDevice') device = msg(AVCaptureDevice, c_void_p, 'defaultDeviceWithMediaType:', [c_void_p], nsstr('vide')) has_torch = msg(device, c_bool, 'hasTorch') if not has_torch: raise RuntimeError('Device has no flashlight') current_mode = msg(device, c_int, 'torchMode') mode = 1 if current_mode == 0 else 0 msg(device, None, 'lockForConfiguration:', [c_void_p], None) msg(device, None, 'setTorchMode:', [c_int], mode) msg(device, None, 'unlockForConfiguration') if __name__ == '__main__': toggle_flashlight()
Thank you very much. I'll test it and will give feedback. May take some time though.
Background on my app:
I am currently writing an app which translates text to the NATO phonetic alphabet and also Morse code.
The translated text is spoken by using the speech modul in several languages (works really well) and the Morse code of the translation shall be flashing on pushing a button.
I'll also have predefined radio text and so on.
@chiefenne Sounds cool. Just to be clear: This will only work in the current beta version of Pythonista (1.6), not in the current version from the App Store (1.5). If you don't have the beta yet, feel free to send me an email with your Apple ID, and I'll invite you via TestFlight: http://omz-software.com/contact
A working prototype of a ui that lets you type in ascii letters and numbers and it will send out corresponding Morse code via the iOS flashlight. You need to have three files in the same directory: MorseView.py and morse_code.py and OMZ's code above put into a file called toggle_flashlight.py.
Run MorseView and change the text if you want and then tap the button. Of course this will only work if your iOS device has flashlight capabilities.
Wow. @ccc. Very cool. Thanks a lot. Though I did already some of the work;)
@omz. I'll apply for the beta. Thank you.
Thank you all.
Great... Please take the best of your work and the best of @omz's and mine and create something even better.
I would be interested in feedback. I don't "speak" Morse code so I would be interested to understand if real practitioners can understand the messages that this app sends. Is 2/10th of a second for
dot_secstoo fast or too slow for folks who really know their Morse code by heart? Would a slider to set the delay time be useful or unnecessary? If a slider would be useful, what would be the fastest and slowest times on that slider? If too fast, perhaps older/slower iPhone models might not process the on/off fast enough to provide clean messages.
Maybe you should edit the title of this thread to read:
Accessing the LED flashlight to send Morse code
I haven't had a chance to play with this, but the engineer in me asks if there are an accessible light sensors in the iphone that can "read" the incoming morse code?
@omz I have been updating my scripts to run on Python3... I have the sense that your
toggle_flashlightcode above could be streamlined these daze but I am not sure how to make it work under P3.
@ccc Yes, much less boilerplate code is required now:
from objc_util import ObjCClass def toggle_flashlight(): AVCaptureDevice = ObjCClass('AVCaptureDevice') device = AVCaptureDevice.defaultDeviceWithMediaType_('vide') if not device.hasTorch(): raise RuntimeError('Device has no flashlight') mode = device.torchMode() device.lockForConfiguration_(None) device.setTorchMode_((mode + 1) % 2) device.unlockForConfiguration() if __name__ == '__main__': toggle_flashlight()
This is not as Flash 😁 But the way the new Iphones use the screen as a flash for the front facing camera is an interesting possibility, also for devices that don't have a flash. I don't know, but could prove more reliable depending on distance etc. just a thought
iPhone 6s and 6s Plus contain a custom display chip that allows the retina display to briefly flash 3 times brighter than its usual maximum illuminance. No new API was added to support this feature. Since iOS 4, AVCaptureDevice has supported the -hasFlash, -isFlashModeSupported: and -flashMode properties. The iPhone 6s and 6s Plus front-facing cameras are the first front-facing iOS cameras to respond YES to the -hasFlash property. By setting the front-facing camera's flashMode to AVCaptureFlashModeOn or AVCaptureFlashModeAuto, the retina flash fires when a still image is captured (see AVCaptureStillImageOutput’s captureStillImageAsynchronouslyFromConnection:completionHandler:), just as the True Tone flash fires for rear-facing camera stills.
Hope this. Only works on 6s, 6s+, and SE.
@blmacbeth , ok thanks. I didn't know how they did it, was just nice, I have tried it several times on my 6s. But I think for the application here, you could still just fake it. Well, I think you could. I think somehow, it could be a better option to fake it with the screen as you have control over the timing and can calibrate etc. I just assume this would be hard to do with the flash. But I can see why the flash is appealing.
I brought my 6s without even knowing that it had the screen flash option.when I seen it, I just thought really, this is so simple and genius. But I get your point about the special API. Timing and white point/Lums etc... Critical.
Edit: but not so critical for dot, dash
@omz @Phuket2 @ccc would any of you happen to know how to set a custom brightness for the flashlight as some apps in the App Store can do? Passing anything other than an integer to
setTorchMode()throws an error...
I tried as to do
device.setTorchModeOnWithLevel_()as per some objective c, and trying to recognize patterns between the. Objc c code on stack overflow and your Python example..., but get error
no method found for selector..
Tbh, I have no idea what I'm doing with objc_util .... :(
As a side note I would gladly pay for a Pythonista objc-util tutorial for dummies.
@Tizzy According to a random Stack Overflow question I googled, the call looks like this in Objective-C:
BOOL success = [device setTorchModeOnWithLevel:0.2 error:&outError];
There is a second
errorargument that you need to pass, so in Python the call would look like this:
NULL- if not, you need to pass something else there, but I know nothing about Objective-C, so I can't help you with that)
I believe you can usually use None for error arguments (often you see error:nil in stackoverflow). I suspect passing zero is the same thing.
You can't pass zero for a parameter that expects a pointer.
This should work:
I noticed that passing 0.0 for the level crashes (haven't checked why exactly, it probably throws an exception), so you'd need to use
setTorchMode_(as above) to turn it back off again.
Something to note: running through the levels in 0.1 increments, it appears the only actual discrete levels are 1.0,0.9,0.8, and 0.4, unless there are finer increments between 0.4 and 1.0 ....
from objc_util import ObjCClass import time def toggle_flashlight(): AVCaptureDevice = ObjCClass('AVCaptureDevice') device = AVCaptureDevice.defaultDeviceWithMediaType_('vide') if not device.hasTorch(): raise RuntimeError('Device has no flashlight') mode = device.torchMode() device.lockForConfiguration_(None) if device.torchMode()>0: device.setTorchMode_((mode + 1) % 2) else: a =[1.0,0.9,0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1] for each in a: device.setTorchModeOnWithLevel_error_(each, None) print('level '+str(each)) time.sleep(.6) #device.setTorchModeOnWithLevel_error_(0.88, None) device.unlockForConfiguration() #device.setTorchModeOnWithLevel_error_(0.2, None) if __name__ == '__main__': toggle_flashlight()```