mirror of
https://github.com/adrianjagielak/home-assistant-futurehome.git
synced 2025-09-13 07:37:09 +00:00
Add initial version of the add-on code
This commit is contained in:
parent
bcfa60a749
commit
1c43d8a3ec
@ -1,15 +1,12 @@
|
|||||||
# Futurehome Home Assistant add-on
|
# Futurehome Home Assistant add-on
|
||||||
|
|
||||||
Futurehome add-on for Home Assistant.
|
Futurehome add-on for Home Assistant. The add-on aims to be a complete drop-in replacement for the official Futurehome app, implementing support for all Futurehome-supported device types.
|
||||||
|
|
||||||
[](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Fadrianjagielak%2Fhome-assistant-futurehome)
|
[](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Fadrianjagielak%2Fhome-assistant-futurehome)
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
Notes to developers after forking or using the github template feature:
|
Notes to developers after forking or using the github template feature:
|
||||||
- While developing comment out the 'image' key from 'example/config.yaml' to make the supervisor build the addon
|
|
||||||
- Remember to put this back when pushing up your changes.
|
|
||||||
- When you merge to the 'main' branch of your repository a new build will be triggered.
|
|
||||||
- Make sure you adjust the 'version' key in 'example/config.yaml' when you do that.
|
- Make sure you adjust the 'version' key in 'example/config.yaml' when you do that.
|
||||||
- Make sure you update 'example/CHANGELOG.md' when you do that.
|
- Make sure you update 'example/CHANGELOG.md' when you do that.
|
||||||
- The first time this runs you might need to adjust the image configuration on github container registry to make it public
|
- The first time this runs you might need to adjust the image configuration on github container registry to make it public
|
||||||
|
2
futurehome/.gitignore
vendored
Normal file
2
futurehome/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
node_modules/*
|
||||||
|
dist/*
|
@ -3,3 +3,7 @@
|
|||||||
## 1.0.0
|
## 1.0.0
|
||||||
|
|
||||||
- Initial release
|
- Initial release
|
||||||
|
|
||||||
|
## 0.0.3
|
||||||
|
|
||||||
|
- Added initial version of the add-on code.
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
# https://developers.home-assistant.io/docs/add-ons/configuration#add-on-dockerfile
|
|
||||||
ARG BUILD_FROM
|
ARG BUILD_FROM
|
||||||
FROM $BUILD_FROM
|
FROM ${BUILD_FROM}
|
||||||
|
|
||||||
# Execute during the build of the image
|
# ---------- install NodeJS ----------
|
||||||
ARG TEMPIO_VERSION BUILD_ARCH
|
RUN apk add --no-cache nodejs npm python3 make g++
|
||||||
RUN \
|
|
||||||
curl -sSLf -o /usr/bin/tempio \
|
|
||||||
"https://github.com/home-assistant/tempio/releases/download/${TEMPIO_VERSION}/tempio_${BUILD_ARCH}"
|
|
||||||
|
|
||||||
# Copy root filesystem
|
# ---------- copy source -------------
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
COPY package.json tsconfig.json ./
|
||||||
|
RUN npm install --omit=dev
|
||||||
|
COPY src ./src
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# ---------- copy s6 service files ---
|
||||||
COPY rootfs /
|
COPY rootfs /
|
||||||
|
@ -42,13 +42,6 @@ profile example flags=(attach_disconnected,mediate_deleted) {
|
|||||||
# Access to mapped volumes specified in config.json
|
# Access to mapped volumes specified in config.json
|
||||||
/share/** rw,
|
/share/** rw,
|
||||||
|
|
||||||
# Access required for service functionality
|
|
||||||
# Note: List was built by doing the following:
|
|
||||||
# 1. Add what is obviously needed based on what is in the script
|
|
||||||
# 2. Add `complain` as a flag to this profile temporarily and run the addon
|
|
||||||
# 3. Review the audit log with `journalctl _TRANSPORT="audit" -g 'apparmor="ALLOWED"'` and add other access as needed
|
|
||||||
# Remember to remove the `complain` flag when you are done
|
|
||||||
/usr/bin/my_program r,
|
|
||||||
/bin/bash rix,
|
/bin/bash rix,
|
||||||
/bin/echo ix,
|
/bin/echo ix,
|
||||||
/etc/passwd r,
|
/etc/passwd r,
|
||||||
|
@ -7,8 +7,6 @@ build_from:
|
|||||||
i386: "ghcr.io/home-assistant/i386-base:3.15"
|
i386: "ghcr.io/home-assistant/i386-base:3.15"
|
||||||
labels:
|
labels:
|
||||||
org.opencontainers.image.title: "Home Assistant Add-on: Futurehome"
|
org.opencontainers.image.title: "Home Assistant Add-on: Futurehome"
|
||||||
org.opencontainers.image.description: "Futurehome Home Assistant add-on"
|
org.opencontainers.image.description: "Integrates Futurehome Smarthub via local FIMP/MQTT"
|
||||||
org.opencontainers.image.source: "https://github.com/adrianjagielak/home-assistant-futurehome"
|
org.opencontainers.image.source: "https://github.com/adrianjagielak/home-assistant-futurehome"
|
||||||
org.opencontainers.image.licenses: "MIT License"
|
org.opencontainers.image.licenses: "MIT License"
|
||||||
args:
|
|
||||||
TEMPIO_VERSION: "2021.09.0"
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# https://developers.home-assistant.io/docs/add-ons/configuration#add-on-config
|
# https://developers.home-assistant.io/docs/add-ons/configuration#add-on-config
|
||||||
name: Futurehome add-on
|
name: Futurehome add-on
|
||||||
version: "0.0.3"
|
version: "0.0.4"
|
||||||
slug: futurehome
|
slug: futurehome
|
||||||
description: Futurehome Home Assistant add-on
|
description: Local Futurehome Smarthub integration
|
||||||
url: "https://github.com/adrianjagielak/home-assistant-futurehome"
|
url: "https://github.com/adrianjagielak/home-assistant-futurehome"
|
||||||
arch:
|
arch:
|
||||||
- armhf
|
- armhf
|
||||||
@ -11,10 +11,18 @@ arch:
|
|||||||
- amd64
|
- amd64
|
||||||
- i386
|
- i386
|
||||||
init: false
|
init: false
|
||||||
map:
|
|
||||||
- share:rw
|
services:
|
||||||
|
- mqtt:need
|
||||||
|
|
||||||
options:
|
options:
|
||||||
message: "Hello world..."
|
hub_ip: ""
|
||||||
|
username: ""
|
||||||
|
password: ""
|
||||||
|
|
||||||
schema:
|
schema:
|
||||||
message: "str?"
|
hub_ip: "str?"
|
||||||
|
username: "str"
|
||||||
|
password: "str"
|
||||||
|
|
||||||
image: "ghcr.io/adrianjagielak/{arch}-home-assistant-futurehome"
|
image: "ghcr.io/adrianjagielak/{arch}-home-assistant-futurehome"
|
||||||
|
26
futurehome/eslint.config.mjs
Normal file
26
futurehome/eslint.config.mjs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import js from "@eslint/js";
|
||||||
|
import globals from "globals";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
import { defineConfig } from "eslint/config";
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
js.configs.recommended,
|
||||||
|
tseslint.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ["**/*.{ts,tsx}"],
|
||||||
|
languageOptions: {
|
||||||
|
parser: tseslint.parser,
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: "latest",
|
||||||
|
sourceType: "module",
|
||||||
|
},
|
||||||
|
globals: globals.node,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
"@typescript-eslint": tseslint.plugin,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
2805
futurehome/package-lock.json
generated
Normal file
2805
futurehome/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
futurehome/package.json
Normal file
28
futurehome/package.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "ha-futurehome-addon",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Home Assistant add-on talking to Futurehome Smarthub via FIMP",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint src/**.ts",
|
||||||
|
"build": "rimraf ./dist && tsc",
|
||||||
|
"start": "node dist/index.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mqtt": "^5.13.3",
|
||||||
|
"uuid": "^11.1.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.31.0",
|
||||||
|
"@types/node": "^24.0.15",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
||||||
|
"@typescript-eslint/parser": "^8.38.0",
|
||||||
|
"eslint": "^9.31.0",
|
||||||
|
"globals": "^16.3.0",
|
||||||
|
"rimraf": "^6.0.1",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.8.3",
|
||||||
|
"typescript-eslint": "^8.38.0"
|
||||||
|
},
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
@ -1,15 +0,0 @@
|
|||||||
#!/usr/bin/env bashio
|
|
||||||
# ==============================================================================
|
|
||||||
# Take down the S6 supervision tree when example fails
|
|
||||||
# s6-overlay docs: https://github.com/just-containers/s6-overlay
|
|
||||||
# ==============================================================================
|
|
||||||
|
|
||||||
declare APP_EXIT_CODE=${1}
|
|
||||||
|
|
||||||
if [[ "${APP_EXIT_CODE}" -ne 0 ]] && [[ "${APP_EXIT_CODE}" -ne 256 ]]; then
|
|
||||||
bashio::log.warning "Halt add-on with exit code ${APP_EXIT_CODE}"
|
|
||||||
echo "${APP_EXIT_CODE}" > /run/s6-linux-init-container-results/exitcode
|
|
||||||
exec /run/s6/basedir/bin/halt
|
|
||||||
fi
|
|
||||||
|
|
||||||
bashio::log.info "Service restart after closing"
|
|
@ -1,19 +0,0 @@
|
|||||||
#!/usr/bin/with-contenv bashio
|
|
||||||
# ==============================================================================
|
|
||||||
# Start the example service
|
|
||||||
# s6-overlay docs: https://github.com/just-containers/s6-overlay
|
|
||||||
# ==============================================================================
|
|
||||||
|
|
||||||
# Add your code here
|
|
||||||
|
|
||||||
# Declare variables
|
|
||||||
declare message
|
|
||||||
|
|
||||||
## Get the 'message' key from the user config options.
|
|
||||||
message=$(bashio::config 'message')
|
|
||||||
|
|
||||||
## Print the message the user supplied, defaults to "Hello World..."
|
|
||||||
bashio::log.info "${message:="Hello World..."}"
|
|
||||||
|
|
||||||
## Run your program
|
|
||||||
exec /usr/bin/my_program
|
|
3
futurehome/rootfs/etc/services.d/futurehome/finish
Executable file
3
futurehome/rootfs/etc/services.d/futurehome/finish
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/with-contenv bashio
|
||||||
|
# Nothing special – just allow S6 to stop gracefully
|
||||||
|
exit 0
|
13
futurehome/rootfs/etc/services.d/futurehome/run
Executable file
13
futurehome/rootfs/etc/services.d/futurehome/run
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/with-contenv bashio
|
||||||
|
# ==============================================================================
|
||||||
|
# s6-overlay docs: https://github.com/just-containers/s6-overlay
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
export FH_HUB_IP=$(bashio::config 'hub_ip')
|
||||||
|
export FH_USERNAME=$(bashio::config 'username')
|
||||||
|
export FH_PASSWORD=$(bashio::config 'password')
|
||||||
|
export LOG_LEVEL=${LOG_LEVEL:-info}
|
||||||
|
|
||||||
|
/usr/bin/node /usr/src/app/dist/index.js
|
@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
echo "All done!" > /share/example_addon_output.txt
|
|
19
futurehome/src/client.ts
Normal file
19
futurehome/src/client.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import mqtt, { MqttClient } from "mqtt";
|
||||||
|
|
||||||
|
export function connectHub(opts: { hubIp: string; username: string; password: string; }): Promise<MqttClient> {
|
||||||
|
const url = `mqtt://${opts.hubIp || "futurehome-smarthub.local"}`;
|
||||||
|
return makeClient(url, opts.username, opts.password);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function connectHA(): Promise<MqttClient> {
|
||||||
|
const url = "mqtt://homeassistant";
|
||||||
|
return makeClient(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeClient(url: string, username = "", password = ""): Promise<MqttClient> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const client = mqtt.connect(url, { username, password, protocolVersion: 4 });
|
||||||
|
client.once("connect", () => resolve(client));
|
||||||
|
client.once("error", reject);
|
||||||
|
});
|
||||||
|
}
|
27
futurehome/src/discovery.ts
Normal file
27
futurehome/src/discovery.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { MqttClient } from 'mqtt';
|
||||||
|
import { handleBattery } from './parsers/battery';
|
||||||
|
import { handleBinSwitch } from './parsers/out_bin_switch';
|
||||||
|
import { handleLvlSwitch } from './parsers/out_lvl_switch';
|
||||||
|
import { handleTempSensor } from './parsers/sensor_temp';
|
||||||
|
|
||||||
|
// map Futurehome → Home Assistant MQTT Discovery
|
||||||
|
export async function publishDiscovery(client: MqttClient, device: any) {
|
||||||
|
for (const svc of device.services) {
|
||||||
|
switch (svc.name) {
|
||||||
|
case 'battery':
|
||||||
|
handleBattery(client, device, svc);
|
||||||
|
break;
|
||||||
|
case 'out_bin_switch':
|
||||||
|
handleBinSwitch(client, device, svc);
|
||||||
|
break;
|
||||||
|
case 'out_lvl_switch':
|
||||||
|
handleLvlSwitch(client, device, svc);
|
||||||
|
break;
|
||||||
|
case 'sensor_temp':
|
||||||
|
handleTempSensor(client, device, svc);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// not implemented yet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
futurehome/src/index.ts
Normal file
39
futurehome/src/index.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { connectHub, connectHA } from "./client";
|
||||||
|
import { publishDiscovery } from "./discovery";
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const hubIp = process.env.FH_HUB_IP || "";
|
||||||
|
const user = process.env.FH_USERNAME || "";
|
||||||
|
const pass = process.env.FH_PASSWORD || "";
|
||||||
|
|
||||||
|
// 1) Connect to HA broker (for discovery + state)
|
||||||
|
const ha = await connectHA();
|
||||||
|
|
||||||
|
// 2) Connect to Futurehome hub (FIMP traffic)
|
||||||
|
const fimp = await connectHub({ hubIp, username: user, password: pass });
|
||||||
|
|
||||||
|
// -- subscribe to FIMP events -----------------------------------------
|
||||||
|
fimp.subscribe("#");
|
||||||
|
fimp.on("message", (topic, buf) => {
|
||||||
|
try {
|
||||||
|
const msg = JSON.parse(buf.toString());
|
||||||
|
if (msg.type === "evt.pd7.response") {
|
||||||
|
const devices = msg.val?.param?.devices ?? [];
|
||||||
|
devices.forEach((d: any) => publishDiscovery(ha, d));
|
||||||
|
}
|
||||||
|
// …forward state events as needed…
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Bad FIMP JSON", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// -- ask hub for the device list --------------------------------------
|
||||||
|
fimp.publish("pt:j1/mt:cmd/rt:app/rn:vinculum/ad:1", JSON.stringify({
|
||||||
|
type: "cmd.pd7.request",
|
||||||
|
service: "vinculum",
|
||||||
|
uid: crypto.randomUUID(),
|
||||||
|
val_t: "object",
|
||||||
|
val: { cmd: "get", component: "state" },
|
||||||
|
resp_to: "pt:j1/mt:rsp/rt:app/rn:ha-futurehome/ad:addon"
|
||||||
|
}), { qos: 1 });
|
||||||
|
})();
|
22
futurehome/src/parsers/battery.ts
Normal file
22
futurehome/src/parsers/battery.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { MqttClient } from 'mqtt';
|
||||||
|
|
||||||
|
export function handleBattery(client: MqttClient, dev: any, svc: any) {
|
||||||
|
const uid = `fh_${dev.id}_${svc.name}`;
|
||||||
|
const base = `homeassistant/sensor/${uid}`;
|
||||||
|
|
||||||
|
// config
|
||||||
|
client.publish(`${base}/config`, JSON.stringify({
|
||||||
|
name: `${dev.name} Battery`,
|
||||||
|
uniq_id: uid,
|
||||||
|
dev_cla: "battery",
|
||||||
|
stat_t: `${base}/state`,
|
||||||
|
unit_of_meas: "%",
|
||||||
|
device: { identifiers: [dev.id.toString()], name: dev.name, model: dev.model }
|
||||||
|
}), { retain: true });
|
||||||
|
|
||||||
|
// initial state if available
|
||||||
|
const lvl = svc.attributes?.find((a: any) => a.name === 'lvl')?.values?.[0]?.val;
|
||||||
|
if (lvl !== undefined) {
|
||||||
|
client.publish(`${base}/state`, String(lvl), { retain: true });
|
||||||
|
}
|
||||||
|
}
|
35
futurehome/src/parsers/out_bin_switch.ts
Normal file
35
futurehome/src/parsers/out_bin_switch.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { MqttClient } from 'mqtt';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
export function handleBinSwitch(client: MqttClient, dev: any, svc: any) {
|
||||||
|
const uid = `fh_${dev.id}_${svc.name}`;
|
||||||
|
const base = `homeassistant/switch/${uid}`;
|
||||||
|
|
||||||
|
client.publish(`${base}/config`, JSON.stringify({
|
||||||
|
name: dev.name,
|
||||||
|
uniq_id: uid,
|
||||||
|
cmd_t: `${base}/set`,
|
||||||
|
stat_t: `${base}/state`,
|
||||||
|
device: { identifiers: [dev.id.toString()], name: dev.name, model: dev.model }
|
||||||
|
}), { retain: true });
|
||||||
|
|
||||||
|
// current value
|
||||||
|
const bin = svc.attributes?.find((a: any) => a.name === 'binary')?.values?.[0]?.val;
|
||||||
|
client.publish(`${base}/state`, bin ? 'ON' : 'OFF', { retain: true });
|
||||||
|
|
||||||
|
// HA → Smarthub
|
||||||
|
client.subscribe(`${base}/set`, { qos: 0 });
|
||||||
|
client.on('message', (topic, payload) => {
|
||||||
|
if (topic !== `${base}/set`) return;
|
||||||
|
const target = payload.toString() === 'ON';
|
||||||
|
|
||||||
|
client.publish(`pt:j1/mt:cmd/${svc.address}`, JSON.stringify({
|
||||||
|
type: "cmd.binary.set",
|
||||||
|
service: svc.name,
|
||||||
|
uid: uuid(),
|
||||||
|
val_t: "bool",
|
||||||
|
val: target,
|
||||||
|
src: "ha-futurehome"
|
||||||
|
}), { qos: 1 });
|
||||||
|
});
|
||||||
|
}
|
47
futurehome/src/parsers/out_lvl_switch.ts
Normal file
47
futurehome/src/parsers/out_lvl_switch.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { MqttClient } from 'mqtt';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
export function handleLvlSwitch(client: MqttClient, dev: any, svc: any) {
|
||||||
|
const uid = `fh_${dev.id}_${svc.name}`;
|
||||||
|
const base = `homeassistant/light/${uid}`;
|
||||||
|
|
||||||
|
client.publish(`${base}/config`, JSON.stringify({
|
||||||
|
name: dev.name,
|
||||||
|
uniq_id: uid,
|
||||||
|
cmd_t: `${base}/set`,
|
||||||
|
stat_t: `${base}/state`,
|
||||||
|
bri_cmd_t: `${base}/brightness/set`,
|
||||||
|
bri_stat_t: `${base}/brightness/state`,
|
||||||
|
schema: "template",
|
||||||
|
device: { identifiers: [dev.id.toString()], name: dev.name, model: dev.model }
|
||||||
|
}), { retain: true });
|
||||||
|
|
||||||
|
const bin = svc.attributes?.find((a: any) => a.name === 'binary')?.values?.[0]?.val;
|
||||||
|
const lvl = svc.attributes?.find((a: any) => a.name === 'lvl')?.values?.[0]?.val;
|
||||||
|
client.publish(`${base}/state`, bin ? "ON" : "OFF", { retain: true });
|
||||||
|
if (lvl !== undefined) client.publish(`${base}/brightness/state`, String(lvl), { retain: true });
|
||||||
|
|
||||||
|
client.subscribe([`${base}/set`, `${base}/brightness/set`]);
|
||||||
|
|
||||||
|
client.on('message', (topic, payload) => {
|
||||||
|
if (topic === `${base}/set`) {
|
||||||
|
const on = payload.toString() === 'ON';
|
||||||
|
client.publish(`pt:j1/mt:cmd/${svc.address}`, JSON.stringify({
|
||||||
|
type: "cmd.binary.set",
|
||||||
|
service: svc.name,
|
||||||
|
uid: uuid(),
|
||||||
|
val_t: "bool",
|
||||||
|
val: on
|
||||||
|
}), { qos: 1 });
|
||||||
|
} else if (topic === `${base}/brightness/set`) {
|
||||||
|
const value = parseInt(payload.toString(), 10);
|
||||||
|
client.publish(`pt:j1/mt:cmd/${svc.address}`, JSON.stringify({
|
||||||
|
type: "cmd.lvl.set",
|
||||||
|
service: svc.name,
|
||||||
|
uid: uuid(),
|
||||||
|
val_t: "int",
|
||||||
|
val: value
|
||||||
|
}), { qos: 1 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
20
futurehome/src/parsers/sensor_presence.ts
Normal file
20
futurehome/src/parsers/sensor_presence.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { MqttClient } from 'mqtt';
|
||||||
|
|
||||||
|
export function handlePresenceSensor(client: MqttClient, dev: any, svc: any) {
|
||||||
|
const uid = `fh_${dev.id}_${svc.name}`;
|
||||||
|
const base = `homeassistant/sensor/${uid}`;
|
||||||
|
|
||||||
|
client.publish(`${base}/config`, JSON.stringify({
|
||||||
|
name: `${dev.name} Presence`,
|
||||||
|
uniq_id: uid,
|
||||||
|
dev_cla: "presence",
|
||||||
|
stat_t: `${base}/state`,
|
||||||
|
unit_of_meas: "℃",
|
||||||
|
device: { identifiers: [dev.id.toString()], name: dev.name, model: dev.model }
|
||||||
|
}), { retain: true });
|
||||||
|
|
||||||
|
const presence = svc.attributes?.find((a: any) => a.name === 'presence')?.values?.[0]?.val;
|
||||||
|
if (presence !== undefined) {
|
||||||
|
client.publish(`${base}/state`, String(presence), { retain: true });
|
||||||
|
}
|
||||||
|
}
|
20
futurehome/src/parsers/sensor_temp.ts
Normal file
20
futurehome/src/parsers/sensor_temp.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { MqttClient } from 'mqtt';
|
||||||
|
|
||||||
|
export function handleTempSensor(client: MqttClient, dev: any, svc: any) {
|
||||||
|
const uid = `fh_${dev.id}_${svc.name}`;
|
||||||
|
const base = `homeassistant/sensor/${uid}`;
|
||||||
|
|
||||||
|
client.publish(`${base}/config`, JSON.stringify({
|
||||||
|
name: `${dev.name} Temperature`,
|
||||||
|
uniq_id: uid,
|
||||||
|
dev_cla: "temperature",
|
||||||
|
stat_t: `${base}/state`,
|
||||||
|
unit_of_meas: "℃",
|
||||||
|
device: { identifiers: [dev.id.toString()], name: dev.name, model: dev.model }
|
||||||
|
}), { retain: true });
|
||||||
|
|
||||||
|
const temp = svc.attributes?.find((a: any) => a.name === 'sensor')?.values?.[0]?.val;
|
||||||
|
if (temp !== undefined) {
|
||||||
|
client.publish(`${base}/state`, String(temp), { retain: true });
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,10 @@
|
|||||||
configuration:
|
configuration:
|
||||||
message:
|
hub_ip:
|
||||||
name: Message
|
name: Hub IP
|
||||||
description: The message that will be printed to the log when starting this example add-on.
|
description: Optional – leave blank for automatic discovery.
|
||||||
|
username:
|
||||||
|
name: Username
|
||||||
|
description: Your Futurehome local API/MQTT username.
|
||||||
|
password:
|
||||||
|
name: Password
|
||||||
|
description: Your Futurehome local API/MQTT password.
|
||||||
|
13
futurehome/tsconfig.json
Normal file
13
futurehome/tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2019",
|
||||||
|
"module": "commonjs",
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"typeRoots": ["node_modules/@types"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"]
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user