Skip to content

Commit 5546cbe

Browse files
authored
Improve entity picker information and filtering options (#4217)
1 parent aae751f commit 5546cbe

File tree

15 files changed

+827
-318
lines changed

15 files changed

+827
-318
lines changed

.claude.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"lsp": {
3+
"swift": {
4+
"command": "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/sourcekit-lsp",
5+
"args": [],
6+
"rootPatterns": ["Package.swift", "*.xcodeproj", "*.xcworkspace"]
7+
}
8+
}
9+
}

HomeAssistant.xcodeproj/project.pbxproj

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -618,16 +618,18 @@
618618
420FE84B2B556BB100878E06 /* CarPlayActionsTemplate+Build.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420FE84A2B556BB100878E06 /* CarPlayActionsTemplate+Build.swift */; };
619619
420FE84E2B556CE500878E06 /* CarPlayEntitiesListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420FE84D2B556CE500878E06 /* CarPlayEntitiesListViewModel.swift */; };
620620
420FE8502B556F7500878E06 /* CarPlayEntitiesListTemplate+Build.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420FE84F2B556F7500878E06 /* CarPlayEntitiesListTemplate+Build.swift */; };
621-
4210CCFF2F155B7900B71FB9 /* AssistConfigurationTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CCFE2F155B7900B71FB9 /* AssistConfigurationTable.swift */; };
622-
4210CD002F155B7900B71FB9 /* AssistConfigurationTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CCFE2F155B7900B71FB9 /* AssistConfigurationTable.swift */; };
623-
4210CD032F155C4500B71FB9 /* AssistConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CCFC2F15556700B71FB9 /* AssistConfiguration.swift */; };
624-
4210CD052F155D2600B71FB9 /* AssistSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CD042F155D2600B71FB9 /* AssistSettingsViewModel.swift */; };
625621
4210CCD92F150CDD00B71FB9 /* WebRTCPlayerVideoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CCD82F150CDD00B71FB9 /* WebRTCPlayerVideoView.swift */; };
626622
4210CCDB2F150CDD00B71FB9 /* WebRTCPlayerViewControls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CCDA2F150CDD00B71FB9 /* WebRTCPlayerViewControls.swift */; };
627623
4210CCDD2F150CDE00B71FB9 /* WebRTCPlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CCDC2F150CDE00B71FB9 /* WebRTCPlayerViewController.swift */; };
628624
4210CCEB2F152AD000B71FB9 /* CameraPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CCEA2F152AD000B71FB9 /* CameraPlayerView.swift */; };
629625
4210CCEF2F152ED800B71FB9 /* CameraStreamHLSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CCEE2F152ED800B71FB9 /* CameraStreamHLSView.swift */; };
630626
4210CCF32F1532C400B71FB9 /* CameraMJPEGPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CCF22F1532C400B71FB9 /* CameraMJPEGPlayerView.swift */; };
627+
4210CCFF2F155B7900B71FB9 /* AssistConfigurationTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CCFE2F155B7900B71FB9 /* AssistConfigurationTable.swift */; };
628+
4210CD002F155B7900B71FB9 /* AssistConfigurationTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CCFE2F155B7900B71FB9 /* AssistConfigurationTable.swift */; };
629+
4210CD032F155C4500B71FB9 /* AssistConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CCFC2F15556700B71FB9 /* AssistConfiguration.swift */; };
630+
4210CD052F155D2600B71FB9 /* AssistSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CD042F155D2600B71FB9 /* AssistSettingsViewModel.swift */; };
631+
4210CD102F16906800B71FB9 /* EntityRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CD0F2F16906800B71FB9 /* EntityRowView.swift */; };
632+
4210CD122F16A1AB00B71FB9 /* EntityFilterPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4210CD112F16A1AB00B71FB9 /* EntityFilterPickerView.swift */; };
631633
421155212D3525F500A71630 /* AppIconSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 421155202D3525F500A71630 /* AppIconSelectorView.swift */; };
632634
421155232D354F3F00A71630 /* AppIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 421155222D354F3F00A71630 /* AppIcon.swift */; };
633635
421155252D355C6700A71630 /* WidgetBuilderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 421155242D355C6700A71630 /* WidgetBuilderView.swift */; };
@@ -921,6 +923,7 @@
921923
429BA2AF2C800CAB00A50996 /* SFSymbolEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429BA2AE2C800CAB00A50996 /* SFSymbolEntity.swift */; };
922924
429BEA1A2D102F3A00F070F9 /* ConnectionErrorDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429BEA192D102F3A00F070F9 /* ConnectionErrorDetailsView.swift */; };
923925
429BEA1D2D10315F00F070F9 /* SheetCloseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429BEA1B2D1030EA00F070F9 /* SheetCloseButton.swift */; };
926+
429C33BF2F17989F0033EF5E /* EntityPickerViewModel.test.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429C33BC2F17986D0033EF5E /* EntityPickerViewModel.test.swift */; };
924927
429C72202B28D0EC00BCD558 /* Haptics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429C721F2B28D0EC00BCD558 /* Haptics.swift */; };
925928
429C82892DCCDB0F007B03AF /* HAProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E4C9922DCC9A6600DF2813 /* HAProgressView.swift */; };
926929
429D51C52EFA0E750014AD0D /* ModernAssistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429D51C42EFA0E750014AD0D /* ModernAssistView.swift */; };
@@ -2288,15 +2291,17 @@
22882291
420FE84A2B556BB100878E06 /* CarPlayActionsTemplate+Build.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CarPlayActionsTemplate+Build.swift"; sourceTree = "<group>"; };
22892292
420FE84D2B556CE500878E06 /* CarPlayEntitiesListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarPlayEntitiesListViewModel.swift; sourceTree = "<group>"; };
22902293
420FE84F2B556F7500878E06 /* CarPlayEntitiesListTemplate+Build.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CarPlayEntitiesListTemplate+Build.swift"; sourceTree = "<group>"; };
2291-
4210CCFC2F15556700B71FB9 /* AssistConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssistConfiguration.swift; sourceTree = "<group>"; };
2292-
4210CCFE2F155B7900B71FB9 /* AssistConfigurationTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssistConfigurationTable.swift; sourceTree = "<group>"; };
2293-
4210CD042F155D2600B71FB9 /* AssistSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssistSettingsViewModel.swift; sourceTree = "<group>"; };
22942294
4210CCD82F150CDD00B71FB9 /* WebRTCPlayerVideoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRTCPlayerVideoView.swift; sourceTree = "<group>"; };
22952295
4210CCDA2F150CDD00B71FB9 /* WebRTCPlayerViewControls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRTCPlayerViewControls.swift; sourceTree = "<group>"; };
22962296
4210CCDC2F150CDE00B71FB9 /* WebRTCPlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRTCPlayerViewController.swift; sourceTree = "<group>"; };
22972297
4210CCEA2F152AD000B71FB9 /* CameraPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPlayerView.swift; sourceTree = "<group>"; };
22982298
4210CCEE2F152ED800B71FB9 /* CameraStreamHLSView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraStreamHLSView.swift; sourceTree = "<group>"; };
22992299
4210CCF22F1532C400B71FB9 /* CameraMJPEGPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraMJPEGPlayerView.swift; sourceTree = "<group>"; };
2300+
4210CCFC2F15556700B71FB9 /* AssistConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssistConfiguration.swift; sourceTree = "<group>"; };
2301+
4210CCFE2F155B7900B71FB9 /* AssistConfigurationTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssistConfigurationTable.swift; sourceTree = "<group>"; };
2302+
4210CD042F155D2600B71FB9 /* AssistSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssistSettingsViewModel.swift; sourceTree = "<group>"; };
2303+
4210CD0F2F16906800B71FB9 /* EntityRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityRowView.swift; sourceTree = "<group>"; };
2304+
4210CD112F16A1AB00B71FB9 /* EntityFilterPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityFilterPickerView.swift; sourceTree = "<group>"; };
23002305
421155202D3525F500A71630 /* AppIconSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconSelectorView.swift; sourceTree = "<group>"; };
23012306
421155222D354F3F00A71630 /* AppIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIcon.swift; sourceTree = "<group>"; };
23022307
421155242D355C6700A71630 /* WidgetBuilderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetBuilderView.swift; sourceTree = "<group>"; };
@@ -2557,6 +2562,7 @@
25572562
429BA2AE2C800CAB00A50996 /* SFSymbolEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SFSymbolEntity.swift; sourceTree = "<group>"; };
25582563
429BEA192D102F3A00F070F9 /* ConnectionErrorDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionErrorDetailsView.swift; sourceTree = "<group>"; };
25592564
429BEA1B2D1030EA00F070F9 /* SheetCloseButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetCloseButton.swift; sourceTree = "<group>"; };
2565+
429C33BC2F17986D0033EF5E /* EntityPickerViewModel.test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityPickerViewModel.test.swift; sourceTree = "<group>"; };
25602566
429C721F2B28D0EC00BCD558 /* Haptics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Haptics.swift; sourceTree = "<group>"; };
25612567
429D51C42EFA0E750014AD0D /* ModernAssistView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModernAssistView.swift; sourceTree = "<group>"; };
25622568
42A0B9352EBB82EB00E074BF /* StatusBarButtonsConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBarButtonsConfigurator.swift; sourceTree = "<group>"; };
@@ -4954,8 +4960,6 @@
49544960
children = (
49554961
4251AA972C6B9D30004CCC9D /* Edit */,
49564962
42D5ACD72C64C0CF00D9C4E2 /* Add */,
4957-
426BB0C42D393F0E0062D905 /* EntityPicker.swift */,
4958-
4245DC002EBA3DD3005E0E04 /* EntityPickerViewModel.swift */,
49594963
426BB0C62D394BFF0062D905 /* AssistPipelinePicker.swift */,
49604964
);
49614965
path = MagicItem;
@@ -5002,6 +5006,17 @@
50025006
path = Domains;
50035007
sourceTree = "<group>";
50045008
};
5009+
4257EA912F1790DE00D81506 /* EntityPicker */ = {
5010+
isa = PBXGroup;
5011+
children = (
5012+
426BB0C42D393F0E0062D905 /* EntityPicker.swift */,
5013+
4245DC002EBA3DD3005E0E04 /* EntityPickerViewModel.swift */,
5014+
4210CD112F16A1AB00B71FB9 /* EntityFilterPickerView.swift */,
5015+
4210CD0F2F16906800B71FB9 /* EntityRowView.swift */,
5016+
);
5017+
path = EntityPicker;
5018+
sourceTree = "<group>";
5019+
};
50055020
425C5A052CF756CF00206B5B /* Views */ = {
50065021
isa = PBXGroup;
50075022
children = (
@@ -5369,6 +5384,14 @@
53695384
path = General;
53705385
sourceTree = "<group>";
53715386
};
5387+
429C33BE2F17988D0033EF5E /* EntityPicker */ = {
5388+
isa = PBXGroup;
5389+
children = (
5390+
429C33BC2F17986D0033EF5E /* EntityPickerViewModel.test.swift */,
5391+
);
5392+
path = EntityPicker;
5393+
sourceTree = "<group>";
5394+
};
53725395
42A2AB7E2C80750A00C5608D /* Control */ = {
53735396
isa = PBXGroup;
53745397
children = (
@@ -6706,6 +6729,7 @@
67066729
B657A8FF1CA646EB00121384 /* App */ = {
67076730
isa = PBXGroup;
67086731
children = (
6732+
429C33BE2F17988D0033EF5E /* EntityPicker */,
67096733
42B89EAB2E080494000224A2 /* AppConstants.test.swift */,
67106734
42646B762E0BE6F100F6B367 /* BackgroundTask.test.swift */,
67116735
11EFD3C1272642FC000AF78B /* Additions */,
@@ -6747,6 +6771,7 @@
67476771
B661FB6B226BCC8500E541DD /* Settings */ = {
67486772
isa = PBXGroup;
67496773
children = (
6774+
4257EA912F1790DE00D81506 /* EntityPicker */,
67506775
429AFE5A2DB7BE3200AF0836 /* General */,
67516776
4211551F2D3525E800A71630 /* AppIcon */,
67526777
4278CB862D01F0BE00CFAAC9 /* Gestures */,
@@ -9114,6 +9139,7 @@
91149139
42F5C0852F02BBA000C50310 /* AppIntentProvider.swift in Sources */,
91159140
42FCCFFB2B9B1C310057783F /* ThreadTransferCredentialToHAViewModel.swift in Sources */,
91169141
4296C36E2B90DB640051B63C /* PerformAction.swift in Sources */,
9142+
4210CD102F16906800B71FB9 /* EntityRowView.swift in Sources */,
91179143
42B980D32DC24E1300BC5C08 /* SensorDetailLabelRowView.swift in Sources */,
91189144
1127383C2625512600F5E312 /* ButtonRowWithLoading.swift in Sources */,
91199145
42ABB0BB2C888BB10081461D /* CarPlayConfigurationViewModel.swift in Sources */,
@@ -9155,6 +9181,7 @@
91559181
42B980D92DC24E5D00BC5C08 /* SensorDetailViewModel.swift in Sources */,
91569182
420E2AE82C47471B004921D8 /* WidgetBasicSizeStyle.swift in Sources */,
91579183
4280E0712DC23CCF0004BE7D /* SensorRow.swift in Sources */,
9184+
4210CD122F16A1AB00B71FB9 /* EntityFilterPickerView.swift in Sources */,
91589185
42C101282CD3DABA0012BA78 /* IntentCoverEntity.swift in Sources */,
91599186
D0EEF324214DF2B700D1D360 /* Utils.swift in Sources */,
91609187
1101D7F92621479200AAE617 /* SettingsButtonRow.swift in Sources */,
@@ -9473,6 +9500,7 @@
94739500
428626072DA5CCAE00D58D13 /* HAButtonStyles.test.swift in Sources */,
94749501
42A818E32BBEA9780083D045 /* MockAudioRecorder.swift in Sources */,
94759502
11ED439A27265DE800B5FD45 /* OnboardingAuthStepRegister.test.swift in Sources */,
9503+
429C33BF2F17989F0033EF5E /* EntityPickerViewModel.test.swift in Sources */,
94769504
42196AD22DA5AF3D00BD501E /* MockBonjour.swift in Sources */,
94779505
11EFD3C027261AA4000AF78B /* OnboardingAuthStepDeviceNaming.test.swift in Sources */,
94789506
42A818E52BBEAA3A0083D045 /* MockAudioPlayer.swift in Sources */,

Sources/App/Resources/en.lproj/Localizable.strings

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,3 +1413,9 @@ Home Assistant is open source, advocates for privacy and runs locally in your ho
14131413
"camera_player.errors.unknown" = "Unknown error";
14141414
"camera_player.errors.unable_to_connect_to_server" = "Unable to connect to Home Assistant";
14151415
"camera_player.errors.no_stream_available" = "No stream available";
1416+
"entity_picker.filter.domain.title" = "Domain";
1417+
"entity_picker.filter.domain.all.title" = "All domains";
1418+
"entity_picker.filter.area.title" = "Area";
1419+
"entity_picker.filter.area.all.title" = "All areas";
1420+
"entity_picker.filter.group_by.title" = "Group by";
1421+
"entity_picker.list.area.no_area.title" = "No area";
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import Shared
2+
import SwiftUI
3+
4+
struct EntityFilterPickerView: View {
5+
struct PickerItem {
6+
let id: String
7+
let title: String
8+
}
9+
10+
let title: String
11+
let pickerItems: [PickerItem]
12+
@Binding var selectedItemId: String?
13+
14+
init(title: String, pickerItems: [PickerItem], selectedItemId: Binding<String?>) {
15+
self.title = title
16+
self.pickerItems = pickerItems
17+
self._selectedItemId = selectedItemId
18+
}
19+
20+
var body: some View {
21+
VStack(alignment: .leading, spacing: DesignSystem.Spaces.half) {
22+
Text(title)
23+
.font(.caption2.bold())
24+
Menu {
25+
ForEach(pickerItems, id: \.id) { item in
26+
Button {
27+
selectedItemId = item.id
28+
} label: {
29+
if item.id == selectedItemId {
30+
Label(item.title, systemSymbol: .checkmark)
31+
} else {
32+
Text(item.title)
33+
}
34+
}
35+
}
36+
} label: {
37+
Text(pickerItems.first { $0.id == selectedItemId }?.title ?? title)
38+
.frame(maxWidth: .infinity, alignment: .leading)
39+
.font(DesignSystem.Font.callout)
40+
.foregroundStyle(.secondary)
41+
.lineLimit(1)
42+
.truncationMode(.middle)
43+
}
44+
.frame(maxWidth: .infinity, alignment: .leading)
45+
}
46+
.padding(.horizontal, DesignSystem.Spaces.three)
47+
.padding(.vertical, DesignSystem.Spaces.one)
48+
.frame(width: 150, alignment: .leading)
49+
.modify { view in
50+
if #available(iOS 26.0, *) {
51+
view.glassEffect(.regular.interactive(), in: .capsule)
52+
} else {
53+
view.clipShape(.capsule)
54+
}
55+
}
56+
}
57+
}
58+
59+
#Preview {
60+
EntityFilterPickerView(
61+
title: "Filter 1",
62+
pickerItems: [.init(id: "1", title: "Abc"), .init(id: "2", title: "Def")],
63+
selectedItemId: .constant("1")
64+
)
65+
}

0 commit comments

Comments
 (0)