mirror of
https://github.com/adrianjagielak/home-assistant-futurehome.git
synced 2026-02-11 07:15:38 +00:00
Add support for 'water_heater' service
This commit is contained in:
@@ -54,6 +54,7 @@ import { sensor_watpressure__components } from '../services/sensor_watpressure';
|
||||
import { sensor_wattemp__components } from '../services/sensor_wattemp';
|
||||
import { sensor_weight__components } from '../services/sensor_weight';
|
||||
import { thermostat__components } from '../services/thermostat';
|
||||
import { water_heater__components } from '../services/water_heater';
|
||||
import { abbreviateHaMqttKeys } from './abbreviate_ha_mqtt_keys';
|
||||
import { ha } from './globals';
|
||||
import { HaMqttComponent } from './mqtt_components/_component';
|
||||
@@ -209,6 +210,7 @@ const serviceHandlers: {
|
||||
sensor_wattemp: sensor_wattemp__components,
|
||||
sensor_weight: sensor_weight__components,
|
||||
thermostat: thermostat__components,
|
||||
water_heater: water_heater__components,
|
||||
};
|
||||
|
||||
export function haPublishDevice(parameters: {
|
||||
|
||||
@@ -14,6 +14,11 @@ import { ha } from './globals';
|
||||
{
|
||||
"name": "presence",
|
||||
"values": [
|
||||
{
|
||||
"ts": "2025-07-22 16:21:31 +0200",
|
||||
"val": true,
|
||||
"val_t": "bool"
|
||||
},
|
||||
{
|
||||
"ts": "2025-07-22 16:21:30 +0200",
|
||||
"val": false,
|
||||
@@ -52,18 +57,85 @@ import { ha } from './globals';
|
||||
}
|
||||
],
|
||||
"name": "battery"
|
||||
},
|
||||
{
|
||||
"addr": "/rt:dev/rn:hoiax/ad:1/sv:water_heater/ad:2",
|
||||
"attributes": [
|
||||
{
|
||||
"name": "state",
|
||||
"values": [
|
||||
{
|
||||
"ts": "2023-04-03 13:37:22 +0200",
|
||||
"val": "idle",
|
||||
"val_t": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "setpoint",
|
||||
"values": [
|
||||
{
|
||||
"ts": "2023-03-27 14:19:52 +0200",
|
||||
"val": {
|
||||
"temp": 49,
|
||||
"type": "vacation",
|
||||
"unit": "C"
|
||||
},
|
||||
"val_t": "object"
|
||||
},
|
||||
{
|
||||
"ts": "2023-03-27 14:19:52 +0200",
|
||||
"val": {
|
||||
"temp": 60,
|
||||
"type": "normal",
|
||||
"unit": "C"
|
||||
},
|
||||
"val_t": "object"
|
||||
},
|
||||
{
|
||||
"ts": "2023-12-21 09:44:28 +0100",
|
||||
"val": {
|
||||
"temp": 85.0,
|
||||
"type": "boost",
|
||||
"unit": "C"
|
||||
},
|
||||
"val_t": "object"
|
||||
},
|
||||
{
|
||||
"ts": "2023-03-27 14:19:52 +0200",
|
||||
"val": {
|
||||
"temp": 60,
|
||||
"type": "external",
|
||||
"unit": "C"
|
||||
},
|
||||
"val_t": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "mode",
|
||||
"values": [
|
||||
{
|
||||
"ts": "2023-04-05 16:08:43 +0200",
|
||||
"val": "off",
|
||||
"val_t": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"name": "water_heater"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Output (assuming hub ID 123456):
|
||||
Saved state (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
|
||||
"presence": true
|
||||
},
|
||||
"/rt:dev/rn:zigbee/ad:1/sv:battery/ad:1_1": {
|
||||
"lvl": 1,
|
||||
@@ -71,6 +143,28 @@ topic: homeassistant/device/futurehome_123456_1/state
|
||||
"event": "low_battery",
|
||||
"status": "deactiv"
|
||||
}
|
||||
},
|
||||
"/rt:dev/rn:hoiax/ad:1/sv:water_heater/ad:2": {
|
||||
"state": "idle",
|
||||
"setpoint": {
|
||||
"vacation": {
|
||||
"temp": 49,
|
||||
"unit": "C"
|
||||
},
|
||||
"normal": {
|
||||
"temp": 60,
|
||||
"unit": "C"
|
||||
},
|
||||
"boost": {
|
||||
"temp": 85.0,
|
||||
"unit": "C"
|
||||
},
|
||||
"external": {
|
||||
"temp": 60,
|
||||
"unit": "C"
|
||||
}
|
||||
},
|
||||
"mode": "off"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -81,6 +175,48 @@ const haStateCache: Record<
|
||||
Record<string, Record<string, any>> // payload (addr → { attr → value })
|
||||
> = {};
|
||||
|
||||
/**
|
||||
* Helper function to process multiple values for an attribute, handling typed values
|
||||
*/
|
||||
function processAttributeValues(values: any[]): any {
|
||||
if (!values || values.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Sort by timestamp to get the latest values first
|
||||
const sortedValues = [...values].sort((a, b) => {
|
||||
const tsA = new Date(a.ts).getTime();
|
||||
const tsB = new Date(b.ts).getTime();
|
||||
return tsB - tsA; // Latest first
|
||||
});
|
||||
|
||||
// Check if any value has a 'type' property in its val object
|
||||
const hasTypedValues = sortedValues.some(
|
||||
(v) => v.val && typeof v.val === 'object' && v.val.type,
|
||||
);
|
||||
|
||||
if (!hasTypedValues) {
|
||||
// No typed values, return the latest value
|
||||
return sortedValues[0].val;
|
||||
}
|
||||
|
||||
// Group by type, keeping only the latest value for each type
|
||||
const typeMap: Record<string, any> = {};
|
||||
|
||||
for (const value of sortedValues) {
|
||||
if (value.val && typeof value.val === 'object' && value.val.type) {
|
||||
const type = value.val.type;
|
||||
if (!typeMap[type]) {
|
||||
// Create a copy without the 'type' property
|
||||
const { type: _, ...valueWithoutType } = value.val;
|
||||
typeMap[type] = valueWithoutType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return typeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes the full state of a Futurehome device to Home Assistant and
|
||||
* stores a copy in the private cache above.
|
||||
@@ -102,8 +238,10 @@ export function haUpdateState(parameters: {
|
||||
const serviceState: Record<string, any> = {};
|
||||
|
||||
for (const attr of service.attributes || []) {
|
||||
const value = attr.values?.[0]?.val;
|
||||
serviceState[attr.name] = value;
|
||||
const processedValue = processAttributeValues(attr.values || []);
|
||||
if (processedValue !== undefined) {
|
||||
serviceState[attr.name] = processedValue;
|
||||
}
|
||||
}
|
||||
|
||||
haState[service.addr] = serviceState;
|
||||
@@ -123,13 +261,13 @@ export function haUpdateState(parameters: {
|
||||
*
|
||||
* @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 value The new sensor reading (number, boolean, string, object with type, …)
|
||||
* @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: {
|
||||
export function haUpdateStateValueReport(parameters: {
|
||||
topic: string;
|
||||
value: any;
|
||||
attrName: string;
|
||||
@@ -140,8 +278,39 @@ export function haUpdateStateSensorReport(parameters: {
|
||||
for (const [stateTopic, payload] of Object.entries(haStateCache)) {
|
||||
if (!payload[sensorAddr]) continue;
|
||||
|
||||
// Update the reading in‑place
|
||||
payload[sensorAddr][parameters.attrName] = parameters.value;
|
||||
// Check if the new value has a type property
|
||||
if (
|
||||
parameters.value &&
|
||||
typeof parameters.value === 'object' &&
|
||||
parameters.value.type
|
||||
) {
|
||||
// Handle typed value update
|
||||
const type = parameters.value.type;
|
||||
const { type: _, ...valueWithoutType } = parameters.value;
|
||||
|
||||
// Get current attribute value
|
||||
const currentAttrValue = payload[sensorAddr][parameters.attrName];
|
||||
|
||||
if (
|
||||
currentAttrValue &&
|
||||
typeof currentAttrValue === 'object' &&
|
||||
!Array.isArray(currentAttrValue)
|
||||
) {
|
||||
// Current value is already a type map, update the specific type
|
||||
payload[sensorAddr][parameters.attrName] = {
|
||||
...currentAttrValue,
|
||||
[type]: valueWithoutType,
|
||||
};
|
||||
} else {
|
||||
// Current value is not a type map, convert it to one
|
||||
payload[sensorAddr][parameters.attrName] = {
|
||||
[type]: valueWithoutType,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Handle regular value update (non-typed)
|
||||
payload[sensorAddr][parameters.attrName] = parameters.value;
|
||||
}
|
||||
|
||||
log.debug(
|
||||
`Publishing updated sensor value for "${sensorAddr}" to "${stateTopic}"`,
|
||||
|
||||
Reference in New Issue
Block a user