<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0">

<channel>

<title>Just Developer Blog, заметки с тегом: IOPowerSources</title>
<link>http://bibobo.ru/tags/iopowersources/</link>
<description></description>
<generator>E2 (v3254; Aegea)</generator>

<item>
<title>Получение информации о состоянии батареи в MacOS (выполнением консольной команды)</title>
<guid isPermaLink="true">http://bibobo.ru/all/poluchenie-informacii-o-sostoyanii-batarei-v-macos-vypolneniem-k/</guid>
<link>http://bibobo.ru/all/poluchenie-informacii-o-sostoyanii-batarei-v-macos-vypolneniem-k/</link>
<comments>http://bibobo.ru/all/poluchenie-informacii-o-sostoyanii-batarei-v-macos-vypolneniem-k/</comments>
<description>&lt;p&gt;После обновления &lt;i&gt;MacOS&lt;/i&gt; до &lt;i&gt;Sierra&lt;/i&gt; я столкнулся с проблемой — в статус баре исчезла информация об оставшемся времени работы от батареи. Проблема на самом деле решается очень просто — можно установить стороннее приложение &lt;a href="https://github.com/codler/Battery-Time-Remaining"&gt;Battery-Time-Remaining&lt;/a&gt;, которое является настоящим швейцарским ножом в области работы с батареей макбука. Но мне захотелось разобраться как можно получить информацию об оставшемся времени работы от батареи в &lt;i&gt;MacOS&lt;/i&gt; и написать свое приложение для статус бара.&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;Существует два способа получения состояния батареи в &lt;i&gt;MacOS&lt;/i&gt;:&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;(правильный) при помощи набора функций &lt;a href="https://developer.apple.com/reference/iokit"&gt;IOPowerFunctions&lt;/a&gt; фреймворка &lt;i&gt;IOKit&lt;/i&gt;;&lt;/li&gt;
&lt;li&gt;(быстрый) выполнением консольной команды “&lt;b&gt;pmset -g batt&lt;/b&gt;” и парсинга вывода.&lt;br /&gt;
&lt;input id="button_macos_pmset_battery_remaining" type="button" onclick="toggle(cut_macos_pmset_battery_remaining, button_macos_pmset_battery_remaining)" value="Читать дальше" hiddenText="Свернуть" visibleText="Читать дальше"&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div id="cut_macos_pmset_battery_remaining" style="display: none;" displayOld=""&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Воспользуемся вторым способом (приложение не нацелено на продакшн и его нужно реализовать быстро) и напишем приложение, которое будет выводить оставшееся время работы ноутбука от батареи в статус баре.&lt;/p&gt;
&lt;p&gt;Принцип работы будет следующим:&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;используя &lt;i&gt;IOKit&lt;/i&gt; подпишемся на нотификации системы об изменении состояния батареи для обновления данных в статус баре (можно конечно сделать при помощи таймера, но это лишняя трата ресурсов системы)&lt;/li&gt;
&lt;li&gt;при изменении состояния батареи используя &lt;i&gt;NSTask&lt;/i&gt; выполняем консольную команду и захватываем ее вывод&lt;/li&gt;
&lt;li&gt;парсим вывод и пересоздаем меню в статус баре&lt;br /&gt;
&lt;br/&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Чтобы все успешно заработало необходимо добавить &lt;i&gt;IOKit.framework&lt;/i&gt; и зареференсить хедер &lt;i&gt;IOKit/ps/IOPowerSources.h&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;Так же, нужно не забыть снять галочку &lt;i&gt;Visible At Launch&lt;/i&gt; с &lt;i&gt;NSWindow&lt;/i&gt; в &lt;i&gt;Xib&lt;/i&gt; интерфейса приложения (это запретит окну автоматически появляться на экране, нам нужен только статус бар)&lt;/p&gt;
&lt;p&gt;Чтобы скрыть иконку приложения из дока &lt;i&gt;MacOS&lt;/i&gt; нужно в &lt;i&gt;info.plist&lt;/i&gt; установить флаг &lt;i&gt;LSUIElement&lt;/i&gt; в значение &lt;i&gt;YES&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Capture Power Source updates and make sure our callback is called
    CFRunLoopSourceRef loop = IOPSNotificationCreateRunLoopSource(PowerSourceChanged, (__bridge void *)self);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), loop, kCFRunLoopDefaultMode);
    CFRelease(loop);
    
    self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
    self.statusItem.title = @&amp;quot;Checking...&amp;quot;;
    self.statusItem.highlightMode = YES;
    
    //request battery status for the first time
    [self requestBatteryStatus];
}

// IOPS notification callback on power source change
static void PowerSourceChanged(void * context)
{
    //requesting battery status
    AppDelegate *self = (__bridge AppDelegate *)context;
    [self requestBatteryStatus];
}

-(void)requestBatteryStatus
{
    //create task &amp;amp; launch 'pmset' to get battery status string
    NSPipe *pipe = [NSPipe pipe];
    NSFileHandle *file = pipe.fileHandleForReading;
    
    NSTask *task = [[NSTask alloc] init];
    task.launchPath = @&amp;quot;/usr/bin/pmset&amp;quot;;
    task.arguments = @[@&amp;quot;-g&amp;quot;, @&amp;quot;batt&amp;quot;];
    task.standardOutput = pipe;
    
    [task launch];
    
    NSData *data = [file readDataToEndOfFile];
    [file closeFile];
    
    //parse string to get particular items
    NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSString *statusData = [result componentsSeparatedByString:@&amp;quot;	&amp;quot;][1];
    NSArray *status = [statusData componentsSeparatedByString:@&amp;quot;;&amp;quot;];
    
    //battery charge percent
    NSString *batteryPercent = [status[0] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
    //charging status
    NSString *batteryChanging = [status[1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
    
    //ETA
    NSString *batteryEstimate = [status[2] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
    //remaining hours
    NSString *batteryRemaining = batteryEstimate;
    NSRange range = [batteryEstimate rangeOfString:@&amp;quot;present&amp;quot;];
    if(range.length &amp;gt; 0){
        batteryEstimate = [[[batteryEstimate substringToIndex:range.location] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@&amp;quot;()&amp;quot;]];
        
        batteryRemaining = [batteryEstimate componentsSeparatedByString:@&amp;quot; &amp;quot;][0];
    }
    
    //recrete status bar menu
    NSMenu *menu = [[NSMenu alloc] init];
    [menu addItemWithTitle:batteryPercent action:nil keyEquivalent:@&amp;quot;&amp;quot;];
    [menu addItemWithTitle:batteryEstimate action:nil keyEquivalent:@&amp;quot;&amp;quot;];
    [menu addItemWithTitle:batteryChanging action:nil keyEquivalent:@&amp;quot;&amp;quot;];
    
    [menu addItem:[NSMenuItem separatorItem]];
    
    [menu addItemWithTitle:@&amp;quot;Exit&amp;quot; action:@selector(exitAction:) keyEquivalent:@&amp;quot;&amp;quot;];
    
    self.statusItem.menu = menu;
    
    self.statusItem.title = [batteryRemaining stringByAppendingString:@&amp;quot; ETA&amp;quot;];
    
    NSLog(@&amp;quot;Request success!&amp;quot;);
}

-(void)exitAction:(id)sender
{
    exit(0);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
<pubDate>Mon, 02 Jan 2017 12:44:55 +0300</pubDate>
</item>


</channel>
</rss>