306 lines
12 KiB
QML
306 lines
12 KiB
QML
import QtQuick
|
|
import qs.config
|
|
import qs.services
|
|
|
|
Item {
|
|
id: root
|
|
|
|
property string lightQuery: searchInput.text.trim().toLowerCase()
|
|
|
|
readonly property var availableLights: HomeAssistant.catalog.filter(entity => {
|
|
if (HomePreferences.isSelected(entity.id))
|
|
return false;
|
|
const haystack = (entity.sourceName + " " + entity.id).toLowerCase();
|
|
return root.lightQuery === "" || haystack.includes(root.lightQuery);
|
|
})
|
|
|
|
function homeStatus(): string {
|
|
if (HomeAssistant.phase === "ready")
|
|
return `Connected · ${HomeAssistant.discoveredCount} lights discovered`;
|
|
if (HomeAssistant.phase === "degraded")
|
|
return "Last update unavailable · showing saved controls";
|
|
if (HomeAssistant.lastError === "authentication-required")
|
|
return "Authentication required";
|
|
if (HomeAssistant.lastError === "not-configured")
|
|
return "Home Assistant is not configured";
|
|
if (HomeAssistant.phase === "loading")
|
|
return "Connecting to Home Assistant";
|
|
return "Home Assistant is unavailable";
|
|
}
|
|
|
|
Flickable {
|
|
anchors.fill: parent
|
|
clip: true
|
|
contentWidth: width
|
|
contentHeight: content.implicitHeight + 64
|
|
boundsBehavior: Flickable.StopAtBounds
|
|
|
|
Column {
|
|
id: content
|
|
width: parent.width - 68
|
|
x: 34
|
|
y: 30
|
|
spacing: 16
|
|
|
|
Text {
|
|
text: "Home & Phone"
|
|
color: Theme.fg
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: 27
|
|
font.weight: Font.DemiBold
|
|
}
|
|
|
|
Text {
|
|
text: "Choose what appears in Control Center and keep phone continuity close at hand."
|
|
color: Theme.fgDim
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSize
|
|
bottomPadding: 6
|
|
}
|
|
|
|
SettingsCard {
|
|
title: "Home Assistant"
|
|
subtitle: root.homeStatus()
|
|
|
|
SettingRow {
|
|
label: "Light catalog"
|
|
detail: "Panama reads light state through the Home Assistant helper."
|
|
divider: false
|
|
controlWidth: 176
|
|
|
|
Row {
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: 8
|
|
|
|
SettingsButton {
|
|
id: refreshButton
|
|
text: "Refresh"
|
|
activeFocusOnTab: true
|
|
border.width: activeFocus ? 2 : 1
|
|
border.color: activeFocus ? Theme.accent : Theme.alpha(Theme.fg, 0.08)
|
|
onClicked: HomeAssistant.refresh()
|
|
Keys.onReturnPressed: HomeAssistant.refresh()
|
|
Keys.onSpacePressed: HomeAssistant.refresh()
|
|
}
|
|
|
|
SettingsButton {
|
|
id: openHomeButton
|
|
text: "Open"
|
|
activeFocusOnTab: true
|
|
border.width: activeFocus ? 2 : 1
|
|
border.color: activeFocus ? Theme.accent : Theme.alpha(Theme.fg, 0.08)
|
|
onClicked: HomeAssistant.open()
|
|
Keys.onReturnPressed: HomeAssistant.open()
|
|
Keys.onSpacePressed: HomeAssistant.open()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
title: "Control Center lights"
|
|
subtitle: HomeAssistant.selectedEntities.length === 0
|
|
? "Select the lights that belong on your shelf."
|
|
: `${Math.min(4, HomeAssistant.selectedEntities.length)} in Control Center · ${HomeAssistant.selectedEntities.length} selected`
|
|
|
|
Text {
|
|
width: parent.width
|
|
visible: HomeAssistant.selectedEntities.length === 0
|
|
text: "Choose lights below to build your Control Center shelf."
|
|
color: Theme.fgDim
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSize
|
|
wrapMode: Text.WordWrap
|
|
topPadding: 3
|
|
bottomPadding: 13
|
|
}
|
|
|
|
GridView {
|
|
id: favoritesGrid
|
|
width: parent.width
|
|
height: Math.ceil(count / 2) * cellHeight
|
|
visible: count > 0
|
|
interactive: false
|
|
clip: false
|
|
cellWidth: width / 2
|
|
cellHeight: 116
|
|
model: HomeAssistant.selectedEntities
|
|
|
|
delegate: HomeFavoriteCard {
|
|
required property var modelData
|
|
width: GridView.view.cellWidth - 6
|
|
height: 108
|
|
favorite: ({
|
|
id: modelData.id,
|
|
alias: modelData.name === modelData.sourceName ? "" : modelData.name
|
|
})
|
|
sourceName: modelData.sourceName
|
|
featured: index < 4
|
|
onAliasCommitted: (id, alias) => HomePreferences.setAlias(id, alias)
|
|
onMoveRequested: (id, targetIndex) => HomePreferences.move(id, targetIndex)
|
|
onRemoveRequested: id => HomePreferences.remove(id)
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
width: parent.width
|
|
height: 46
|
|
visible: HomePreferences.saveError !== ""
|
|
radius: 9
|
|
color: Theme.alpha(Theme.warn, 0.09)
|
|
border.width: 1
|
|
border.color: Theme.alpha(Theme.warn, 0.24)
|
|
|
|
Text {
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 12
|
|
anchors.right: retryButton.left
|
|
anchors.rightMargin: 12
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: HomePreferences.saveError
|
|
color: Theme.warn
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
SettingsButton {
|
|
id: retryButton
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: 8
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: "Retry"
|
|
activeFocusOnTab: true
|
|
border.width: activeFocus ? 2 : 1
|
|
border.color: activeFocus ? Theme.warn : Theme.alpha(Theme.fg, 0.08)
|
|
onClicked: HomePreferences.retrySave()
|
|
Keys.onReturnPressed: HomePreferences.retrySave()
|
|
Keys.onSpacePressed: HomePreferences.retrySave()
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
title: "Available lights"
|
|
subtitle: "Search the Home Assistant catalog by source name or entity ID."
|
|
|
|
Rectangle {
|
|
width: parent.width
|
|
height: 38
|
|
radius: 10
|
|
color: Theme.alpha(Theme.fg, searchInput.activeFocus ? 0.08 : 0.05)
|
|
border.width: searchInput.activeFocus ? 2 : 1
|
|
border.color: searchInput.activeFocus
|
|
? Theme.alpha(Theme.accent, 0.72)
|
|
: Theme.alpha(Theme.fg, 0.065)
|
|
|
|
Text {
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 11
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: "\u{F0349}"
|
|
color: Theme.fgDim
|
|
font.family: Theme.fontMono
|
|
font.pixelSize: 14
|
|
}
|
|
|
|
TextInput {
|
|
id: searchInput
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 36
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: 11
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
activeFocusOnTab: true
|
|
color: Theme.fg
|
|
selectionColor: Theme.accent
|
|
selectedTextColor: Theme.bgDark
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSize
|
|
clip: true
|
|
|
|
Text {
|
|
anchors.fill: parent
|
|
visible: searchInput.text === "" && !searchInput.activeFocus
|
|
text: "Search available lights"
|
|
color: Theme.fgMuted
|
|
font: searchInput.font
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
}
|
|
}
|
|
|
|
Column {
|
|
width: parent.width
|
|
visible: root.availableLights.length > 0
|
|
|
|
Repeater {
|
|
model: root.availableLights
|
|
|
|
AvailableLightRow {
|
|
required property var modelData
|
|
width: parent.width
|
|
entity: modelData
|
|
onAddRequested: id => HomePreferences.add(id)
|
|
}
|
|
}
|
|
}
|
|
|
|
Text {
|
|
width: parent.width
|
|
visible: root.availableLights.length === 0
|
|
text: root.lightQuery === "" && HomeAssistant.catalog.length > 0
|
|
? "All discovered lights are already selected"
|
|
: "No lights match that search"
|
|
color: Theme.fgDim
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSize
|
|
horizontalAlignment: Text.AlignHCenter
|
|
wrapMode: Text.WordWrap
|
|
topPadding: 18
|
|
bottomPadding: 10
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
title: "Phone continuity"
|
|
subtitle: "Keep the Messages handoff independent from phone connectivity."
|
|
|
|
SettingRow {
|
|
label: "Messages"
|
|
detail: "Opens BlueBubbles"
|
|
divider: false
|
|
controlWidth: 204
|
|
|
|
Row {
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: 12
|
|
|
|
Text {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: SystemSettings.bluebubblesAvailable ? "Installed" : "Unavailable"
|
|
color: SystemSettings.bluebubblesAvailable ? Theme.ok : Theme.fgMuted
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
}
|
|
|
|
SettingsButton {
|
|
id: openBlueBubblesButton
|
|
text: "Open"
|
|
enabled: SystemSettings.bluebubblesAvailable
|
|
activeFocusOnTab: enabled
|
|
border.width: activeFocus ? 2 : 1
|
|
border.color: activeFocus ? Theme.accent : Theme.alpha(Theme.fg, 0.08)
|
|
onClicked: SystemSettings.openApplication("bluebubbles")
|
|
Keys.onReturnPressed: if (enabled) SystemSettings.openApplication("bluebubbles")
|
|
Keys.onSpacePressed: if (enabled) SystemSettings.openApplication("bluebubbles")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|