@@ -15,12 +15,15 @@ final class MenuItemController: NSObject, NSMenuDelegate {
15
15
var haService = HaService . shared
16
16
var prefs = Preferences ( )
17
17
var haStates : [ HaState ] ?
18
+ var groups = [ HaEntity] ( )
19
+ var menuItems = [ PrefMenuItem] ( )
18
20
19
21
let statusItem = NSStatusBar . system. statusItem ( withLength: NSStatusItem . squareLength)
20
22
let menu = NSMenu ( )
21
23
22
24
var preferences : Preferences
23
25
26
+ let menuItemTypeTopLevel = 996
24
27
let menuItemTypeInfo = 997
25
28
let menuItemTypeError = 999
26
29
@@ -109,8 +112,25 @@ final class MenuItemController: NSObject, NSMenuDelegate {
109
112
result in
110
113
switch result {
111
114
case . success( _) :
112
- self . addDomains ( )
113
- self . addGroups ( )
115
+
116
+ self . menuItems. removeAll ( )
117
+
118
+ self . groups = self . haService. filterEntities ( entityDomain: EntityDomains . groupDomain. rawValue) . reversed ( )
119
+
120
+ let menuItemsWithFriendlyNames = self . prefs. menuItemsWithFriendlyNames ( groups: self . groups)
121
+
122
+ let sortedMenuItemsWithFriendlyNames = menuItemsWithFriendlyNames. sorted { $0. value. index < $1. value. index }
123
+
124
+ for (_, value) in sortedMenuItemsWithFriendlyNames {
125
+ self . menuItems. insert ( value, at: 0 )
126
+ }
127
+
128
+ // Add Menu Items
129
+ for menuItem in self . menuItems {
130
+ if menuItem. enabled {
131
+ self . addMenuItem ( menuItem: menuItem)
132
+ }
133
+ }
114
134
115
135
case . failure( let haServiceApiError) :
116
136
switch haServiceApiError {
@@ -134,137 +154,118 @@ final class MenuItemController: NSObject, NSMenuDelegate {
134
154
}
135
155
}
136
156
137
- func addDomains( ) {
138
- if ( self . prefs. domainInputSelects) {
139
- let inputSelects = self . haService. filterEntities ( entityDomain: EntityDomains . inputSelectDomain. rawValue)
140
- if inputSelects. count > 0 {
141
- self . addEntitiesToMenu ( entities: inputSelects)
142
- }
143
- }
144
-
145
- if ( self . prefs. domainInputBooleans) {
146
- let inputBooleans = self . haService. filterEntities ( entityDomain: EntityDomains . inputBooleanDomain. rawValue)
147
- if inputBooleans. count > 0 {
148
- self . addEntitiesToMenu ( entities: inputBooleans)
149
- }
150
- }
151
-
152
- if ( self . prefs. domainAutomations) {
153
- let automations = self . haService. filterEntities ( entityDomain: EntityDomains . automationDomain. rawValue)
154
- if automations. count > 0 {
155
- self . addEntitiesToMenu ( entities: automations)
156
- }
157
- }
158
-
159
- if ( self . prefs. domainSwitches) {
160
- let switches = self . haService. filterEntities ( entityDomain: EntityDomains . switchDomain. rawValue)
161
- if switches. count > 0 {
162
- self . addEntitiesToMenu ( entities: switches)
163
- }
164
- }
165
-
166
- if ( self . prefs. domainLights) {
167
- let lights = self . haService. filterEntities ( entityDomain: EntityDomains . lightDomain. rawValue)
168
- if lights. count > 0 {
169
- self . addEntitiesToMenu ( entities: lights)
170
- }
171
- }
172
- }
157
+ func addMenuItem( menuItem: PrefMenuItem ) {
158
+ switch menuItem. itemType {
159
+ case itemTypes. Domain:
160
+ let domainItems = self . haService. filterEntities ( entityDomain: menuItem. entityId)
161
+ self . addEntitiesToMenu ( menuItem: menuItem, entities: domainItems)
162
+ case itemTypes. Group:
163
+ self . haService. getState ( entityId: " group. \( menuItem. entityId) " ) { result in
164
+ switch result {
165
+ case . success( let group) :
166
+ // For each entity, get it's attributes and if available add to array
167
+ var entities = [ HaEntity] ( )
168
+
169
+ for entityId in ( group. attributes. entityIds) {
170
+
171
+ let entityType = entityId. components ( separatedBy: " . " ) [ 0 ]
172
+ var itemType : EntityTypes ?
173
+
174
+ switch entityType {
175
+ case " switch " :
176
+ itemType = EntityTypes . switchType
177
+ case " light " :
178
+ itemType = EntityTypes . lightType
179
+ case " input_boolean " :
180
+ itemType = EntityTypes . inputBooleanType
181
+ case " input_select " :
182
+ itemType = EntityTypes . inputSelectType
183
+ case " automation " :
184
+ itemType = EntityTypes . automationType
185
+ default :
186
+ itemType = nil
187
+ }
173
188
174
- func addGroups( ) {
175
- // Iterate groups in preferences
176
- for groupId in ( self . prefs. groups) {
177
- if groupId. count > 0 {
178
-
179
- self . haService. getState ( entityId: " group. \( groupId) " ) { result in
180
- switch result {
181
- case . success( let group) :
182
- // For each entity, get it's attributes and if available add to array
183
- var entities = [ HaEntity] ( )
184
-
185
- for entityId in ( group. attributes. entityIds) {
186
-
187
- let entityType = entityId. components ( separatedBy: " . " ) [ 0 ]
188
- var itemType : EntityTypes ?
189
-
190
- switch entityType {
191
- case " switch " :
192
- itemType = EntityTypes . switchType
193
- case " light " :
194
- itemType = EntityTypes . lightType
195
- case " input_boolean " :
196
- itemType = EntityTypes . inputBooleanType
197
- case " input_select " :
198
- itemType = EntityTypes . inputSelectType
199
- case " automation " :
200
- itemType = EntityTypes . automationType
201
- default :
202
- itemType = nil
203
- }
189
+ if itemType != nil {
204
190
205
- if itemType != nil {
191
+ self . haService. getState ( entityId: entityId) { result in
192
+ switch result {
193
+ case . success( let entity) :
194
+ var options = [ String] ( )
206
195
207
- self . haService. getState ( entityId: entityId) { result in
208
- switch result {
209
- case . success( let entity) :
210
- let options = [ String] ( )
196
+ if itemType == EntityTypes . inputSelectType {
197
+ options = entity. attributes. options
198
+ }
211
199
212
- // Do not add unavailable state entities
213
- if ( entity. state != " unavailable " ) {
200
+ // Do not add unavailable state entities
201
+ if ( entity. state != " unavailable " ) {
214
202
215
- let haEntity : HaEntity = HaEntity ( entityId: entityId, friendlyName: ( entity. attributes. friendlyName) , state: ( entity. state) , options: options)
203
+ let haEntity : HaEntity = HaEntity ( entityId: entityId, friendlyName: ( entity. attributes. friendlyName) , state: ( entity. state) , options: options)
216
204
217
- entities. append ( haEntity)
218
- }
219
- case . failure( _) :
220
- break
205
+ entities. append ( haEntity)
221
206
}
207
+ case . failure( _) :
208
+ break
222
209
}
223
210
}
224
211
}
212
+ }
225
213
226
- entities = entities. reversed ( )
214
+ entities = entities. reversed ( )
227
215
228
- self . addEntitiesToMenu ( entities: entities)
216
+ self . addEntitiesToMenu ( menuItem : menuItem , entities: entities)
229
217
230
- break
231
- case . failure( _) :
232
- self . addErrorMenuItem ( message: " Group not found " )
233
- }
218
+ break
219
+ case . failure( _) :
220
+ self . addErrorMenuItem ( message: " Group not found " )
234
221
}
235
222
}
236
223
}
224
+
237
225
}
238
226
239
- func addEntitiesToMenu( entities: [ HaEntity ] ) {
227
+ func addEntitiesToMenu( menuItem : PrefMenuItem , entities: [ HaEntity ] ) {
240
228
DispatchQueue . main. async {
241
229
230
+ if entities. count == 0 {
231
+ return
232
+ }
233
+
242
234
// Add a seperator before static menu items/previous group
243
235
self . menu. insertItem ( NSMenuItem . separator ( ) , at: 0 )
244
236
245
- if ( entities. count == 0 ) {
246
- self . addErrorMenuItem ( message: " No Entities " )
247
- return
237
+ var parent = self . menu
238
+
239
+
240
+ if menuItem. subMenu {
241
+ let topMenuItem = NSMenuItem ( )
242
+ topMenuItem. title = menuItem. friendlyName
243
+ topMenuItem. tag = self . menuItemTypeTopLevel
244
+ self . menu. insertItem ( topMenuItem, at: 0 )
245
+
246
+ let subMenu = NSMenu ( )
247
+ parent = subMenu
248
+ self . menu. setSubmenu ( subMenu, for: topMenuItem)
248
249
}
249
250
250
- // Populate menu items for switches
251
+ // Populate menu items
251
252
for haEntity in entities {
252
- self . addEntityMenuItem ( haEntity: haEntity)
253
+ self . addEntityMenuItem ( parent : parent , haEntity: haEntity)
253
254
}
254
255
255
256
}
256
257
}
257
258
258
- func addEntityMenuItem( haEntity: HaEntity ) {
259
+ func addEntityMenuItem( parent : NSMenu , haEntity: HaEntity ) {
259
260
260
- if haEntity. domain. rawValue == " inputselect " {
261
+ if haEntity. domain == EntityDomains . inputSelectDomain {
261
262
let inputSelectMenuItem = NSMenuItem ( )
262
263
inputSelectMenuItem. title = haEntity. friendlyName
263
264
inputSelectMenuItem. tag = haEntity. type. rawValue
264
- self . menu . insertItem ( inputSelectMenuItem, at: 0 )
265
+ parent . insertItem ( inputSelectMenuItem, at: 0 )
265
266
266
267
let subMenu = NSMenu ( )
267
- self . menu . setSubmenu ( subMenu, for: inputSelectMenuItem)
268
+ parent . setSubmenu ( subMenu, for: inputSelectMenuItem)
268
269
269
270
for option in haEntity. options {
270
271
let optionMenuItem = NSMenuItem ( )
@@ -289,7 +290,7 @@ final class MenuItemController: NSObject, NSMenuDelegate {
289
290
// menuItem.image = NSImage(named: "StatusBarButtonImage")
290
291
// menuItem.offStateImage = NSImage(named: "NSMenuOnStateTemplate")
291
292
292
- self . menu . insertItem ( menuItem, at: 0 )
293
+ parent . insertItem ( menuItem, at: 0 )
293
294
}
294
295
}
295
296
@@ -321,6 +322,14 @@ final class MenuItemController: NSObject, NSMenuDelegate {
321
322
} while dynamicItem != nil
322
323
}
323
324
325
+ // Top Level Menu
326
+ repeat {
327
+ dynamicItem = self . menu. item ( withTag: self . menuItemTypeTopLevel)
328
+ if ( dynamicItem != nil ) {
329
+ self . menu. removeItem ( dynamicItem!)
330
+ }
331
+ } while dynamicItem != nil
332
+
324
333
// Info
325
334
repeat {
326
335
dynamicItem = self . menu. item ( withTag: self . menuItemTypeInfo)
0 commit comments