From c844e46b26faac80011bbb7b0aeb7937166d4a53 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Sat, 1 Feb 2020 14:27:10 +0000 Subject: [PATCH] Change menubar to use new json prefs --- HA Menu.xcodeproj/project.pbxproj | 16 +- HA Menu/AppDelegate.swift | 3 - HA Menu/MenuItemController.swift | 203 +++++++++--------- HA Menu/Models/Preferences.swift | 36 ++-- .../ViewControllers/PrefsViewController.swift | 10 +- 5 files changed, 145 insertions(+), 123 deletions(-) diff --git a/HA Menu.xcodeproj/project.pbxproj b/HA Menu.xcodeproj/project.pbxproj index b131521..5dbfad2 100644 --- a/HA Menu.xcodeproj/project.pbxproj +++ b/HA Menu.xcodeproj/project.pbxproj @@ -615,7 +615,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 19; DEVELOPMENT_TEAM = VZ3Z8BPWPW; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "HA Menu/Info.plist"; @@ -623,7 +623,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.1.1; + MARKETING_VERSION = 2.2.0; PRODUCT_BUNDLE_IDENTIFIER = "org.codechimp.HA-Menu"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; @@ -638,7 +638,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 19; DEVELOPMENT_TEAM = VZ3Z8BPWPW; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "HA Menu/Info.plist"; @@ -646,7 +646,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.1.1; + MARKETING_VERSION = 2.2.0; PRODUCT_BUNDLE_IDENTIFIER = "org.codechimp.HA-Menu"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; @@ -743,7 +743,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 19; DEVELOPMENT_TEAM = VZ3Z8BPWPW; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "HA Menu Launcher/Info.plist"; @@ -751,7 +751,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.1.1; + MARKETING_VERSION = 2.2.0; PRODUCT_BUNDLE_IDENTIFIER = "org.codechimp.HA-Menu-Launcher"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -767,7 +767,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 19; DEVELOPMENT_TEAM = VZ3Z8BPWPW; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "HA Menu Launcher/Info.plist"; @@ -775,7 +775,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.1.1; + MARKETING_VERSION = 2.2.0; PRODUCT_BUNDLE_IDENTIFIER = "org.codechimp.HA-Menu-Launcher"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; diff --git a/HA Menu/AppDelegate.swift b/HA Menu/AppDelegate.swift index 7cf75cf..945f0a6 100644 --- a/HA Menu/AppDelegate.swift +++ b/HA Menu/AppDelegate.swift @@ -64,10 +64,7 @@ extension AppDelegate { } func updateFromPrefs() { - print("Prefs Updated") - SMLoginItemSetEnabled(launcherAppId as CFString, prefs.launch) - } } diff --git a/HA Menu/MenuItemController.swift b/HA Menu/MenuItemController.swift index 152da73..2c44759 100644 --- a/HA Menu/MenuItemController.swift +++ b/HA Menu/MenuItemController.swift @@ -15,12 +15,15 @@ final class MenuItemController: NSObject, NSMenuDelegate { var haService = HaService.shared var prefs = Preferences() var haStates : [HaState]? + var groups = [HaEntity]() + var menuItems = [PrefMenuItem]() let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) let menu = NSMenu() var preferences: Preferences + let menuItemTypeTopLevel = 996 let menuItemTypeInfo = 997 let menuItemTypeError = 999 @@ -109,8 +112,25 @@ final class MenuItemController: NSObject, NSMenuDelegate { result in switch result { case .success( _): - self.addDomains() - self.addGroups() + + self.menuItems.removeAll() + + self.groups = self.haService.filterEntities(entityDomain: EntityDomains.groupDomain.rawValue).reversed() + + let menuItemsWithFriendlyNames = self.prefs.menuItemsWithFriendlyNames(groups: self.groups) + + let sortedMenuItemsWithFriendlyNames = menuItemsWithFriendlyNames.sorted { $0.value.index < $1.value.index } + + for (_, value) in sortedMenuItemsWithFriendlyNames { + self.menuItems.insert(value, at: 0) + } + + // Add Menu Items + for menuItem in self.menuItems { + if menuItem.enabled { + self.addMenuItem(menuItem: menuItem) + } + } case .failure(let haServiceApiError): switch haServiceApiError { @@ -134,137 +154,118 @@ final class MenuItemController: NSObject, NSMenuDelegate { } } - func addDomains() { - if (self.prefs.domainInputSelects) { - let inputSelects = self.haService.filterEntities(entityDomain: EntityDomains.inputSelectDomain.rawValue) - if inputSelects.count > 0 { - self.addEntitiesToMenu(entities: inputSelects) - } - } - - if (self.prefs.domainInputBooleans) { - let inputBooleans = self.haService.filterEntities(entityDomain: EntityDomains.inputBooleanDomain.rawValue) - if inputBooleans.count > 0 { - self.addEntitiesToMenu(entities: inputBooleans) - } - } - - if (self.prefs.domainAutomations) { - let automations = self.haService.filterEntities(entityDomain: EntityDomains.automationDomain.rawValue) - if automations.count > 0 { - self.addEntitiesToMenu(entities: automations) - } - } - - if (self.prefs.domainSwitches) { - let switches = self.haService.filterEntities(entityDomain: EntityDomains.switchDomain.rawValue) - if switches.count > 0 { - self.addEntitiesToMenu(entities: switches) - } - } - - if (self.prefs.domainLights) { - let lights = self.haService.filterEntities(entityDomain: EntityDomains.lightDomain.rawValue) - if lights.count > 0 { - self.addEntitiesToMenu(entities: lights) - } - } - } + func addMenuItem(menuItem: PrefMenuItem) { + switch menuItem.itemType { + case itemTypes.Domain: + let domainItems = self.haService.filterEntities(entityDomain: menuItem.entityId) + self.addEntitiesToMenu(menuItem: menuItem, entities: domainItems) + case itemTypes.Group: + self.haService.getState(entityId: "group.\(menuItem.entityId)") { result in + switch result { + case .success(let group): + // For each entity, get it's attributes and if available add to array + var entities = [HaEntity]() + + for entityId in (group.attributes.entityIds) { + + let entityType = entityId.components(separatedBy: ".")[0] + var itemType: EntityTypes? + + switch entityType { + case "switch": + itemType = EntityTypes.switchType + case "light": + itemType = EntityTypes.lightType + case "input_boolean": + itemType = EntityTypes.inputBooleanType + case "input_select": + itemType = EntityTypes.inputSelectType + case "automation": + itemType = EntityTypes.automationType + default: + itemType = nil + } - func addGroups() { - // Iterate groups in preferences - for groupId in (self.prefs.groups) { - if groupId.count > 0 { - - self.haService.getState(entityId: "group.\(groupId)") { result in - switch result { - case .success(let group): - // For each entity, get it's attributes and if available add to array - var entities = [HaEntity]() - - for entityId in (group.attributes.entityIds) { - - let entityType = entityId.components(separatedBy: ".")[0] - var itemType: EntityTypes? - - switch entityType { - case "switch": - itemType = EntityTypes.switchType - case "light": - itemType = EntityTypes.lightType - case "input_boolean": - itemType = EntityTypes.inputBooleanType - case "input_select": - itemType = EntityTypes.inputSelectType - case "automation": - itemType = EntityTypes.automationType - default: - itemType = nil - } + if itemType != nil { - if itemType != nil { + self.haService.getState(entityId: entityId) { result in + switch result { + case .success(let entity): + var options = [String]() - self.haService.getState(entityId: entityId) { result in - switch result { - case .success(let entity): - let options = [String]() + if itemType == EntityTypes.inputSelectType { + options = entity.attributes.options + } - // Do not add unavailable state entities - if (entity.state != "unavailable") { + // Do not add unavailable state entities + if (entity.state != "unavailable") { - let haEntity: HaEntity = HaEntity(entityId: entityId, friendlyName: (entity.attributes.friendlyName), state: (entity.state), options: options) + let haEntity: HaEntity = HaEntity(entityId: entityId, friendlyName: (entity.attributes.friendlyName), state: (entity.state), options: options) - entities.append(haEntity) - } - case .failure( _): - break + entities.append(haEntity) } + case .failure( _): + break } } } + } - entities = entities.reversed() + entities = entities.reversed() - self.addEntitiesToMenu(entities: entities) + self.addEntitiesToMenu(menuItem: menuItem, entities: entities) - break - case .failure( _): - self.addErrorMenuItem(message: "Group not found") - } + break + case .failure( _): + self.addErrorMenuItem(message: "Group not found") } } } + } - func addEntitiesToMenu(entities: [HaEntity]) { + func addEntitiesToMenu(menuItem: PrefMenuItem, entities: [HaEntity]) { DispatchQueue.main.async { + if entities.count == 0 { + return + } + // Add a seperator before static menu items/previous group self.menu.insertItem(NSMenuItem.separator(), at: 0) - if (entities.count == 0) { - self.addErrorMenuItem(message: "No Entities") - return + var parent = self.menu + + + if menuItem.subMenu { + let topMenuItem = NSMenuItem() + topMenuItem.title = menuItem.friendlyName + topMenuItem.tag = self.menuItemTypeTopLevel + self.menu.insertItem(topMenuItem, at: 0) + + let subMenu = NSMenu() + parent = subMenu + self.menu.setSubmenu(subMenu, for: topMenuItem) } - // Populate menu items for switches + // Populate menu items for haEntity in entities { - self.addEntityMenuItem(haEntity: haEntity) + self.addEntityMenuItem(parent: parent, haEntity: haEntity) } } } - func addEntityMenuItem(haEntity: HaEntity) { + func addEntityMenuItem(parent: NSMenu, haEntity: HaEntity) { - if haEntity.domain.rawValue == "inputselect" { + if haEntity.domain == EntityDomains.inputSelectDomain { let inputSelectMenuItem = NSMenuItem() inputSelectMenuItem.title = haEntity.friendlyName inputSelectMenuItem.tag = haEntity.type.rawValue - self.menu.insertItem(inputSelectMenuItem, at: 0) + parent.insertItem(inputSelectMenuItem, at: 0) let subMenu = NSMenu() - self.menu.setSubmenu(subMenu, for: inputSelectMenuItem) + parent.setSubmenu(subMenu, for: inputSelectMenuItem) for option in haEntity.options { let optionMenuItem = NSMenuItem() @@ -289,7 +290,7 @@ final class MenuItemController: NSObject, NSMenuDelegate { // menuItem.image = NSImage(named: "StatusBarButtonImage") // menuItem.offStateImage = NSImage(named: "NSMenuOnStateTemplate") - self.menu.insertItem(menuItem, at: 0) + parent.insertItem(menuItem, at: 0) } } @@ -321,6 +322,14 @@ final class MenuItemController: NSObject, NSMenuDelegate { } while dynamicItem != nil } + // Top Level Menu + repeat { + dynamicItem = self.menu.item(withTag: self.menuItemTypeTopLevel) + if (dynamicItem != nil) { + self.menu.removeItem(dynamicItem!) + } + } while dynamicItem != nil + // Info repeat { dynamicItem = self.menu.item(withTag: self.menuItemTypeInfo) diff --git a/HA Menu/Models/Preferences.swift b/HA Menu/Models/Preferences.swift index 31fc4fd..66e8151 100644 --- a/HA Menu/Models/Preferences.swift +++ b/HA Menu/Models/Preferences.swift @@ -133,17 +133,33 @@ struct Preferences { get { var decodedResponse = [PrefMenuItem]() - guard let jsonString = UserDefaults.standard.string(forKey: "menu_items") else { + // Check if string empty + var jsonString = UserDefaults.standard.string(forKey: "menu_items") ?? "" + + if !jsonString.isEmpty { + // Got JSON + do { + let jsonData = jsonString.data(using: String.Encoding.utf8, allowLossyConversion: false) + decodedResponse = try JSONDecoder().decode([PrefMenuItem].self, from: jsonData!) + return decodedResponse + } + catch { + // Something odd with json, blank it out and default + jsonString = "" + } + } + + if jsonString.isEmpty { // Init Domains - decodedResponse.append(PrefMenuItem(entityId: "lights", itemType: itemTypes.Domain, subMenu: false, enabled: domainLights, friendlyName: "Lights")) + decodedResponse.append(PrefMenuItem(entityId: "light", itemType: itemTypes.Domain, subMenu: false, enabled: domainLights, friendlyName: "Lights")) - decodedResponse.append(PrefMenuItem(entityId: "switches", itemType: itemTypes.Domain, subMenu: false, enabled: domainSwitches, friendlyName: "Switches")) + decodedResponse.append(PrefMenuItem(entityId: "switch", itemType: itemTypes.Domain, subMenu: false, enabled: domainSwitches, friendlyName: "Switches")) - decodedResponse.append(PrefMenuItem(entityId: "automations", itemType: itemTypes.Domain, subMenu: false, enabled: domainAutomations, friendlyName: "Automations")) + decodedResponse.append(PrefMenuItem(entityId: "automation", itemType: itemTypes.Domain, subMenu: false, enabled: domainAutomations, friendlyName: "Automations")) - decodedResponse.append(PrefMenuItem(entityId: "inputbooleans", itemType: itemTypes.Domain, subMenu: false, enabled: domainInputBooleans, friendlyName: "Input Booleans")) + decodedResponse.append(PrefMenuItem(entityId: "input_boolean", itemType: itemTypes.Domain, subMenu: false, enabled: domainInputBooleans, friendlyName: "Input Booleans")) - decodedResponse.append(PrefMenuItem(entityId: "inputselects", itemType: itemTypes.Domain, subMenu: false, enabled: domainInputSelects, friendlyName: "Input Selects")) + decodedResponse.append(PrefMenuItem(entityId: "input_select", itemType: itemTypes.Domain, subMenu: false, enabled: domainInputSelects, friendlyName: "Input Selects")) // Init Groups from old setting for group in groups { @@ -152,13 +168,7 @@ struct Preferences { return decodedResponse } - do { - let jsonData = jsonString.data(using: String.Encoding.utf8, allowLossyConversion: false) - decodedResponse = try JSONDecoder().decode([PrefMenuItem].self, from: jsonData!) - } - catch { - // Something odd with json, blank it out - } + return decodedResponse } set { diff --git a/HA Menu/ViewControllers/PrefsViewController.swift b/HA Menu/ViewControllers/PrefsViewController.swift index 01b57df..4f3d798 100644 --- a/HA Menu/ViewControllers/PrefsViewController.swift +++ b/HA Menu/ViewControllers/PrefsViewController.swift @@ -25,6 +25,7 @@ class PrefsViewController: NSViewController { var prefs = Preferences() var groups = [HaEntity]() var menuItems = [PrefMenuItem]() + var okToSaveMenuItems = false private var dragDropType = NSPasteboard.PasteboardType(rawValue: "private.table-row") @@ -58,7 +59,8 @@ class PrefsViewController: NSViewController { switch result { case .success( _): DispatchQueue.main.async { - self.textfieldStatus.stringValue = "OK" + self.okToSaveMenuItems = true + self.textfieldStatus.stringValue = "Connected" self.groups = self.haService.filterEntities(entityDomain: EntityDomains.groupDomain.rawValue).reversed() @@ -75,6 +77,8 @@ class PrefsViewController: NSViewController { case .failure(let haServiceApiError): DispatchQueue.main.async { + self.okToSaveMenuItems = false + self.menuItems.removeAll() self.tableViewGroups.reloadData() @@ -116,7 +120,9 @@ class PrefsViewController: NSViewController { } func saveNewMenuItems() { - prefs.menuItems = menuItems + if okToSaveMenuItems { + prefs.menuItems = menuItems + } } // @objc func tableViewGroupsDoubleClick(_ sender:AnyObject) {