omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    Getting the battery level and battery state of the device

    Pythonista
    5
    13
    9595
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • ccc
      ccc last edited by

      Will ctype or other allow a Pythonista app to determine battery level or signal strength?

      1 Reply Last reply Reply Quote 0
      • JonB
        JonB last edited by

        here's battery level. this requires the beginning matter from

        https://gist.github.com/omz/9882a00abf59c6009fa4

        def get_battery_level():
           '''[[UIDevice currentDevice] setBatteryMonitoringEnabled:YES]]
           [[UIDevice currentDevice] batteryLevel];
           [[UIDevice currentDevice] batteryState];
           '''
           UIDevice = cls('UIDevice')    
           currentDevice = msg(UIDevice, c_void_p, 'currentDevice')
           msg(currentDevice,None,'setBatteryMonitoringEnabled:',[c_bool],True)
           b=msg(currentDevice,c_float,'batteryLevel')
           return b
        
        1 Reply Last reply Reply Quote 0
        • JonB
          JonB last edited by

          apparently Apple doesn't let apps access the wifi signal strength meter.

          1 Reply Last reply Reply Quote 0
          • omz
            omz last edited by

            You beat me to it. Anyway, here's a "standalone" version, but it does pretty much the same as @JonB's script:

            # coding: utf-8
            from ctypes import cdll, c_void_p, c_char_p, c_int, c_bool, c_float
            c = cdll.LoadLibrary(None)
            
            def get_current_device():
            	c.sel_registerName.restype = c_void_p
            	c.sel_registerName.argtypes = [c_char_p]
            	c.objc_getClass.restype = c_void_p
            	c.objc_getClass.argtypes = [c_char_p]
            	UIDevice = c.objc_getClass('UIDevice')
            	c.objc_msgSend.argtypes = [c_void_p, c_void_p]
            	c.objc_msgSend.restype = c_void_p
            	device = c.objc_msgSend(UIDevice, c.sel_registerName('currentDevice'))
            	return device
            
            def set_battery_monitoring_enabled(flag):
            	device = get_current_device()
            	c.objc_msgSend.argtypes = [c_void_p, c_void_p, c_bool]
            	c.objc_msgSend.restype = None
            	c.objc_msgSend(device, c.sel_registerName('setBatteryMonitoringEnabled:'), c_bool(flag))
            
            def get_battery_level():
            	device = get_current_device()
            	c.objc_msgSend.argtypes = [c_void_p, c_void_p]
            	c.objc_msgSend.restype = c_float
            	battery_level = c.objc_msgSend(device, c.sel_registerName('batteryLevel'))
            	return battery_level
            
            def get_battery_state():
            	device = get_current_device()
            	c.objc_msgSend.argtypes = [c_void_p, c_void_p]
            	c.objc_msgSend.restype = c_int
            	battery_state = c.objc_msgSend(device, c.sel_registerName('batteryState'))
            	return battery_state
            
            def main():
            	set_battery_monitoring_enabled(True)
            	battery_level = get_battery_level()
            	print 'Battery Level: %0.1f%%' % (battery_level * 100.0,)
            	battery_state = get_battery_state()
            	states = {0: 'unknown', 1: 'unplugged', 2: 'charging', 3: 'full'}
            	print 'Battery State: %i (%s)' % (battery_state, states.get(battery_state))
            	set_battery_monitoring_enabled(False)
            
            if __name__ == '__main__':
            	main()
            

            I don't think getting the signal level will be possible, at least it would be quite difficult as there's no public API for that.

            1 Reply Last reply Reply Quote 0
            • ccc
              ccc last edited by

              Thanks!! You guys are awesome. I doubt that I will ever be able to write ctype code (my mind does not work that way) but I am impressed with what it can do.

              I did make one change to @omz's code to create a context manager:

              import contextlib
              
              @contextlib.contextmanager
              def battery_monitoring_enabled():
                  set_battery_monitoring_enabled(True)
                  yield
                  set_battery_monitoring_enabled(False)
              

              Then we can remove the first and last lines of main() and replace them with a single with statement.

              def main():
                  with battery_monitoring_enabled():
                      battery_level = get_battery_level()
                      print 'Battery Level: %0.1f%%' % (battery_level * 100.0,)
                      battery_state = get_battery_state()
                      states = {0: 'unknown', 1: 'unplugged', 2: 'charging', 3: 'full'}
                      print 'Battery State: %i (%s)' % (battery_state, states.get(battery_state))
              

              The other nice feature of the with statement context manager is that set_battery_monitoring_enabled(False) will always get called even if an exception is thrown in the code underneath the with statement.

              Thanks again!

              1 Reply Last reply Reply Quote 1
              • Phuket2
                Phuket2 last edited by

                @ccc, I am not being a smart a**. In both above cases are they just not preparing the c data types to be used in a call the native API call, then also preparing the return data types to be recieved from the called function (msg) Maybe I am wrong, but that's what I make of it. Keep in mind I have only ever open Xcode accidentally viewing a plist file.

                I only mention, because you say you think you can not think like that. I find that hard to believe. If it is what I think it is, I think you would have your head around it in a very short period of time

                My humble opinion

                1 Reply Last reply Reply Quote 0
                • ccc
                  ccc last edited by

                  You are right... I just needed an easier to understand approach so I defined objc_call(return_type, obj, sel_name, *args) which simplifies get_current_device() allows get_battery_level() and get_battery_state() to be one liners. If parameter passing was working (fixed), then set_battery_monitoring_enabled() would also be a one liner.

                  EDIT: The code below has been edited to add the missing : as pointed out by @omz in the following post. This also makes set_battery_monitoring_enabled() a one liner.

                  # coding: utf-8
                  
                  #See: http://omz-forums.appspot.com/pythonista/post/5776493279969280
                  
                  import contextlib
                  from ctypes import cdll, c_void_p, c_char_p, c_int, c_bool, c_float
                  c = cdll.LoadLibrary(None)
                  
                  objc_types = cdll, c_void_p, c_char_p, c_int, c_bool, c_float
                  return_types = list(objc_types) + [None]
                  
                  def objc_call(return_type, obj, sel_name, *args):
                      assert return_type in return_types, '{} is an invalid return type.'.format(return_type)
                      assert isinstance(sel_name, basestring), '{} is an invalid sel_name.'.format(sel_name)
                      arg_types = [c_void_p, c_void_p]
                      if args:
                          fmt = 'arg[{}] has an invalid arg_type: {}.'
                          for i, arg in enumerate(args):
                              arg_type = type(arg)
                              assert arg_type in objc_types, fmt.format(i, arg_type)
                              arg_types.append(arg_type)
                      c.objc_msgSend.argtypes = arg_types
                      c.objc_msgSend.restype = return_type
                      return c.objc_msgSend(obj, c.sel_registerName(sel_name), *args)
                  
                  def get_current_device():
                      c.sel_registerName.restype = c_void_p
                      c.sel_registerName.argtypes = [c_char_p]
                      c.objc_getClass.restype = c_void_p
                      c.objc_getClass.argtypes = [c_char_p]
                      UIDevice = c.objc_getClass('UIDevice')
                      return objc_call(c_void_p, UIDevice, 'currentDevice')
                  
                  def set_battery_monitoring_enabled(flag):
                      objc_call(None, get_current_device(), 'setBatteryMonitoringEnabled:', c_bool(flag))
                  
                  import contextlib
                  @contextlib.contextmanager
                  def battery_monitoring_enabled():
                      set_battery_monitoring_enabled(True)
                      yield
                      set_battery_monitoring_enabled(False)
                  
                  def get_battery_level():
                      return objc_call(c_float, get_current_device(), 'batteryLevel')
                  
                  def get_battery_state():
                      return objc_call(c_int, get_current_device(), 'batteryState')
                  
                  def main():
                      with battery_monitoring_enabled():
                          battery_level = get_battery_level()
                          print 'Battery Level: %0.1f%%' % (battery_level * 100.0,)
                          battery_state = get_battery_state()
                          states = {0: 'unknown', 1: 'unplugged', 2: 'charging', 3: 'full'}
                          print 'Battery State: %i (%s)' % (battery_state, states.get(battery_state))
                  
                  if __name__ == '__main__':
                      main()
                  
                  1 Reply Last reply Reply Quote 0
                  • omz
                    omz last edited by

                    @ccc Your selector name is wrong, it should be 'setBatteryMonitoringEnabled:' (note the trailing colon).

                    1 Reply Last reply Reply Quote 0
                    • ccc
                      ccc last edited by

                      Perfect. Edited in the code above.

                      1 Reply Last reply Reply Quote 0
                      • Phuket2
                        Phuket2 last edited by

                        @ccc, nice. I can not run your code yet, still on 1.5. But I knew I would take you minutes to figure it out. I don't understand all the code you wrote, just the concept.

                        1 Reply Last reply Reply Quote 0
                        • Phuket2
                          Phuket2 last edited by

                          I was trying to think what this reminded me of. But, I remember now. In a way reminds me of Apple Events. As far as I remember, a lot of work with types. Also back then had worries about big and little endian. From memory, 80xxx series Vrs Motorola MC64xxx series processors supported opposing standards. I am not sure but I think byte order, byte boundaries are a thing of the past. Or at least if not, only in assembly language.

                          1 Reply Last reply Reply Quote 0
                          • henryiii
                            henryiii last edited by

                            @ccc: No, the code will not run with an exception. I've been caught by that before. This is the correct way to use the context manager if you want to be exception safe. (See context manager's docs)

                            import contextlib
                            
                            @contextlib.contextmanager
                            def battery_monitoring_enabled():
                                set_battery_monitoring_enabled(True)
                                try:
                                    yield
                                finally:
                                    set_battery_monitoring_enabled(False)
                            
                            1 Reply Last reply Reply Quote 0
                            • omz
                              omz last edited by

                              With the latest beta, the code becomes much simpler:

                              from objc_util import *
                              device = ObjCClass('UIDevice').currentDevice()
                              device.setBatteryMonitoringEnabled_(True)
                              print 'Battery level:', device.batteryLevel()
                              device.setBatteryMonitoringEnabled_(False)
                              
                              1 Reply Last reply Reply Quote 0
                              • First post
                                Last post
                              Powered by NodeBB Forums | Contributors