feat: added widgets capture, music visualiser, bluetooth, notes
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: haSettings
|
||||
|
||||
property var settings: null
|
||||
|
||||
readonly property var daemon: (typeof PluginService !== "undefined" && PluginService.pluginInstances) ? PluginService.pluginInstances["gameBar"] : null
|
||||
readonly property var service: daemon ? daemon.homeService : null
|
||||
readonly property var entityList: service ? service.entityList : []
|
||||
|
||||
property string filterText: ""
|
||||
|
||||
// Read straight from the daemon's (reactive) plugin data. Caching this in a
|
||||
// local list loses the saved picks, because PluginSettings.loadValue() still
|
||||
// returns the default while pluginService is being injected - and the next
|
||||
// pick would then save that empty list over the stored one.
|
||||
readonly property var selectedIds: {
|
||||
const data = service ? service.pluginData : null;
|
||||
const stored = data ? data.haEntities : null;
|
||||
if (Array.isArray(stored))
|
||||
return stored;
|
||||
|
||||
const fallback = settings ? settings.loadValue("haEntities", []) : [];
|
||||
return Array.isArray(fallback) ? fallback : [];
|
||||
}
|
||||
|
||||
readonly property var filteredEntities: {
|
||||
const query = filterText.trim().toLowerCase();
|
||||
const list = haSettings.entityList;
|
||||
if (query === "")
|
||||
return list;
|
||||
|
||||
return list.filter(entity => {
|
||||
const name = (entity.attributes && entity.attributes.friendly_name) ? entity.attributes.friendly_name : "";
|
||||
return entity.entity_id.toLowerCase().includes(query) || name.toLowerCase().includes(query);
|
||||
});
|
||||
}
|
||||
|
||||
readonly property string statusText: {
|
||||
if (!service)
|
||||
return "Plugin daemon not running";
|
||||
if (!service.configured)
|
||||
return "Enter a URL and token, then refresh";
|
||||
if (service.loading)
|
||||
return "Contacting Home Assistant…";
|
||||
if (service.lastError !== "")
|
||||
return service.lastError;
|
||||
if (service.everLoaded)
|
||||
return entityList.length + " entities · " + selectedIds.length + " picked";
|
||||
return "Not loaded yet";
|
||||
}
|
||||
|
||||
function isSelected(entityId) {
|
||||
return haSettings.selectedIds.indexOf(entityId) !== -1;
|
||||
}
|
||||
|
||||
function setSelected(entityId, selected) {
|
||||
const next = haSettings.selectedIds.slice();
|
||||
const index = next.indexOf(entityId);
|
||||
if (selected && index === -1)
|
||||
next.push(entityId);
|
||||
else if (!selected && index !== -1)
|
||||
next.splice(index, 1);
|
||||
|
||||
if (settings)
|
||||
settings.saveValue("haEntities", next);
|
||||
}
|
||||
|
||||
implicitHeight: column.implicitHeight
|
||||
height: implicitHeight
|
||||
|
||||
Component.onCompleted: {
|
||||
if (service && service.configured && !service.everLoaded)
|
||||
service.refresh();
|
||||
}
|
||||
|
||||
Column {
|
||||
id: column
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
text: "Home Assistant"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: "Pick the entities you want in the Home widget. The access token is stored in plugin_settings.json."
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: Math.round(parent.width * 0.3)
|
||||
text: "Status"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - Math.round(parent.width * 0.3) - refreshButton.width - parent.spacing * 2
|
||||
text: haSettings.statusText
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: (haSettings.service && haSettings.service.lastError !== "") ? Theme.error : Theme.surfaceText
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
DankButton {
|
||||
id: refreshButton
|
||||
|
||||
text: "Refresh"
|
||||
iconName: "refresh"
|
||||
enabled: haSettings.service !== null && haSettings.service.configured
|
||||
onClicked: haSettings.service.refresh()
|
||||
}
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
width: parent.width
|
||||
placeholderText: "Filter entities (e.g. light, kitchen)"
|
||||
leftIconName: "search"
|
||||
showClearButton: true
|
||||
text: haSettings.filterText
|
||||
onTextChanged: haSettings.filterText = text
|
||||
visible: haSettings.entityList.length > 0
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 280
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||
border.color: Theme.outlineMedium
|
||||
border.width: Theme.layerOutlineWidth
|
||||
visible: haSettings.entityList.length > 0
|
||||
|
||||
DankListView {
|
||||
id: entityListView
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingS
|
||||
clip: true
|
||||
spacing: 2
|
||||
model: haSettings.filteredEntities
|
||||
|
||||
delegate: Rectangle {
|
||||
id: entityRow
|
||||
|
||||
required property var modelData
|
||||
|
||||
readonly property bool picked: haSettings.isSelected(modelData.entity_id)
|
||||
|
||||
width: entityListView.width
|
||||
height: 44
|
||||
radius: Theme.cornerRadius
|
||||
color: picked ? Theme.primarySelected : (rowArea.containsMouse ? Theme.surfaceHover : "transparent")
|
||||
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.right: pickToggle.left
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 0
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: (entityRow.modelData.attributes && entityRow.modelData.attributes.friendly_name) ? entityRow.modelData.attributes.friendly_name : entityRow.modelData.entity_id
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: entityRow.picked ? Font.Medium : Font.Normal
|
||||
color: Theme.surfaceText
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: entityRow.modelData.entity_id + " · " + entityRow.modelData.state
|
||||
font.pixelSize: Theme.fontSizeSmall - 1
|
||||
color: Theme.surfaceVariantText
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
id: pickToggle
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
hideText: true
|
||||
checked: entityRow.picked
|
||||
onToggled: isChecked => haSettings.setSelected(entityRow.modelData.entity_id, isChecked)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: rowArea
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: pickToggle.width + Theme.spacingM
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: haSettings.setSelected(entityRow.modelData.entity_id, !entityRow.picked)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user