Skip to content

Commit

Permalink
chore: implement back-end for HA switches
Browse files Browse the repository at this point in the history
  • Loading branch information
mdvanes committed May 22, 2024
1 parent dbd49ba commit 002ff5a
Show file tree
Hide file tree
Showing 8 changed files with 299 additions and 3 deletions.
12 changes: 10 additions & 2 deletions apps/client/src/Components/Molecules/SwitchBarList/HaSwitchBar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { HomeRemoteHaSwitch } from "@homeremote/types";
import { FC } from "react";
import { useUpdateHaSwitchMutation } from "../../../Services/generated/switchesApi";
import SwitchBar from "./SwitchBar";
import SwitchBarInnerButton from "./SwitchBarInnerButton";

Expand All @@ -9,6 +10,7 @@ interface HaSwitchBarProps {

const HaSwitchBar: FC<HaSwitchBarProps> = ({ haSwitch }) => {
// Implement the HaSwitchBar component logic here
const [updateHaSwitch] = useUpdateHaSwitchMutation();

return (
<SwitchBar
Expand All @@ -17,7 +19,10 @@ const HaSwitchBar: FC<HaSwitchBarProps> = ({ haSwitch }) => {
<SwitchBarInnerButton
isReadOnly={false}
clickAction={() => {
/* */
updateHaSwitch({
entityId: haSwitch.idx,
body: { state: "On" },
});
}}
icon="radio_button_checked"
isActive={haSwitch.status === "On"}
Expand All @@ -27,7 +32,10 @@ const HaSwitchBar: FC<HaSwitchBarProps> = ({ haSwitch }) => {
<SwitchBarInnerButton
isReadOnly={false}
clickAction={() => {
/* */
updateHaSwitch({
entityId: haSwitch.idx,
body: { state: "Off" },
});
}}
icon="radio_button_unchecked"
isActive={haSwitch.status === "Off"}
Expand Down
46 changes: 46 additions & 0 deletions apps/client/src/Services/generated/switchesApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { emptySplitApi as api } from "../emptyApi";
export const addTagTypes = [] as const;
const injectedRtkApi = api
.enhanceEndpoints({
addTagTypes,
})
.injectEndpoints({
endpoints: (build) => ({
updateHaSwitch: build.mutation<
UpdateHaSwitchApiResponse,
UpdateHaSwitchApiArg
>({
query: (queryArg) => ({
url: `/api/switches/ha/${queryArg.entityId}`,
method: "POST",
body: queryArg.body,
}),
}),
}),
overrideExisting: false,
});
export { injectedRtkApi as switchesApi };
export type UpdateHaSwitchApiResponse =
/** status 200 updateHaSwitch */ UpdateHaSwitchResponse;
export type UpdateHaSwitchApiArg = {
/** Entity ID */
entityId: string;
body: {
/** Target state, On or Off */
state?: "On" | "Off";
};
};
export type UpdateHaSwitchResponse = string;
export type ErrorResponse = {
/** Time when error happened */
timestamp?: string;
/** Code describing the error */
status?: number;
/** Short error name */
error?: string;
/** Message explaining the error */
message?: string;
/** Code of the error */
code?: number;
};
export const { useUpdateHaSwitchMutation } = injectedRtkApi;
38 changes: 38 additions & 0 deletions apps/server/src/switches/switches.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ import {
HomeRemoteHaSwitch,
HomeRemoteSwitch,
SwitchesResponse,
UpdateHaSwitchArgs,
UpdateHaSwitchResponse,
} from "@homeremote/types";
import {
Body,
Controller,
Get,
HttpException,
HttpStatus,
Logger,
Param,
Post,
Expand Down Expand Up @@ -264,4 +268,38 @@ export class SwitchesController {
return { status: "error" };
}
}

@UseGuards(JwtAuthGuard)
@Post("/ha/:entityId")
async updateHaSwitch(
@Param("entityId") entityId: string,
@Body() args: UpdateHaSwitchArgs,
@Request() req: AuthenticatedRequest
): Promise<UpdateHaSwitchResponse> {
this.logger.verbose(
`[${req.user.name}] Call to /switch/ha/${entityId} state: ${args.state}`
);

try {
await fetch(
`${
this.haApiConfig.baseUrl
}/api/services/light/turn_${args.state.toLowerCase()}`,
{
method: "POST",
headers: {
Authorization: `Bearer ${this.haApiConfig.token}`,
},
body: JSON.stringify({ entity_id: entityId }),
}
);
return "received";
} catch (err) {
this.logger.error(`[${req.user.name}] ${err}`);
throw new HttpException(
"failed to receive downstream data",
HttpStatus.INTERNAL_SERVER_ERROR
);
}
}
}
79 changes: 79 additions & 0 deletions libs/types/definitions/internal/switches.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# npx mock-to-openapi ./libs/types/examples
# https://editor.swagger.io/

openapi: 3.0.1
info:
title: Switches Controller API
description: Switches Controller
version: 1.0.0
servers:
- url: https://example.com
description: Generated server url
paths:
/api/switches/ha/{entity_id}:
post:
operationId: updateHaSwitch
parameters:
- name: entity_id
in: path
description: Entity ID
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
state:
type: string
description: Target state, On or Off
enum: [On, Off]
responses:
"200":
description: updateHaSwitch
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateHaSwitchResponse"
"400":
description: Bad request.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"401":
description: Unauthorized.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
components:
schemas:
ErrorResponse:
type: object
properties:
timestamp:
type: string
description: Time when error happened
status:
type: integer
description: Code describing the error
format: int32
error:
type: string
description: Short error name
message:
type: string
description: Message explaining the error
code:
type: integer
description: Code of the error
format: int32
description: Error information details
UpdateHaSwitchResponse:
type: string


107 changes: 107 additions & 0 deletions libs/types/src/lib/generated/switches.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* This file was auto-generated by openapi-typescript.
* Do not make direct changes to the file.
*/

export interface paths {
"/api/switches/ha/{entity_id}": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
post: operations["updateHaSwitch"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
}
export type webhooks = Record<string, never>;
export interface components {
schemas: {
/** @description Error information details */
ErrorResponse: {
/** @description Time when error happened */
timestamp?: string;
/**
* Format: int32
* @description Code describing the error
*/
status?: number;
/** @description Short error name */
error?: string;
/** @description Message explaining the error */
message?: string;
/**
* Format: int32
* @description Code of the error
*/
code?: number;
};
UpdateHaSwitchResponse: string;
};
responses: never;
parameters: never;
requestBodies: never;
headers: never;
pathItems: never;
}
export type $defs = Record<string, never>;
export interface operations {
updateHaSwitch: {
parameters: {
query?: never;
header?: never;
path: {
/** @description Entity ID */
entity_id: string;
};
cookie?: never;
};
requestBody: {
content: {
"application/json": {
/**
* @description Target state, On or Off
* @enum {string}
*/
state?: "On" | "Off";
};
};
};
responses: {
/** @description updateHaSwitch */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["UpdateHaSwitchResponse"];
};
};
/** @description Bad request. */
400: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["ErrorResponse"];
};
};
/** @description Unauthorized. */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["ErrorResponse"];
};
};
};
};
}
8 changes: 8 additions & 0 deletions libs/types/src/lib/switches.types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { operations } from "./generated/switches";

export const DomoticzTypeOptions = {
Dimmer: "Dimmer",
Group: "Group",
Expand Down Expand Up @@ -39,3 +41,9 @@ export interface SwitchesResponse {
status: "received" | "error";
switches?: Array<HomeRemoteSwitch | HomeRemoteHaSwitch>;
}

export type UpdateHaSwitchArgs =
operations["updateHaSwitch"]["requestBody"]["content"]["application/json"];

export type UpdateHaSwitchResponse =
operations["updateHaSwitch"]["responses"]["200"]["content"]["application/json"];
6 changes: 6 additions & 0 deletions openapi-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ const config: ConfigFile = {
exportName: "energyUsageApi",
tag: true,
},
"./apps/client/src/Services/generated/switchesApi.ts": {
apiFile: "./apps/client/src/Services/emptyApi.ts",
schemaFile: "./libs/types/definitions/internal/switches.yml",
exportName: "switchesApi",
tag: true,
},
},
hooks: true,
};
Expand Down
6 changes: 5 additions & 1 deletion redocly.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ apis:
domoticz@v1:
root: ./libs/types/definitions/external/domoticz.yml
x-openapi-ts:
output: ./libs/types/src/lib/external/generated/domoticz.ts
output: ./libs/types/src/lib/external/generated/domoticz.ts
external@v1:
root: ./libs/types/definitions/internal/energyUsage.yml
x-openapi-ts:
output: ./libs/types/src/lib/generated/energyUsage.ts
switches@v1:
root: ./libs/types/definitions/internal/switches.yml
x-openapi-ts:
output: ./libs/types/src/lib/generated/switches.ts
homeAssistant@v1:
root: ./libs/types/definitions/external/homeAssistant.yml
x-openapi-ts:
Expand Down

0 comments on commit 002ff5a

Please sign in to comment.