Simple RGB LED controller for Mac OS X
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AppDelegate.m 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. //
  2. // AppDelegate.m
  3. // CaseLights
  4. //
  5. // Created by Thomas Buck on 21.12.15.
  6. // Copyright © 2015 xythobuz. All rights reserved.
  7. //
  8. #import "AppDelegate.h"
  9. #import "Serial.h"
  10. #import "GPUStats.h"
  11. #import <SystemInfoKit/SystemInfoKit.h>
  12. #define PREF_SERIAL_PORT @"SerialPort"
  13. #define PREF_LIGHTS_STATE @"LightState"
  14. #define PREF_LED_MODE @"LEDMode"
  15. #define TEXT_CPU_USAGE @"CPU Usage"
  16. #define TEXT_RAM_USAGE @"RAM Usage"
  17. #define TEXT_GPU_USAGE @"GPU Usage"
  18. #define TEXT_VRAM_USAGE @"VRAM Usage"
  19. #define TEXT_CPU_TEMPERATURE @"CPU Temperature"
  20. #define TEXT_GPU_TEMPERATURE @"GPU Temperature"
  21. #define TEXT_RGB_FADE @"RGB Fade"
  22. #define TEXT_HSV_FADE @"HSV Fade"
  23. #define KEY_CPU_TEMPERATURE @"TC0D"
  24. #define KEY_GPU_TEMPERATURE @"TG0D"
  25. @interface AppDelegate ()
  26. @property (strong) NSStatusItem *statusItem;
  27. @property (strong) NSImage *statusImage;
  28. @property (strong) NSDictionary *staticColors;
  29. @property (strong) NSTimer *animation;
  30. @property (strong) Serial *serial;
  31. @property (strong) NSMenuItem *lastLEDMode;
  32. @end
  33. @implementation AppDelegate
  34. @synthesize statusMenu, application;
  35. @synthesize menuColors, menuAnimations, menuVisualizations, menuPorts;
  36. @synthesize buttonOff, buttonLights;
  37. @synthesize statusItem, statusImage;
  38. @synthesize staticColors, animation;
  39. @synthesize serial, lastLEDMode;
  40. - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
  41. serial = [[Serial alloc] init];
  42. lastLEDMode = nil;
  43. animation = nil;
  44. // Prepare status bar menu
  45. statusImage = [NSImage imageNamed:@"MenuIcon"];
  46. [statusImage setTemplate:YES];
  47. statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
  48. [statusItem setImage:statusImage];
  49. [statusItem setMenu:statusMenu];
  50. // Set default configuration values, load existing ones
  51. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  52. NSMutableDictionary *appDefaults = [NSMutableDictionary dictionaryWithObject:@"" forKey:PREF_SERIAL_PORT];
  53. [appDefaults setObject:[NSNumber numberWithBool:NO] forKey:PREF_LIGHTS_STATE];
  54. [appDefaults setObject:@"" forKey:PREF_LED_MODE];
  55. [store registerDefaults:appDefaults];
  56. [store synchronize];
  57. NSString *savedPort = [store stringForKey:PREF_SERIAL_PORT];
  58. BOOL turnOnLights = [store boolForKey:PREF_LIGHTS_STATE];
  59. NSString *lastMode = [store stringForKey:PREF_LED_MODE];
  60. // Prepare serial port menu
  61. NSArray *ports = [Serial listSerialPorts];
  62. if ([ports count] > 0) {
  63. [menuPorts removeAllItems];
  64. for (int i = 0; i < [ports count]; i++) {
  65. // Add Menu Item for this port
  66. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[ports objectAtIndex:i] action:@selector(selectedSerialPort:) keyEquivalent:@""];
  67. [menuPorts addItem:item];
  68. // Set Enabled if it was used the last time
  69. if ((savedPort != nil) && [[ports objectAtIndex:i] isEqualToString:savedPort]) {
  70. [[menuPorts itemAtIndex:i] setState:NSOnState];
  71. // Try to open serial port
  72. [serial setPortName:savedPort];
  73. if ([serial openPort]) {
  74. // Unselect it when an error occured opening the port
  75. [[menuPorts itemAtIndex:i] setState:NSOffState];
  76. }
  77. }
  78. }
  79. }
  80. // Select "Off" button if it was last selected
  81. if ([lastMode isEqualToString:@""]) {
  82. [buttonOff setState:NSOffState];
  83. [self turnLEDsOff:buttonOff];
  84. }
  85. // Prepare static colors menu
  86. staticColors = [NSDictionary dictionaryWithObjectsAndKeys:
  87. [NSColor colorWithCalibratedRed:1.0f green:0.0f blue:0.0f alpha:0.0f], @"Red",
  88. [NSColor colorWithCalibratedRed:0.0f green:1.0f blue:0.0f alpha:0.0f], @"Green",
  89. [NSColor colorWithCalibratedRed:0.0f green:0.0f blue:1.0f alpha:0.0f], @"Blue",
  90. [NSColor colorWithCalibratedRed:0.0f green:1.0f blue:1.0f alpha:0.0f], @"Cyan",
  91. [NSColor colorWithCalibratedRed:1.0f green:0.0f blue:1.0f alpha:0.0f], @"Magenta",
  92. [NSColor colorWithCalibratedRed:1.0f green:1.0f blue:0.0f alpha:0.0f], @"Yellow",
  93. [NSColor colorWithCalibratedRed:1.0f green:1.0f blue:1.0f alpha:0.0f], @"White",
  94. nil];
  95. for (NSString *key in [staticColors allKeys]) {
  96. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:key action:@selector(selectedVisualization:) keyEquivalent:@""];
  97. if ([key isEqualToString:lastMode]) {
  98. [self selectedVisualization:item];
  99. }
  100. [menuColors addItem:item];
  101. }
  102. // Prepare animations menu
  103. NSArray *animationStrings = [NSArray arrayWithObjects:
  104. TEXT_RGB_FADE,
  105. TEXT_HSV_FADE,
  106. nil];
  107. for (NSString *key in animationStrings) {
  108. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:key action:@selector(selectedVisualization:) keyEquivalent:@""];
  109. if ([key isEqualToString:lastMode]) {
  110. [self selectedVisualization:item];
  111. }
  112. [menuAnimations addItem:item];
  113. }
  114. JSKSystemMonitor *systemMonitor = [JSKSystemMonitor systemMonitor];
  115. #ifdef DEBUG
  116. JSKMCPUUsageInfo cpuUsageInfo = systemMonitor.cpuUsageInfo;
  117. NSLog(@"CPU Usage: %.3f%%\n", cpuUsageInfo.usage);
  118. JSKMMemoryUsageInfo memoryUsageInfo = systemMonitor.memoryUsageInfo;
  119. NSLog(@"Memory Usage: %.2fGB Free + %.2fGB Used = %.2fGB\n", memoryUsageInfo.freeMemory / (1024.0 * 1024.0 * 1024.0), memoryUsageInfo.usedMemory / (1024.0 * 1024.0 * 1024.0), (memoryUsageInfo.freeMemory + memoryUsageInfo.usedMemory) / (1024.0 * 1024.0 * 1024.0));
  120. #endif
  121. // Add CPU Usage menu item
  122. NSMenuItem *cpuUsageItem = [[NSMenuItem alloc] initWithTitle:TEXT_CPU_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  123. if ([lastMode isEqualToString:TEXT_CPU_USAGE]) {
  124. [self selectedVisualization:cpuUsageItem];
  125. }
  126. [menuVisualizations addItem:cpuUsageItem];
  127. // Add Memory Usage item
  128. NSMenuItem *memoryUsageItem = [[NSMenuItem alloc] initWithTitle:TEXT_RAM_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  129. if ([lastMode isEqualToString:TEXT_RAM_USAGE]) {
  130. [self selectedVisualization:memoryUsageItem];
  131. }
  132. [menuVisualizations addItem:memoryUsageItem];
  133. // Check if GPU Stats are available, add menu items if so
  134. NSNumber *usage;
  135. NSNumber *freeVRAM;
  136. NSNumber *usedVRAM;
  137. if ([GPUStats getGPUUsage:&usage freeVRAM:&freeVRAM usedVRAM:&usedVRAM] != 0) {
  138. NSLog(@"Error reading GPU information\n");
  139. } else {
  140. NSMenuItem *itemUsage = [[NSMenuItem alloc] initWithTitle:TEXT_GPU_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  141. if ([lastMode isEqualToString:TEXT_GPU_USAGE]) {
  142. [self selectedVisualization:itemUsage];
  143. }
  144. [menuVisualizations addItem:itemUsage];
  145. NSMenuItem *itemVRAM = [[NSMenuItem alloc] initWithTitle:TEXT_VRAM_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  146. if ([lastMode isEqualToString:TEXT_VRAM_USAGE]) {
  147. [self selectedVisualization:itemVRAM];
  148. }
  149. [menuVisualizations addItem:itemVRAM];
  150. }
  151. // Check available temperatures and add menu items
  152. JSKSMC *smc = [JSKSMC smc];
  153. for (int i = 0; i < [[smc workingTempKeys] count]; i++) {
  154. NSString *key = [smc.workingTempKeys objectAtIndex:i];
  155. #ifdef DEBUG
  156. NSString *name = [smc humanReadableNameForKey:key];
  157. NSLog(@"Sensor \"%@\": \"%@\"\n", key, name);
  158. #endif
  159. if ([key isEqualToString:KEY_CPU_TEMPERATURE]) {
  160. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:TEXT_CPU_TEMPERATURE action:@selector(selectedVisualization:) keyEquivalent:@""];
  161. if ([lastMode isEqualToString:TEXT_CPU_TEMPERATURE]) {
  162. [self selectedVisualization:item];
  163. }
  164. [menuVisualizations addItem:item];
  165. }
  166. if ([key isEqualToString:KEY_GPU_TEMPERATURE]) {
  167. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:TEXT_GPU_TEMPERATURE action:@selector(selectedVisualization:) keyEquivalent:@""];
  168. if ([lastMode isEqualToString:TEXT_GPU_TEMPERATURE]) {
  169. [self selectedVisualization:item];
  170. }
  171. [menuVisualizations addItem:item];
  172. }
  173. }
  174. // Restore previously used lights configuration
  175. if (turnOnLights) {
  176. // Turn on lights
  177. if ([serial isOpen]) {
  178. [serial sendString:@"UV 1\n"];
  179. }
  180. [buttonLights setState:NSOnState];
  181. } else {
  182. // Turn off lights
  183. if ([serial isOpen]) {
  184. [serial sendString:@"UV 0\n"];
  185. }
  186. }
  187. }
  188. - (void)applicationWillTerminate:(NSNotification *)aNotification {
  189. // Close serial port, if it was opened
  190. if ([serial isOpen]) {
  191. [serial closePort];
  192. }
  193. }
  194. - (IBAction)relistSerialPorts:(id)sender {
  195. // Refill port list
  196. NSArray *ports = [Serial listSerialPorts];
  197. [menuPorts removeAllItems];
  198. for (int i = 0; i < [ports count]; i++) {
  199. // Add Menu Item for this port
  200. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[ports objectAtIndex:i] action:@selector(selectedSerialPort:) keyEquivalent:@""];
  201. [menuPorts addItem:item];
  202. // Mark it if it is currently open
  203. if ([serial isOpen]) {
  204. if ([[ports objectAtIndex:i] isEqualToString:[serial portName]]) {
  205. [[menuPorts itemAtIndex:i] setState:NSOnState];
  206. }
  207. }
  208. }
  209. }
  210. - (IBAction)turnLEDsOff:(NSMenuItem *)sender {
  211. if ([sender state] == NSOffState) {
  212. lastLEDMode = nil;
  213. // Turn off all other LED menu items
  214. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  215. if ([[menuColors itemAtIndex:i] state] == NSOnState) {
  216. lastLEDMode = [menuColors itemAtIndex:i];
  217. }
  218. [[menuColors itemAtIndex:i] setState:NSOffState];
  219. }
  220. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  221. if ([[menuAnimations itemAtIndex:i] state] == NSOnState) {
  222. lastLEDMode = [menuAnimations itemAtIndex:i];
  223. }
  224. [[menuAnimations itemAtIndex:i] setState:NSOffState];
  225. }
  226. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  227. if ([[menuVisualizations itemAtIndex:i] state] == NSOnState) {
  228. lastLEDMode = [menuVisualizations itemAtIndex:i];
  229. }
  230. [[menuVisualizations itemAtIndex:i] setState:NSOffState];
  231. }
  232. // Turn on "off" menu item
  233. [sender setState:NSOnState];
  234. // Store changed value in preferences
  235. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  236. [store setObject:@"" forKey:PREF_LED_MODE];
  237. [store synchronize];
  238. #ifdef DEBUG
  239. NSLog(@"Stored new mode: \"off\"!\n");
  240. #endif
  241. // Send command to turn off LEDs
  242. if ([serial isOpen]) {
  243. [serial sendString:@"RGB 0 0 0\n"];
  244. }
  245. } else {
  246. // Try to restore last LED setting
  247. if (lastLEDMode != nil) {
  248. [self selectedVisualization:lastLEDMode];
  249. }
  250. }
  251. }
  252. - (IBAction)toggleLights:(NSMenuItem *)sender {
  253. if ([sender state] == NSOffState) {
  254. // Turn on lights
  255. if ([serial isOpen]) {
  256. [serial sendString:@"UV 1\n"];
  257. }
  258. [sender setState:NSOnState];
  259. } else {
  260. // Turn off lights
  261. if ([serial isOpen]) {
  262. [serial sendString:@"UV 0\n"];
  263. }
  264. [sender setState:NSOffState];
  265. }
  266. // Store changed value in preferences
  267. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  268. [store setBool:([sender state] == NSOnState) forKey:PREF_LIGHTS_STATE];
  269. [store synchronize];
  270. }
  271. - (void)visualizeGPUUsage:(NSTimer *)timer {
  272. }
  273. - (void)visualizeVRAMUsage:(NSTimer *)timer {
  274. }
  275. - (void)visualizeCPUUsage:(NSTimer *)timer {
  276. }
  277. - (void)visualizeRAMUsage:(NSTimer *)timer {
  278. }
  279. - (void)visualizeGPUTemperature:(NSTimer *)timer {
  280. }
  281. - (void)visualizeCPUTemperature:(NSTimer *)timer {
  282. }
  283. - (void)visualizeRGBFade:(NSTimer *)timer {
  284. }
  285. - (void)visualizeHSVFade:(NSTimer *)timer {
  286. }
  287. - (BOOL)timedVisualization:(NSString *)mode {
  288. // Stop previous timer setting
  289. if (animation != nil) {
  290. [animation invalidate];
  291. animation = nil;
  292. }
  293. // Schedule next invocation for this animation...
  294. if ([mode isEqualToString:TEXT_GPU_USAGE]) {
  295. animation = [NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(visualizeGPUUsage:) userInfo:mode repeats:YES];
  296. } else if ([mode isEqualToString:TEXT_VRAM_USAGE]) {
  297. animation = [NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(visualizeVRAMUsage:) userInfo:mode repeats:YES];
  298. } else if ([mode isEqualToString:TEXT_CPU_USAGE]) {
  299. animation = [NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(visualizeCPUUsage:) userInfo:mode repeats:YES];
  300. } else if ([mode isEqualToString:TEXT_RAM_USAGE]) {
  301. animation = [NSTimer timerWithTimeInterval:20.0 target:self selector:@selector(visualizeRAMUsage:) userInfo:mode repeats:YES];
  302. } else if ([mode isEqualToString:TEXT_CPU_TEMPERATURE]) {
  303. animation = [NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(visualizeCPUTemperature:) userInfo:mode repeats:YES];
  304. } else if ([mode isEqualToString:TEXT_GPU_TEMPERATURE]) {
  305. animation = [NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(visualizeGPUTemperature:) userInfo:mode repeats:YES];
  306. } else if ([mode isEqualToString:TEXT_RGB_FADE]) {
  307. animation = [NSTimer timerWithTimeInterval:0.1 target:self selector:@selector(visualizeRGBFade:) userInfo:mode repeats:YES];
  308. } else if ([mode isEqualToString:TEXT_HSV_FADE]) {
  309. animation = [NSTimer timerWithTimeInterval:0.1 target:self selector:@selector(visualizeHSVFade:) userInfo:mode repeats:YES];
  310. } else {
  311. return NO;
  312. }
  313. // ...and also execute it right now
  314. [animation fire];
  315. return YES;
  316. }
  317. - (void)selectedVisualization:(NSMenuItem *)sender {
  318. // Turn off all other LED menu items
  319. if (menuColors != nil) {
  320. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  321. [[menuColors itemAtIndex:i] setState:NSOffState];
  322. }
  323. }
  324. if (menuAnimations != nil) {
  325. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  326. [[menuAnimations itemAtIndex:i] setState:NSOffState];
  327. }
  328. }
  329. if (menuVisualizations != nil) {
  330. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  331. [[menuVisualizations itemAtIndex:i] setState:NSOffState];
  332. }
  333. }
  334. [buttonOff setState:NSOffState];
  335. [sender setState:NSOnState];
  336. // Check if a static color was selected
  337. BOOL found = NO;
  338. if (staticColors != nil) {
  339. for (NSString *key in [staticColors allKeys]) {
  340. if ([sender.title isEqualToString:key]) {
  341. found = YES;
  342. NSColor *color = [staticColors valueForKey:key];
  343. unsigned char red = [color redComponent] * 255;
  344. unsigned char green = [color greenComponent] * 255;
  345. unsigned char blue = [color blueComponent] * 255;
  346. NSString *string = [NSString stringWithFormat:@"RGB %d %d %d\n", red, green, blue];
  347. if ([serial isOpen]) {
  348. [serial sendString:string];
  349. }
  350. break;
  351. }
  352. }
  353. }
  354. if (!found) {
  355. // Check if an animated visualization was selected
  356. if ([self timedVisualization:[sender title]] == NO) {
  357. NSLog(@"Unknown LED Visualization selected!\n");
  358. return;
  359. }
  360. }
  361. // Store changed value in preferences
  362. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  363. [store setObject:[sender title] forKey:PREF_LED_MODE];
  364. [store synchronize];
  365. #ifdef DEBUG
  366. NSLog(@"Stored new mode: \"%@\"!\n", [sender title]);
  367. #endif
  368. }
  369. - (void)selectedSerialPort:(NSMenuItem *)source {
  370. // Store selection for next start-up
  371. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  372. [store setObject:[source title] forKey:PREF_SERIAL_PORT];
  373. [store synchronize];
  374. // De-select all other ports
  375. for (int i = 0; i < [menuPorts numberOfItems]; i++) {
  376. [[menuPorts itemAtIndex:i] setState:NSOffState];
  377. }
  378. // Select only the current port
  379. [source setState:NSOnState];
  380. // Close previously opened port, if any
  381. if ([serial isOpen]) {
  382. [serial closePort];
  383. }
  384. // Try to open selected port
  385. [serial setPortName:[source title]];
  386. if ([serial openPort] != 0) {
  387. [source setState:NSOffState];
  388. } else {
  389. // Restore the current configuration
  390. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  391. if ([[menuColors itemAtIndex:i] state] == NSOnState) {
  392. [self selectedVisualization:[menuColors itemAtIndex:i]];
  393. }
  394. }
  395. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  396. if ([[menuAnimations itemAtIndex:i] state] == NSOnState) {
  397. [self selectedVisualization:[menuAnimations itemAtIndex:i]];
  398. }
  399. }
  400. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  401. if ([[menuVisualizations itemAtIndex:i] state] == NSOnState) {
  402. [self selectedVisualization:[menuVisualizations itemAtIndex:i]];
  403. }
  404. }
  405. if ([buttonOff state] == NSOnState) {
  406. [buttonOff setState:NSOffState];
  407. [self turnLEDsOff:buttonOff];
  408. }
  409. if ([buttonLights state] == NSOnState) {
  410. [serial sendString:@"UV 1\n"];
  411. } else {
  412. [serial sendString:@"UV 0\n"];
  413. }
  414. }
  415. }
  416. - (IBAction)showAbout:(id)sender {
  417. [NSApp activateIgnoringOtherApps:YES];
  418. [application orderFrontStandardAboutPanel:self];
  419. }
  420. @end