Skip to content

Commit

Permalink
Home Assistant: Fix connection error when instance url contains a tra…
Browse files Browse the repository at this point in the history
…iling slash + Scene support (raycast#682)

* fix trailing slash in instance url

* add missing dependency

* add scene support

* no progress indicator on error

* turn off loading in hook
  • Loading branch information
tonka3000 authored Jan 19, 2022
1 parent d36a658 commit f5e38ef
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 5 deletions.
Binary file added extensions/homeassistant/assets/palette.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion extensions/homeassistant/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion extensions/homeassistant/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@
"subtitle": "Home Assistant",
"description": "Query Buttons",
"mode": "view"
},
{
"name": "scenes",
"title": "Scenes",
"subtitle": "Home Assistant",
"description": "Query Scenes",
"mode": "view"
}
],
"preferences": [
Expand Down Expand Up @@ -166,9 +173,10 @@
}
],
"dependencies": {
"@raycast/api": "^1.25.0",
"@raycast/api": "^1.26.0",
"home-assistant-js-websocket": "^5.11.1",
"node-fetch": "^2.6.1",
"open": "^8.3.0",
"url-join": "^4.0.1",
"ws": "^8.2.3"
},
Expand Down
11 changes: 10 additions & 1 deletion extensions/homeassistant/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ import { Connection, createConnection, createLongLivedTokenAuth } from "home-ass
import { HomeAssistant } from "./haapi";
import { createSocket } from "./socket";

function getInstance(): string {
let result = preferences.instance?.value as string;
if (result && result.endsWith("/")) {
// make sure to have no trailing slash
result = result.substring(0, result.length - 1);
}
return result;
}

export function createHomeAssistantClient(): HomeAssistant {
const instance = preferences.instance?.value as string;
const token = preferences.token?.value as string;
Expand All @@ -18,7 +27,7 @@ export async function getHAWSConnection(): Promise<Connection> {
return con;
} else {
console.log("create new home assistant ws con");
const instance = preferences.instance?.value as string;
const instance = getInstance();
const token = preferences.token?.value as string;
const ignoreCertificates = (preferences.ignorecerts.value as boolean) || false;
const auth = createLongLivedTokenAuth(instance, token);
Expand Down
2 changes: 0 additions & 2 deletions extensions/homeassistant/src/components/buttons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ export function ButtonPressAction(props: { state: State }): JSX.Element | null {
if (!s.entity_id.startsWith("button")) {
return null;
}
const name = s.entity_id.substring(7);
console.log(name);
const handle = async () => {
await ha.callService("button", "press", { entity_id: s.entity_id });
};
Expand Down
20 changes: 20 additions & 0 deletions extensions/homeassistant/src/components/scenes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ActionPanel, Icon, Color } from "@raycast/api";
import { ha } from "../common";
import { State } from "../haapi";

export function SceneActivateAction(props: { state: State }): JSX.Element | null {
const s = props.state;
if (!s.entity_id.startsWith("scene")) {
return null;
}
const handle = async () => {
await ha.callService("scene", "turn_on", { entity_id: s.entity_id });
};
return (
<ActionPanel.Item
title="Activate"
onAction={handle}
icon={{ source: Icon.Terminal, tintColor: Color.PrimaryText }}
/>
);
}
23 changes: 23 additions & 0 deletions extensions/homeassistant/src/components/states.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
import { CameraShowImage, CameraTurnOffAction, CameraTurnOnAction } from "./cameras";
import { ScriptRunAction } from "./scripts";
import { ButtonPressAction } from "./buttons";
import { SceneActivateAction } from "./scenes";

const PrimaryIconColor = Color.Blue;
const UnavailableColor = "#bdbdbd";
Expand Down Expand Up @@ -170,6 +171,8 @@ function getIcon(state: State): ImageLike | undefined {
} else if (e.startsWith("script")) {
const color = state.state === "on" ? Color.Yellow : PrimaryIconColor;
return { source: "play.png", tintColor: color };
} else if (e.startsWith("scene")) {
return { source: "palette.png", tintColor: PrimaryIconColor };
} else {
const di = getDeviceClassIcon(state);
return di ? di : { source: "entity.png", tintColor: PrimaryIconColor };
Expand Down Expand Up @@ -718,6 +721,26 @@ export function StateActionPanel(props: { state: State }): JSX.Element {
</ActionPanel>
);
}
case "scene": {
return (
<ActionPanel>
<ActionPanel.Section title="Controls">
<SceneActivateAction state={state} />
</ActionPanel.Section>
<ActionPanel.Section title="Attributes">
<ShowAttributesAction state={props.state} />
</ActionPanel.Section>
<ActionPanel.Section title="Values">
<CopyEntityIDAction state={state} />
<CopyStateValueAction state={state} />
</ActionPanel.Section>
<ActionPanel.Section title="History">
<OpenEntityHistoryAction state={state} />
<OpenEntityLogbookAction state={state} />
</ActionPanel.Section>
</ActionPanel>
);
}
default: {
return (
<ActionPanel>
Expand Down
4 changes: 4 additions & 0 deletions extensions/homeassistant/src/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export function useHAStates(): {
const err = e instanceof Error ? e : new Error(e);
setError(err);
}
} finally {
if (!didUnmount) {
setIsLoading(false);
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions extensions/homeassistant/src/scenes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { StatesList } from "./components/states";

export default function main(): JSX.Element {
return <StatesList domain="scene" />;
}

0 comments on commit f5e38ef

Please sign in to comment.