Skip to content

Commit

Permalink
working commit
Browse files Browse the repository at this point in the history
  • Loading branch information
zoe-codez committed May 17, 2024
1 parent 1d33d6e commit 20fa961
Show file tree
Hide file tree
Showing 23 changed files with 1,989 additions and 808 deletions.
3 changes: 3 additions & 0 deletions .entity_generator
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[hass]
BASE_URL=http://localhost:9123
TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhYTdjOGQ5MzBiMDM0MDM2OGVjZTdjOTRjMTcyOWQ0OSIsImlhdCI6MTcxNDYwMzkyMywiZXhwIjoyMDI5OTYzOTIzfQ.Tvoh25bukQh5T5WIuvvkL9jVBihcOGG6D5JYdqdwx1U
1 change: 0 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@
"unicorn/prefer-node-protocol": "off",
"unicorn/no-array-for-each": "off",
"unicorn/import-style": "off",
"sonarjs/cognitive-complexity": "off",
"sort-keys-fix/sort-keys-fix": "warn",
"unicorn/prefer-event-target": "off",
"simple-import-sort/imports": "warn",
Expand Down
2 changes: 2 additions & 0 deletions cspell.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ words:
- hass
- gotify
- desync
- homeassistant
- zeroconf
- endregion
ignoreWords: []
import: []
2,433 changes: 1,674 additions & 759 deletions package-lock.json

Large diffs are not rendered by default.

21 changes: 12 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"test": "./scripts/test.sh",
"lint": "eslint src",
"prepublishOnly": "npm run build",
"upgrade": "ncu -u; npm i"
"upgrade": "ncu -u -f '@digital-alchemy/*'; npm i"
},
"author": {
"url": "https://github.com/zoe-codez",
Expand All @@ -25,24 +25,27 @@
},
"license": "MIT",
"dependencies": {
"@digital-alchemy/core": "^0.3.13",
"@digital-alchemy/hass": "^0.3.23",
"@digital-alchemy/core": "^0.3.16",
"@digital-alchemy/fastify-extension": "^0.3.3",
"@digital-alchemy/hass": "^0.3.25",
"bonjour": "^3.5.0",
"dayjs": "^1.11.11",
"prom-client": "^15.1.2",
"ws": "^8.17.0"
},
"devDependencies": {
"@cspell/eslint-plugin": "^8.8.0",
"@cspell/eslint-plugin": "^8.8.1",
"@types/bonjour": "^3.5.13",
"@types/figlet": "^1.5.8",
"@types/jest": "^29.5.12",
"@types/js-yaml": "^4.0.9",
"@types/minimist": "^1.2.5",
"@types/mute-stream": "^0.0.4",
"@types/node": "^20.12.10",
"@types/node": "^20.12.12",
"@types/uuid": "^9.0.8",
"@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "7.8.0",
"@typescript-eslint/parser": "7.8.0",
"@typescript-eslint/eslint-plugin": "7.9.0",
"@typescript-eslint/parser": "7.9.0",
"eslint": "8.57.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-import": "^2.29.1",
Expand All @@ -53,13 +56,13 @@
"eslint-plugin-simple-import-sort": "^12.1.0",
"eslint-plugin-sonarjs": "^1.0.3",
"eslint-plugin-sort-keys-fix": "^1.1.2",
"eslint-plugin-unicorn": "^52.0.0",
"eslint-plugin-unicorn": "^53.0.0",
"jest": "^29.7.0",
"jest-environment-node": "^29.7.0",
"npm-check-updates": "^16.14.20",
"prettier": "^3.2.5",
"ts-jest": "^29.1.2",
"tsx": "^4.9.3",
"tsx": "^4.10.3",
"type-fest": "^4.18.2",
"typescript": "^5.4.5"
}
Expand Down
2 changes: 1 addition & 1 deletion src/extensions/binary-sensor.extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const isOnOff = (value: unknown): value is OnOff =>
export function BinarySensor({ context, synapse, logger }: TServiceParams) {
const callbacks = [] as BinarySensorUpdateCallback[];

const registry = synapse.registry<VirtualBinarySensor>({
const registry = synapse.registry.create<VirtualBinarySensor>({
context,
details: item => ({ state: item.state }),
domain: "binary_sensor",
Expand Down
49 changes: 49 additions & 0 deletions src/extensions/bonjour.extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { is, TServiceParams } from "@digital-alchemy/core";
import Bonjour from "bonjour";
import { createHash } from "crypto";
import { hostname, userInfo } from "os";

export function BonjourExtension({
config,
lifecycle,
logger,
internal,
}: TServiceParams) {
let bonjour: Bonjour.Bonjour;
function uniqueProperties(): string[] {
return [hostname(), userInfo().username, internal.boot.application.name];
}

// Create a UNIQUE_ID, if one is not provided via .bootstrap()
lifecycle.onPreInit(() => {
if (is.empty(config.synapse.METADATA_UNIQUE_ID)) {
internal.boilerplate.configuration.set(
"synapse",
"METADATA_UNIQUE_ID",
createHash("md5").update(uniqueProperties().join("-")).digest("hex"),
);
}
});

lifecycle.onReady(() => {
logger.info("WAT");
});

lifecycle.onPostConfig(() => {
if (!config.synapse.PUBLISH_BONJOUR) {
return;
}
logger.info({ name: "onPostConfig" }, `publishing`);
bonjour = Bonjour();
bonjour.publish({
name: internal.boot.application.name,
port: config.fastify.PORT,
txt: { UNIQUE_ID: config.synapse.METADATA_UNIQUE_ID },
type: "_da_synapse.tcp",
});
});

lifecycle.onShutdownStart(() => {
bonjour?.destroy();
});
}
6 changes: 4 additions & 2 deletions src/extensions/button.extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
BUTTON_ERRORS,
BUTTON_EXECUTION_COUNT,
BUTTON_EXECUTION_TIME,
TSynapseId,
} from "..";

type TButton = {
Expand All @@ -16,7 +17,7 @@ type TButton = {

type HassButtonUpdateEvent = {
event_type: "digital_alchemy_activate";
data: { id: string };
data: { id: TSynapseId };
};

export function Button({
Expand All @@ -26,8 +27,9 @@ export function Button({
synapse,
internal,
}: TServiceParams) {
const registry = synapse.registry<TButton>({
const registry = synapse.registry.create<TButton>({
context,
// @ts-expect-error fixme
domain: "button",
});

Expand Down
15 changes: 15 additions & 0 deletions src/extensions/configure.extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { is, TServiceParams } from "@digital-alchemy/core";

export function Configure({ lifecycle, config, internal }: TServiceParams) {
lifecycle.onPreInit(() => {
if (is.empty(config.synapse.METADATA_NAME)) {
internal.boilerplate.configuration.set(
"synapse",
"METADATA_NAME",
internal.boot.application.name,
);
}
});


}
29 changes: 29 additions & 0 deletions src/extensions/controller.extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { TServiceParams } from "@digital-alchemy/core";
import { hostname, userInfo } from "os";

import { SynapseDescribeResponse } from "../helpers";

export function Controller({
fastify,
config,
synapse,
logger,
internal,
}: TServiceParams) {
fastify.routes(server => {
server.get("/synapse", (): SynapseDescribeResponse => {
logger.info(`describe app`);
return {
app: internal.boot.application.name,
hostname: hostname(),
meta: config.synapse.METADATA,
unique_id: config.synapse.METADATA_UNIQUE_ID,
username: userInfo().username,
};
});

server.get("/synapse/configuration", () =>
synapse.registry.buildEntityState(),
);
});
}
3 changes: 3 additions & 0 deletions src/extensions/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
export * from "./binary-sensor.extension";
export * from "./bonjour.extension";
export * from "./button.extension";
export * from "./configure.extension";
export * from "./controller.extension";
export * from "./number.extension";
export * from "./registry.extension";
export * from "./scene.extension";
Expand Down
2 changes: 1 addition & 1 deletion src/extensions/number.extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type BaseNumberAttributes = {
};

export function NumberDomain({ context, synapse }: TServiceParams) {
const registry = synapse.registry<VirtualNumber>({
const registry = synapse.registry.create<VirtualNumber>({
context,
details: entity => ({
attributes: entity._rawAttributes,
Expand Down
32 changes: 15 additions & 17 deletions src/extensions/registry.extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,28 @@ export function Registry({
type TDomain = ReturnType<typeof create>;
const domains = new Map<ALL_DOMAINS, TDomain>();
let initComplete = false;
const getIdentifier = () =>
config.synapse.APPLICATION_IDENTIFIER || internal.boot.application.name;
const getIdentifier = () => internal.boot.application.name;

async function SendEntityList() {
logger.debug(`send entity list`);
function buildEntityState() {
const domains = Object.fromEntries(
[...LOADERS.keys()].map(domain => {
const data = LOADERS.get(domain)();
return [domain, data];
}),
);
const hash = generateHash(JSON.stringify(domains));
return {
boot: BOOT_TIME,
domains,
hash,
};
}

async function SendEntityList() {
logger.debug(`send entity list`);
await hass.socket.fireEvent(
`digital_alchemy_application_state`,
{ app: getIdentifier(), boot: BOOT_TIME, domains, hash },
false,
buildEntityState(),
);
}

Expand All @@ -84,7 +90,6 @@ export function Registry({
await hass.socket.fireEvent(
`digital_alchemy_heartbeat_${getIdentifier()}`,
{},
false,
),
interval: config.synapse.HEARTBEAT_INTERVAL * SECOND,
});
Expand All @@ -98,7 +103,6 @@ export function Registry({
await hass.socket.fireEvent(
`digital_alchemy_application_shutdown_${getIdentifier()}`,
{},
false,
);
});

Expand All @@ -109,7 +113,6 @@ export function Registry({
await hass.socket.fireEvent(
`digital_alchemy_heartbeat_${getIdentifier()}`,
{},
false,
);
if (!config.synapse.ANNOUNCE_AT_CONNECT) {
return;
Expand Down Expand Up @@ -210,7 +213,7 @@ export function Registry({
// > Rebooting hass could be done to clear out the values if needed
// > Need to go through synapse based side channels to avoid retrieving an "unavailable" state
hass.socket.onConnect(async () => {
if (is.empty(LOAD_ME)) {
if (is.empty(LOAD_ME) || true) {
return;
}
logger.debug(
Expand Down Expand Up @@ -246,7 +249,6 @@ export function Registry({
await hass.socket.fireEvent(
`digital_alchemy_retrieve_state_${domain}`,
{ app: getIdentifier() },
false,
);
// wait 1 second
await sleep(SECOND);
Expand Down Expand Up @@ -346,11 +348,7 @@ export function Registry({
);
return;
}
await hass.socket.fireEvent(
`digital_alchemy_event`,
{ data, id },
false,
);
await hass.socket.fireEvent(`digital_alchemy_event`, { data, id });
},
// ### setCache
async setCache(id: TSynapseId, value: unknown): Promise<void> {
Expand All @@ -361,7 +359,7 @@ export function Registry({
return out;
}
create.registeredDomains = domains;
return create;
return { buildEntityState, create };
}

export type TRegistry<DATA extends unknown = unknown> = {
Expand Down
3 changes: 2 additions & 1 deletion src/extensions/scene.extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ export function Scene({
context,
internal,
}: TServiceParams) {
const registry = synapse.registry<TScene>({
const registry = synapse.registry.create<TScene>({
context,
// @ts-expect-error fixme
domain: "scene",
});

Expand Down
2 changes: 1 addition & 1 deletion src/extensions/select.extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type BaseSelectAttributes = {
};

export function SelectDomain({ context, synapse }: TServiceParams) {
const registry = synapse.registry<VirtualSelect>({
const registry = synapse.registry.create<VirtualSelect>({
context,
details: entity => ({
attributes: entity._rawAttributes,
Expand Down
2 changes: 1 addition & 1 deletion src/extensions/sensor.extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type VirtualSensor<
} & SensorDeviceClasses;

export function Sensor({ context, synapse }: TServiceParams) {
const registry = synapse.registry<VirtualSensor>({
const registry = synapse.registry.create<VirtualSensor>({
context,
details: entity => ({
attributes: entity._rawAttributes,
Expand Down
5 changes: 2 additions & 3 deletions src/extensions/storage.extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@ export function ValueStorage({
}
// #region file storage init
// APPLICATION_IDENTIFIER > app name
const APP_NAME = is.empty(config.synapse.APPLICATION_IDENTIFIER)
? internal.boot.application.name
: config.synapse.APPLICATION_IDENTIFIER;
const APP_NAME = internal.boot.application.name;

const localConfig = join(cwd(), APP_NAME);
const file = is.empty(config.synapse.STORAGE_FILE_LOCATION)
Expand Down Expand Up @@ -142,6 +140,7 @@ export function ValueStorage({
lifecycle.onBootstrap(async () => {
const loaders = synapse.storage.storage.load;
await each(registry.list(), async (id: TSynapseId) => {
return;
for (let i = START; i <= loaders.length; i++) {
const result = await loaders[i](id, registry);
if (result) {
Expand Down
2 changes: 1 addition & 1 deletion src/extensions/switch.extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type UpdateSwitchBody = {
type SwitchUpdateCallback = (state: boolean) => TBlackHole;

export function Switch({ logger, context, hass, synapse }: TServiceParams) {
const registry = synapse.registry<VirtualSwitch>({
const registry = synapse.registry.create<VirtualSwitch>({
context,
details: entity => ({
state: entity.state,
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/metrics.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const BUTTON_EXECUTION_TIME = new Summary({
});

export const ENTITY_SET_ATTRIBUTE = new Counter({
help: "",
help: "Set attribute",
labelNames: ["domain", "name", "attribute"] as const,
name: "digital_alchemy_virtual_entity_set_attribute",
});
Loading

0 comments on commit 20fa961

Please sign in to comment.