mirror of
https://github.com/adrianjagielak/home-assistant-futurehome.git
synced 2025-09-13 15:47:08 +00:00
Add handling sensor events to immediately update state
This commit is contained in:
parent
9dd9046954
commit
adce00d3d9
@ -75,15 +75,28 @@ topic: homeassistant/device/futurehome_123456_1/state
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
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 } } = {};
|
const haStateCache: Record<
|
||||||
|
string, // state topic
|
||||||
|
Record<string, Record<string, any>> // payload (addr → { attr → value })
|
||||||
|
> = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publishes the full state of a Futurehome device to Home Assistant and
|
||||||
|
* stores a copy in the private cache above.
|
||||||
|
*
|
||||||
|
* Example MQTT topic produced for hub 123456 and device id 1:
|
||||||
|
* homeassistant/device/futurehome_123456_1/state
|
||||||
|
*/
|
||||||
|
export function haUpdateState(parameters: { hubId: string; deviceState: DeviceState }) {
|
||||||
|
const stateTopic = `homeassistant/device/futurehome_${parameters.hubId}_${parameters.deviceState.id?.toString()}/state`;
|
||||||
|
|
||||||
|
const haState: Record<string, Record<string, any>> = {};
|
||||||
|
|
||||||
for (const service of parameters.deviceState.services || []) {
|
for (const service of parameters.deviceState.services || []) {
|
||||||
if (!service.addr) { continue; }
|
if (!service.addr) continue;
|
||||||
|
|
||||||
const serviceState: { [attrName: string]: any } = {};
|
const serviceState: Record<string, any> = {};
|
||||||
|
|
||||||
for (const attr of service.attributes || []) {
|
for (const attr of service.attributes || []) {
|
||||||
const value = attr.values?.[0]?.val;
|
const value = attr.values?.[0]?.val;
|
||||||
@ -93,6 +106,39 @@ export function haUpdateState(parameters: { hubId: string, deviceState: DeviceSt
|
|||||||
haState[service.addr] = serviceState;
|
haState[service.addr] = serviceState;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug(`Publishing HA state "${stateTopic}"`)
|
log.debug(`Publishing HA state "${stateTopic}"`);
|
||||||
ha?.publish(stateTopic, JSON.stringify(haState), { retain: true, qos: 2 });
|
ha?.publish(stateTopic, JSON.stringify(haState), { retain: true, qos: 2 });
|
||||||
|
|
||||||
|
// ---- cache state for later incremental updates ----
|
||||||
|
haStateCache[stateTopic] = haState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incrementally updates a single sensor value inside cached state payload
|
||||||
|
* that references the given device‑service address and republishes
|
||||||
|
* the modified payload(s).
|
||||||
|
*
|
||||||
|
* @param topic Full FIMP event topic, e.g.
|
||||||
|
* "pt:j1/mt:evt/rt:dev/rn:zigbee/ad:1/sv:sensor_temp/ad:3_1"
|
||||||
|
* @param value The new sensor reading (number, boolean, string, …)
|
||||||
|
* @param attrName Attribute name to store the reading to
|
||||||
|
*
|
||||||
|
* The prefix "pt:j1/mt:evt" is removed before matching so that the remainder
|
||||||
|
* exactly matches the address keys stored in the cached HA payloads.
|
||||||
|
*/
|
||||||
|
export function haUpdateStateSensorReport(parameters: { topic: string; value: any, attrName: string }) {
|
||||||
|
// Strip the FIMP envelope so we end up with "/rt:dev/…/ad:x_y"
|
||||||
|
const sensorAddr = parameters.topic.replace(/^pt:j1\/mt:evt/, "");
|
||||||
|
|
||||||
|
for (const [stateTopic, payload] of Object.entries(haStateCache)) {
|
||||||
|
if (!payload[sensorAddr]) continue;
|
||||||
|
|
||||||
|
// Update the reading in‑place
|
||||||
|
payload[sensorAddr][parameters.attrName] = parameters.value;
|
||||||
|
|
||||||
|
log.debug(`Publishing updated sensor value for "${sensorAddr}" to "${stateTopic}"`);
|
||||||
|
ha?.publish(stateTopic, JSON.stringify(payload), { retain: true, qos: 2 });
|
||||||
|
|
||||||
|
haStateCache[stateTopic] = payload;
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ import { getInclusionReport } from "./fimp/inclusion_report";
|
|||||||
import { adapterAddressFromServiceAddress, adapterServiceFromServiceAddress } from "./fimp/helpers";
|
import { adapterAddressFromServiceAddress, adapterServiceFromServiceAddress } from "./fimp/helpers";
|
||||||
import { setHa } from "./ha/globals";
|
import { setHa } from "./ha/globals";
|
||||||
import { haPublishDevice } from "./ha/publish_device";
|
import { haPublishDevice } from "./ha/publish_device";
|
||||||
import { haUpdateState } from "./ha/update_state";
|
import { haUpdateState, haUpdateStateSensorReport } from "./ha/update_state";
|
||||||
import { VinculumPd7Device } from "./fimp/vinculum_pd7_device";
|
import { VinculumPd7Device } from "./fimp/vinculum_pd7_device";
|
||||||
import { haUpdateAvailability } from "./ha/update_availability";
|
import { haUpdateAvailability } from "./ha/update_availability";
|
||||||
|
|
||||||
@ -109,18 +109,48 @@ import { haUpdateAvailability } from "./ha/update_availability";
|
|||||||
fimp.on("message", (topic, buf) => {
|
fimp.on("message", (topic, buf) => {
|
||||||
try {
|
try {
|
||||||
const msg: FimpResponse = JSON.parse(buf.toString());
|
const msg: FimpResponse = JSON.parse(buf.toString());
|
||||||
log.debug(JSON.stringify(msg, null, 0));
|
log.debug(`Received FIMP message on topic "${topic}":\n${JSON.stringify(msg, null, 0)}`);
|
||||||
if (msg.type === "evt.pd7.response") {
|
|
||||||
const devicesState = msg.val?.param?.state?.devices;
|
switch (msg.type) {
|
||||||
if (!devicesState) { return; }
|
case 'evt.pd7.response': {
|
||||||
for (const deviceState of devicesState) {
|
const devicesState = msg.val?.param?.state?.devices;
|
||||||
haUpdateState({ hubId, deviceState });
|
if (!devicesState) { return; }
|
||||||
|
for (const deviceState of devicesState) {
|
||||||
|
haUpdateState({ hubId, deviceState });
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else if (msg.type === "evt.network.all_nodes_report") {
|
case 'evt.sensor.report': {
|
||||||
const devicesAvailability = msg.val;
|
haUpdateStateSensorReport({ topic, value: msg.val, attrName: 'sensor' })
|
||||||
if (!devicesAvailability) { return; }
|
break;
|
||||||
for (const deviceAvailability of devicesAvailability) {
|
}
|
||||||
haUpdateAvailability({ hubId, deviceAvailability });
|
case 'evt.presence.report': {
|
||||||
|
if (!(msg.serv === 'sensor_presence')) { return; }
|
||||||
|
haUpdateStateSensorReport({ topic, value: msg.val, attrName: 'presence' })
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'evt.open.report': {
|
||||||
|
if (!(msg.serv === 'sensor_contact')) { return; }
|
||||||
|
haUpdateStateSensorReport({ topic, value: msg.val, attrName: 'open' })
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'evt.lvl.report': {
|
||||||
|
if (!(msg.serv === 'battery')) { return; }
|
||||||
|
haUpdateStateSensorReport({ topic, value: msg.val, attrName: 'lvl' })
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'evt.alarm.report': {
|
||||||
|
if (!(msg.serv === 'battery')) { return; }
|
||||||
|
haUpdateStateSensorReport({ topic, value: msg.val, attrName: 'alarm' })
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'evt.network.all_nodes_report': {
|
||||||
|
const devicesAvailability = msg.val;
|
||||||
|
if (!devicesAvailability) { return; }
|
||||||
|
for (const deviceAvailability of devicesAvailability) {
|
||||||
|
haUpdateAvailability({ hubId, deviceAvailability });
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user