From 35c2684031d2f9c7025c7ee0eab4faaf6259a926 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Thu, 20 May 2021 16:20:03 +0100 Subject: [PATCH] Add display of sensor values within groups --- HA Menu.xcodeproj/project.pbxproj | 16 ++++++------- .../BulletImage.imageset/Bullet@1x.png | Bin 0 -> 241 bytes .../BulletImage.imageset/Bullet@2x.png | Bin 0 -> 342 bytes .../BulletImage.imageset/Contents.json | 22 ++++++++++++++++++ HA Menu/Assets.xcassets/Contents.json | 6 ++--- HA Menu/MenuItemController.swift | 22 ++++++++++++++++-- HA Menu/Models/HaEntity.swift | 7 ++++++ HA Menu/Models/HaService.swift | 2 +- HA Menu/Models/HaStateAttributes.swift | 9 +++++++ README.md | 8 +++++-- 10 files changed, 76 insertions(+), 16 deletions(-) create mode 100644 HA Menu/Assets.xcassets/BulletImage.imageset/Bullet@1x.png create mode 100644 HA Menu/Assets.xcassets/BulletImage.imageset/Bullet@2x.png create mode 100644 HA Menu/Assets.xcassets/BulletImage.imageset/Contents.json diff --git a/HA Menu.xcodeproj/project.pbxproj b/HA Menu.xcodeproj/project.pbxproj index fc2c08c..7c93ac9 100644 --- a/HA Menu.xcodeproj/project.pbxproj +++ b/HA Menu.xcodeproj/project.pbxproj @@ -621,7 +621,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 24; + CURRENT_PROJECT_VERSION = 25; DEVELOPMENT_TEAM = VZ3Z8BPWPW; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "HA Menu/Info.plist"; @@ -629,7 +629,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.4.1; + MARKETING_VERSION = 2.5.0; OTHER_SWIFT_FLAGS = "-D DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = "org.codechimp.HA-Menu"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -645,7 +645,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 24; + CURRENT_PROJECT_VERSION = 25; DEVELOPMENT_TEAM = VZ3Z8BPWPW; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "HA Menu/Info.plist"; @@ -653,7 +653,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.4.1; + MARKETING_VERSION = 2.5.0; PRODUCT_BUNDLE_IDENTIFIER = "org.codechimp.HA-Menu"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; @@ -750,7 +750,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 24; + CURRENT_PROJECT_VERSION = 25; DEVELOPMENT_TEAM = VZ3Z8BPWPW; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "HA Menu Launcher/Info.plist"; @@ -758,7 +758,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.4.1; + MARKETING_VERSION = 2.5.0; PRODUCT_BUNDLE_IDENTIFIER = "org.codechimp.HA-Menu-Launcher"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -774,7 +774,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 24; + CURRENT_PROJECT_VERSION = 25; DEVELOPMENT_TEAM = VZ3Z8BPWPW; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "HA Menu Launcher/Info.plist"; @@ -782,7 +782,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.4.1; + MARKETING_VERSION = 2.5.0; PRODUCT_BUNDLE_IDENTIFIER = "org.codechimp.HA-Menu-Launcher"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; diff --git a/HA Menu/Assets.xcassets/BulletImage.imageset/Bullet@1x.png b/HA Menu/Assets.xcassets/BulletImage.imageset/Bullet@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..3b612eef9b0599297485054dee5df05defe892b6 GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyy~STEL88gA~qMxhWJ#MSHq9hFAzD zCrGd!)@F%ly!VpfQF;JRQu7fHHih2X4H0ZzjyV$~PBJ_ZnxJUF02JTAq_$>J3Ww3- zi^^P6ycr)gJDg!Zvbmu`T;M%p(j|k1|%Oc%$NbB7>k44ofy`glX(f`xTHpSruq6Z zXaU(A42G$TlC0TWzSWdSpS4N|DZ_xKNxI^gNz7!twx zcIrjW1_K_KtAc-urLPw-9RG5kbkl?y^&@q#*Xz#J8&?cb(CNBq0 zjmH0K0ku z`1hNW4V_=-3m&dcdvUl!(IF@%UT{Aj4=2caM%4u@ULFq^_8qritn$_W5Au_ztDnm{ Hr-UW|bc}08 literal 0 HcmV?d00001 diff --git a/HA Menu/Assets.xcassets/BulletImage.imageset/Contents.json b/HA Menu/Assets.xcassets/BulletImage.imageset/Contents.json new file mode 100644 index 0000000..e781b29 --- /dev/null +++ b/HA Menu/Assets.xcassets/BulletImage.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "Bullet@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Bullet@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HA Menu/Assets.xcassets/Contents.json b/HA Menu/Assets.xcassets/Contents.json index da4a164..73c0059 100644 --- a/HA Menu/Assets.xcassets/Contents.json +++ b/HA Menu/Assets.xcassets/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/HA Menu/MenuItemController.swift b/HA Menu/MenuItemController.swift index e1e442e..1ba2820 100644 --- a/HA Menu/MenuItemController.swift +++ b/HA Menu/MenuItemController.swift @@ -239,6 +239,8 @@ final class MenuItemController: NSObject, NSMenuDelegate { itemType = EntityTypes.sceneType case "script": itemType = EntityTypes.scriptType + case "sensor": + itemType = EntityTypes.sensorType default: itemType = nil @@ -250,15 +252,20 @@ final class MenuItemController: NSObject, NSMenuDelegate { switch result { case .success(let entity): var options = [String]() + var unitOfMeasurement = "" if itemType == EntityTypes.inputSelectType { options = entity.attributes.options } + if itemType == EntityTypes.sensorType { + unitOfMeasurement = entity.attributes.unitOfMeasurement + } + // 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), unitOfMeasurement: unitOfMeasurement, options: options) entities.append(haEntity) } @@ -345,6 +352,8 @@ final class MenuItemController: NSObject, NSMenuDelegate { else { let menuItem = NSMenuItem() + menuItem.title = haEntity.friendlyName + if haEntity.domainType == EntityDomains.sceneDomain { menuItem.action = #selector(self.turnOnEntity(_:)) menuItem.state = NSControl.StateValue.off @@ -355,13 +364,18 @@ final class MenuItemController: NSObject, NSMenuDelegate { menuItem.state = NSControl.StateValue.off menuItem.offStateImage = NSImage(named: "PlayButtonImage") } + else if haEntity.domainType == EntityDomains.sensorDomain { + menuItem.title = haEntity.friendlyName + ": " + haEntity.state + haEntity.unitOfMeasurement + menuItem.action = #selector(self.doNothing(_:)) + menuItem.state = NSControl.StateValue.off + menuItem.offStateImage = NSImage(named: "BulletImage") + } else { menuItem.action = #selector(self.toggleEntityState(_:)) menuItem.state = ((haEntity.state == "on") ? NSControl.StateValue.on : NSControl.StateValue.off) } menuItem.target = self - menuItem.title = haEntity.friendlyName menuItem.keyEquivalent = "" menuItem.representedObject = haEntity menuItem.tag = haEntity.type.rawValue // Tag defines what type of item it is @@ -430,6 +444,10 @@ final class MenuItemController: NSObject, NSMenuDelegate { } } + @objc func doNothing(_ sender: NSMenuItem) { + // fake menu responder + } + @objc func toggleEntityState(_ sender: NSMenuItem) { let haEntity: HaEntity = sender.representedObject as! HaEntity haService.toggleEntityState(haEntity: haEntity) diff --git a/HA Menu/Models/HaEntity.swift b/HA Menu/Models/HaEntity.swift index 15d929e..085cf65 100644 --- a/HA Menu/Models/HaEntity.swift +++ b/HA Menu/Models/HaEntity.swift @@ -17,6 +17,7 @@ enum EntityTypes: Int, CaseIterable { case groupType = 7 case sceneType = 8 case scriptType = 9 + case sensorType = 10 case unknownType = 999 } @@ -29,6 +30,7 @@ enum EntityDomains: String, CaseIterable { case sceneDomain = "scene" case scriptDomain = "script" case groupDomain = "group" + case sensorDomain = "sensor" case unknownDomain = "unknown" } @@ -37,6 +39,7 @@ struct HaEntity { var entityId: String var friendlyName: String var state: String + var unitOfMeasurement: String var options: [String] var domainType: EntityDomains { @@ -56,6 +59,8 @@ struct HaEntity { return EntityDomains.sceneDomain case EntityDomains.scriptDomain.rawValue: return EntityDomains.scriptDomain + case EntityDomains.sensorDomain.rawValue: + return EntityDomains.sensorDomain case EntityDomains.groupDomain.rawValue: return EntityDomains.groupDomain @@ -88,6 +93,8 @@ struct HaEntity { return EntityTypes.sceneType case EntityDomains.scriptDomain: return EntityTypes.scriptType + case EntityDomains.sensorDomain: + return EntityTypes.sensorType case EntityDomains.groupDomain: return EntityTypes.groupType diff --git a/HA Menu/Models/HaService.swift b/HA Menu/Models/HaService.swift index 6651680..6e79dfb 100644 --- a/HA Menu/Models/HaService.swift +++ b/HA Menu/Models/HaService.swift @@ -106,7 +106,7 @@ class HaService { // Do not add unavailable state entities if (haState.state != "unavailable") { - let haEntity: HaEntity = HaEntity(entityId: haState.entityId, friendlyName: (haState.attributes.friendlyName), state: (haState.state), options: haState.attributes.options) + let haEntity: HaEntity = HaEntity(entityId: haState.entityId, friendlyName: (haState.attributes.friendlyName), state: (haState.state), unitOfMeasurement: haState.attributes.unitOfMeasurement, options: haState.attributes.options) entities.append(haEntity) } diff --git a/HA Menu/Models/HaStateAttributes.swift b/HA Menu/Models/HaStateAttributes.swift index 2655c9d..eae0e1f 100644 --- a/HA Menu/Models/HaStateAttributes.swift +++ b/HA Menu/Models/HaStateAttributes.swift @@ -12,6 +12,7 @@ struct HaStateAttributes : Decodable { let entityIds: [String] var friendlyName: String // Allow rewriting to entityId when decoded + let unitOfMeasurement: String let options: [String] enum CodingKeys: String, CodingKey { @@ -21,12 +22,14 @@ struct HaStateAttributes : Decodable { enum AttributeKeys: String, CodingKey { case entityIds = "entity_id" case friendlyName = "friendly_name" + case unitOfMeasurement = "unit_of_measurement" case options = "options" } init() { entityIds = [String]() friendlyName = "" + unitOfMeasurement = "" options = [String]() } @@ -45,6 +48,12 @@ struct HaStateAttributes : Decodable { } else { self.friendlyName = "" } + + if let unitOfMeasurement = try attributes.decodeIfPresent(String.self, forKey: .unitOfMeasurement) { + self.unitOfMeasurement = unitOfMeasurement + } else { + self.unitOfMeasurement = "" + } if let options = try attributes.decodeIfPresent([String].self, forKey: .options) { self.options = options diff --git a/README.md b/README.md index 2d41ba4..89de18c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ Currently HA Menu supports * Turning available switches, lights, automations and input_boolean's on and off * Activating scenes and scripts * input_select option menus - +* Viewing sensor values (Sensors have to be specifically added to a group) + HA Menu supports MacOS 10.13 (High Sierra) and later. ![alt text](Art/menu.png "HA Menu") @@ -48,7 +49,9 @@ First of all create your group(s) within groups.yaml as per the example. Note t Once you have the group(s) added to HA, within HA Menu go to Preferences and tick the Groups you want to be displayed. Close preferences to save these settings. -Now when you click on HA Menu again the group's you have setup will be displayed. Entities within groups are displayed in the order they are added within the group (printer, lego_lights, desk_lamp, notifications, entry_alert, who_cooks in the example). +Now when you click on HA Menu again the group's you have setup will be displayed. Entities within groups are displayed in the order they are added within the group (printer, lego_lights, desk_lamp, notifications, entry_alert, who_cooks, outside_temperature in the example). + +Where sensors are included they will be displayed with a bullet in the menu, and show the friendly name, state/value and unit of measurement. Clicking on a sensor does not perform any action. Example groups.yaml ```yaml @@ -61,6 +64,7 @@ ha_menu: - input_boolean.notifications - automation.entry_alert - input_select.who_cooks + - sensor.outside_temperature ``` ## Say Thanks