feat: home assistant entity generic toggle (#2015)

Added support for generic toggle on Home Assistant entities that support
it. For entities that does not support toggle the feature will silently
fail. This way matches how Home Assistant handles it.
This commit is contained in:
Dennis Vesterlund
2024-05-06 20:00:13 +02:00
committed by GitHub
parent 6a5836f096
commit 18cd1f961f
4 changed files with 85 additions and 10 deletions

View File

@@ -22,8 +22,13 @@
},
"displayFriendlyName": {
"label": "Display friendly name",
"info": "Display friendly name from Home Assistant instead instead of display name"
"info": "Display friendly name from Home Assistant instead instead of display name."
},
"genericToggle": {
"label": "Entity toggle",
"info": "Perform a generic Home Assistant toggle action on entity when clicked."
}
}
}
}
}

View File

@@ -88,6 +88,36 @@ export const smartHomeEntityStateRouter = createTRPCRouter({
}
}
return false;
}),
triggerToggle: protectedProcedure
.input(z.object({
widgetId: z.string(),
configName: z.string()
})).mutation(async ({ input }) => {
const config = getConfig(input.configName);
const widget = config.widgets.find(widget => widget.id === input.widgetId) as ISmartHomeEntityStateWidget | null;
if (!widget) {
Consola.error(`Referenced widget ${input.widgetId} does not exist on backend.`);
throw new TRPCError({
code: 'CONFLICT',
message: 'Referenced widget does not exist on backend',
});
}
const instances = config.apps.filter((app) => app.integration?.type == 'homeAssistant');
for (const instance of instances) {
const url = new URL(instance.url);
const client = HomeAssistantSingleton.getOrSet(url, findAppProperty(instance, 'apiKey'));
const state = await client.triggerToggle(widget.properties.entityId);
if (state) {
return true;
}
}
return false;
}),
});

View File

@@ -56,4 +56,28 @@ export class HomeAssistant {
return false;
}
}
/**
* Triggers a toggle action for a specific entity.
*
* @param entityId - The ID of the entity to toggle.
* @returns A boolean indicating whether the toggle action was successful.
*/
async triggerToggle(entityId: string) {
try {
const response = await fetch(appendPath(this.basePath, `/services/homeassistant/toggle`), {
headers: {
'Authorization': `Bearer ${this.token}`,
},
body: JSON.stringify({
'entity_id': entityId,
}),
method: 'POST'
});
return response.ok;
} catch (err) {
Consola.error(`Failed to fetch from '${this.basePath}': ${err}`);
return false;
}
}
}

View File

@@ -21,6 +21,11 @@ const definition = defineWidget({
defaultValue: false,
info: true,
},
genericToggle: {
type: 'switch',
defaultValue: false,
info: true,
},
automationId: {
type: 'text',
info: true,
@@ -82,15 +87,26 @@ function EntityStateTile({ widget }: SmartHomeEntityStateWidgetProps) {
},
});
const handleClick = async () => {
if (!widget.properties.automationId) {
return;
}
const { mutateAsync: mutateTriggerToggleSync } = api.smartHomeEntityState.triggerToggle.useMutation({
onSuccess: () => {
void utils.smartHomeEntityState.invalidate();
},
});
await mutateTriggerAutomationAsync({
configName: configName as string,
widgetId: widget.id,
});
const handleClick = async () => {
if (widget.properties.genericToggle) {
await mutateTriggerToggleSync({
configName: configName as string,
widgetId: widget.id,
});
}
if (widget.properties.automationId) {
await mutateTriggerAutomationAsync({
configName: configName as string,
widgetId: widget.id,
});
}
};
let dataComponent = null;