mirror of
https://github.com/adrianjagielak/home-assistant-futurehome.git
synced 2026-02-11 07:15:38 +00:00
Finish most of the basic features, add few services
This commit is contained in:
7
futurehome/src/ha/globals.ts
Normal file
7
futurehome/src/ha/globals.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { MqttClient } from "mqtt/*";
|
||||
|
||||
export let ha: MqttClient | undefined = undefined;
|
||||
|
||||
export function setHa(client: MqttClient) {
|
||||
ha = client;
|
||||
}
|
||||
131
futurehome/src/ha/publish_device.ts
Normal file
131
futurehome/src/ha/publish_device.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import { InclusionReport, InclusionReportService } from "../fimp/inclusion_report";
|
||||
import { VinculumPd7Device } from "../fimp/vinculum_pd7_device";
|
||||
import { log } from "../logger";
|
||||
import { cmps_battery } from "../services/battery";
|
||||
import { cmps_out_bin_switch } from "../services/out_bin_switch";
|
||||
import { cmps_out_lvl_switch } from "../services/out_lvl_switch";
|
||||
import { cmps_sensor_presence } from "../services/sensor_presence";
|
||||
import { cmps_sensor_temp } from "../services/sensor_temp";
|
||||
import { ha } from "./globals";
|
||||
|
||||
type HaDeviceConfig = {
|
||||
dev: {
|
||||
ids: string | null | undefined,
|
||||
name: string | null | undefined,
|
||||
mf: string | null | undefined,
|
||||
mdl: string | null | undefined,
|
||||
sw: string | null | undefined,
|
||||
sn: string | null | undefined,
|
||||
hw: string | null | undefined,
|
||||
};
|
||||
o: {
|
||||
name: 'futurehome',
|
||||
sw: '1.0',
|
||||
url: 'https://github.com/adrianjagielak/home-assistant-futurehome',
|
||||
};
|
||||
cmps: {
|
||||
[key: string]: CMP;
|
||||
},
|
||||
state_topic: string,
|
||||
availability_topic: string,
|
||||
qos: number,
|
||||
}
|
||||
|
||||
export type CMP = {
|
||||
p: string;
|
||||
device_class?: string;
|
||||
unit_of_measurement?: string;
|
||||
value_template?: string;
|
||||
unique_id: string;
|
||||
}
|
||||
|
||||
const serviceHandlers: {
|
||||
[name: string]: (vinculumDeviceData: VinculumPd7Device, svc: InclusionReportService) => { [key: string]: CMP }
|
||||
} = {
|
||||
battery: cmps_battery,
|
||||
out_bin_switch: cmps_out_bin_switch,
|
||||
out_lvl_switch: cmps_out_lvl_switch,
|
||||
sensor_temp: cmps_sensor_temp,
|
||||
sensor_presence: cmps_sensor_presence,
|
||||
};
|
||||
|
||||
export function haPublishDevice(parameters: { hubId: string, vinculumDeviceData: VinculumPd7Device, deviceInclusionReport: InclusionReport }) {
|
||||
if (!parameters.deviceInclusionReport.services) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cmps: { [key: string]: CMP } = {};
|
||||
|
||||
for (const svc of parameters.deviceInclusionReport.services) {
|
||||
if (!svc.name) { continue; }
|
||||
const handler = serviceHandlers[svc.name];
|
||||
if (handler) {
|
||||
const result = handler(parameters.vinculumDeviceData, svc);
|
||||
for (const key in result) {
|
||||
cmps[key] = result[key];
|
||||
}
|
||||
} else {
|
||||
log.error(`No handler for service: ${svc.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
// "cmps": {
|
||||
// "some_unique_component_id1": {
|
||||
// "p": "sensor",
|
||||
// "device_class":"temperature",
|
||||
// "unit_of_measurement":"°C",
|
||||
// "value_template":"{{ value_json.temperature }}",
|
||||
// "unique_id":"temp01ae_t"
|
||||
// },
|
||||
// "some_unique_id2": {
|
||||
// "p": "sensor",
|
||||
// "device_class":"humidity",
|
||||
// "unit_of_measurement":"%",
|
||||
// "value_template":"{{ value_json.humidity }}",
|
||||
// "unique_id":"temp01ae_h"
|
||||
// },
|
||||
// "bla1": {
|
||||
// "p": "device_automation",
|
||||
// "automation_type": "trigger",
|
||||
// "payload": "short_press",
|
||||
// "topic": "foobar/triggers/button1",
|
||||
// "type": "button_short_press",
|
||||
// "subtype": "button_1"
|
||||
// },
|
||||
// "bla2": {
|
||||
// "p": "sensor",
|
||||
// "state_topic": "foobar/sensor/sensor1",
|
||||
// "unique_id": "bla_sensor001"
|
||||
// }
|
||||
// },
|
||||
|
||||
const configTopic = `homeassistant/device/futurehome_${parameters.hubId}_${parameters.deviceInclusionReport.address}/config`
|
||||
const stateTopic = `homeassistant/device/futurehome_${parameters.hubId}_${parameters.deviceInclusionReport.address}/state`
|
||||
const availabilityTopic = `homeassistant/device/futurehome_${parameters.hubId}_${parameters.deviceInclusionReport.address}/availability`
|
||||
const config: HaDeviceConfig = {
|
||||
dev: {
|
||||
ids: parameters.deviceInclusionReport.address,
|
||||
name:
|
||||
// User-defined device name
|
||||
parameters.vinculumDeviceData?.client?.name ??
|
||||
parameters.deviceInclusionReport.product_name,
|
||||
mf: parameters.deviceInclusionReport.manufacturer_id,
|
||||
mdl: parameters.deviceInclusionReport.product_id,
|
||||
sw: parameters.deviceInclusionReport.sw_ver,
|
||||
sn: parameters.deviceInclusionReport.product_hash,
|
||||
hw: parameters.deviceInclusionReport.hw_ver,
|
||||
},
|
||||
o: {
|
||||
name: 'futurehome',
|
||||
sw: '1.0',
|
||||
url: 'https://github.com/adrianjagielak/home-assistant-futurehome',
|
||||
},
|
||||
cmps: cmps,
|
||||
state_topic: stateTopic,
|
||||
availability_topic: availabilityTopic,
|
||||
qos: 2,
|
||||
};
|
||||
|
||||
log.debug(`Publishing HA device "${configTopic}"`)
|
||||
ha?.publish(configTopic, JSON.stringify(config), { retain: true, qos: 2 });
|
||||
}
|
||||
30
futurehome/src/ha/update_availability.ts
Normal file
30
futurehome/src/ha/update_availability.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { log } from "../logger";
|
||||
import { ha } from "./globals";
|
||||
|
||||
/**
|
||||
* Example raw FIMP availaility (from evt.network.all_nodes_report) input:
|
||||
```json
|
||||
{
|
||||
"address": "1",
|
||||
"hash": "TS0202",
|
||||
"power_source": "battery",
|
||||
"status": "UP",
|
||||
"wakeup_interval": "1"
|
||||
}
|
||||
```
|
||||
|
||||
Output (assuming hub ID 123456):
|
||||
|
||||
```
|
||||
topic: homeassistant/device/futurehome_123456_1/availability
|
||||
online
|
||||
```
|
||||
*/
|
||||
export function haUpdateAvailability(parameters: { hubId: string, deviceAvailability: any }) {
|
||||
const availabilityTopic = `homeassistant/device/futurehome_${parameters.hubId}_${parameters.deviceAvailability.address?.toString()}/availability`
|
||||
|
||||
const availability = parameters.deviceAvailability?.status === "UP" ? "online" : "offline";
|
||||
|
||||
log.debug(`Publishing HA availability "${availabilityTopic}"`)
|
||||
ha?.publish(availabilityTopic, availability, { retain: true, qos: 2 });
|
||||
}
|
||||
98
futurehome/src/ha/update_state.ts
Normal file
98
futurehome/src/ha/update_state.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { DeviceState } from "../fimp/state";
|
||||
import { log } from "../logger";
|
||||
import { ha } from "./globals";
|
||||
|
||||
/**
|
||||
* Example raw FIMP state input:
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"services": [
|
||||
{
|
||||
"addr": "/rt:dev/rn:zigbee/ad:1/sv:sensor_presence/ad:1_1",
|
||||
"attributes": [
|
||||
{
|
||||
"name": "presence",
|
||||
"values": [
|
||||
{
|
||||
"ts": "2025-07-22 16:21:30 +0200",
|
||||
"val": false,
|
||||
"val_t": "bool"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"name": "sensor_presence"
|
||||
},
|
||||
{
|
||||
"addr": "/rt:dev/rn:zigbee/ad:1/sv:battery/ad:1_1",
|
||||
"attributes": [
|
||||
{
|
||||
"name": "lvl",
|
||||
"values": [
|
||||
{
|
||||
"ts": "2025-07-19 00:43:30 +0200",
|
||||
"val": 1,
|
||||
"val_t": "int"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "alarm",
|
||||
"values": [
|
||||
{
|
||||
"ts": "2025-07-22 16:21:30 +0200",
|
||||
"val": {
|
||||
"event": "low_battery",
|
||||
"status": "deactiv"
|
||||
},
|
||||
"val_t": "str_map"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"name": "battery"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Output (assuming hub ID 123456):
|
||||
|
||||
```
|
||||
topic: homeassistant/device/futurehome_123456_1/state
|
||||
{
|
||||
"/rt:dev/rn:zigbee/ad:1/sv:sensor_presence/ad:1_1": {
|
||||
"presence": false
|
||||
},
|
||||
"/rt:dev/rn:zigbee/ad:1/sv:battery/ad:1_1": {
|
||||
"lvl": 1,
|
||||
"alarm": {
|
||||
"event": "low_battery",
|
||||
"status": "deactiv"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
*/
|
||||
export function haUpdateState(parameters: { hubId: string, deviceState: DeviceState }) {
|
||||
const stateTopic = `homeassistant/device/futurehome_${parameters.hubId}_${parameters.deviceState.id?.toString()}/state`
|
||||
|
||||
const haState: { [addr: string]: { [attrName: string]: any } } = {};
|
||||
|
||||
for (const service of parameters.deviceState.services || []) {
|
||||
if (!service.addr) { continue; }
|
||||
|
||||
const serviceState: { [attrName: string]: any } = {};
|
||||
|
||||
for (const attr of service.attributes || []) {
|
||||
const value = attr.values?.[0]?.val;
|
||||
serviceState[attr.name] = value;
|
||||
}
|
||||
|
||||
haState[service.addr] = serviceState;
|
||||
}
|
||||
|
||||
log.debug(`Publishing HA state "${stateTopic}"`)
|
||||
ha?.publish(stateTopic, JSON.stringify(haState), { retain: true, qos: 2 });
|
||||
}
|
||||
Reference in New Issue
Block a user