Skip to content

Commit fcd75dd

Browse files
Pastor02Koenkk
andauthored
feat(add): FDS315 (#9488)
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
1 parent 8904940 commit fcd75dd

1 file changed

Lines changed: 138 additions & 0 deletions

File tree

src/devices/owon.ts

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import {Zcl} from "zigbee-herdsman";
2+
13
import * as fz from "../converters/fromZigbee";
24
import * as tz from "../converters/toZigbee";
35
import * as exposes from "../lib/exposes";
@@ -117,6 +119,50 @@ const fzLocal = {
117119
return payload;
118120
},
119121
} satisfies Fz.Converter,
122+
123+
owonFds315: {
124+
cluster: "fallDetectionOwon",
125+
type: ["attributeReport", "readResponse"],
126+
convert: (model, msg, publish, options, meta) => {
127+
const result: Record<string, unknown> = {};
128+
const data = msg.data;
129+
const status_mapping: Record<number, string> = {
130+
0: "unoccupied",
131+
1: "occupied",
132+
2: "sitting",
133+
3: "on_the_bed",
134+
4: "low_posture",
135+
5: "falling",
136+
};
137+
138+
if (data.status !== undefined) {
139+
const code = data.status;
140+
result.status = status_mapping[code] || `Unknown (${code})`;
141+
}
142+
if (data.breathing_rate !== undefined) result.breathing_rate = data.breathing_rate;
143+
if (data.location_x !== undefined) result.location_x = data.location_x;
144+
if (data.location_y !== undefined) result.location_y = data.location_y;
145+
146+
const keys = [
147+
"bedUpperLeftX",
148+
"bedUpperLeftY",
149+
"bedLowerRightX",
150+
"bedLowerRightY",
151+
"doorCenterX",
152+
"doorCenterY",
153+
"leftFallDetectionRange",
154+
"rightFallDetectionRange",
155+
"frontFallDetectionRange",
156+
];
157+
const values = keys.map((k) => (data[k] !== undefined ? data[k] : null));
158+
159+
if (!values.includes(null)) {
160+
result.fall_detection_settings = values.join(",");
161+
}
162+
163+
return result;
164+
},
165+
} satisfies Fz.Converter,
120166
};
121167

122168
const tzLocal = {
@@ -127,6 +173,50 @@ const tzLocal = {
127173
await entity.command(0xffe0, 0x00, {}, {disableDefaultResponse: true});
128174
},
129175
} satisfies Tz.Converter,
176+
177+
owonFds315SetFallSettings: {
178+
key: ["fall_detection_settings"],
179+
convertSet: async (entity, key, value, meta) => {
180+
const mapping: Record<number, {id: number; type: number}> = {
181+
0: {id: 0x0100, type: 0x29},
182+
1: {id: 0x0101, type: 0x29},
183+
2: {id: 0x0102, type: 0x29},
184+
3: {id: 0x0103, type: 0x29},
185+
4: {id: 0x0108, type: 0x29},
186+
5: {id: 0x0109, type: 0x29},
187+
6: {id: 0x010c, type: 0x21},
188+
7: {id: 0x010d, type: 0x21},
189+
8: {id: 0x010e, type: 0x21},
190+
};
191+
192+
const strValue = String(value);
193+
const values = strValue?.split(",").map(Number);
194+
if (values.length !== 9) throw new Error("Incorrect number of values.");
195+
196+
const payload: Record<number, {value: number; type: number}> = {};
197+
values.forEach((val, idx) => {
198+
const {id, type} = mapping[idx];
199+
payload[id] = {value: val, type};
200+
});
201+
202+
await entity.write("fallDetectionOwon", payload, {manufacturerCode: 0x113c});
203+
return {state: {fall_detection_settings: value}};
204+
},
205+
convertGet: async (entity, key, meta) => {
206+
const attrs = [
207+
"bedUpperLeftX",
208+
"bedUpperLeftY",
209+
"bedLowerRightX",
210+
"bedLowerRightY",
211+
"doorCenterX",
212+
"doorCenterY",
213+
"leftFallDetectionRange",
214+
"rightFallDetectionRange",
215+
"frontFallDetectionRange",
216+
];
217+
await entity.read("fallDetectionOwon", attrs, {manufacturerCode: 0x113c});
218+
},
219+
} satisfies Tz.Converter,
130220
};
131221

132222
export const definitions: DefinitionWithExtend[] = [
@@ -479,4 +569,52 @@ export const definitions: DefinitionWithExtend[] = [
479569
description: "Sleeping pad monitor",
480570
extend: [m.battery(), m.iasZoneAlarm({zoneType: "contact", zoneAttributes: ["alarm_1", "battery_low", "tamper"]})],
481571
},
572+
{
573+
zigbeeModel: ["FDS315"],
574+
model: "FDS315",
575+
vendor: "OWON",
576+
description: "Fall Detection Sensor",
577+
extend: [
578+
m.deviceAddCustomCluster("fallDetectionOwon", {
579+
ID: 0xfd00,
580+
manufacturerCode: Zcl.ManufacturerCode.OWON_TECHNOLOGY_INC,
581+
attributes: {
582+
status: {ID: 0x0000, type: Zcl.DataType.ENUM8},
583+
breathing_rate: {ID: 0x0002, type: Zcl.DataType.UINT8},
584+
location_x: {ID: 0x0003, type: Zcl.DataType.INT16},
585+
location_y: {ID: 0x0004, type: Zcl.DataType.INT16},
586+
bedUpperLeftX: {ID: 0x0100, type: Zcl.DataType.INT16},
587+
bedUpperLeftY: {ID: 0x0101, type: Zcl.DataType.INT16},
588+
bedLowerRightX: {ID: 0x0102, type: Zcl.DataType.INT16},
589+
bedLowerRightY: {ID: 0x0103, type: Zcl.DataType.INT16},
590+
doorCenterX: {ID: 0x0108, type: Zcl.DataType.INT16},
591+
doorCenterY: {ID: 0x0109, type: Zcl.DataType.INT16},
592+
leftFallDetectionRange: {ID: 0x010c, type: Zcl.DataType.UINT16},
593+
rightFallDetectionRange: {ID: 0x010d, type: Zcl.DataType.UINT16},
594+
frontFallDetectionRange: {ID: 0x010e, type: Zcl.DataType.UINT16},
595+
},
596+
commands: {},
597+
commandsResponse: {},
598+
}),
599+
],
600+
fromZigbee: [fz.identify, fzLocal.owonFds315],
601+
toZigbee: [tzLocal.owonFds315SetFallSettings],
602+
exposes: [
603+
e.enum("status", ea.STATE, ["unoccupied", "occupied", "sitting", "on_the_bed", "low_posture", "falling"]),
604+
e.numeric("breathing_rate", ea.STATE).withUnit("breaths/min").withDescription("Breathing rate."),
605+
e.numeric("location_x", ea.STATE).withUnit("cm").withDescription("X coordinate of human activity."),
606+
e.numeric("location_y", ea.STATE).withUnit("cm").withDescription("Y coordinate of human activity."),
607+
e
608+
.text("fall_detection_settings", ea.ALL)
609+
.withDescription(
610+
"Comma-separated values for bed, door and fall detection settings: bedUpperLeftX, bedUpperLeftY, bedLowerRightX, bedLowerRightY, doorCenterX, doorCenterY, leftFallDetectionRange, rightFallDetectionRange, frontFallDetectionRange. Put -21931 for disabled bed and door.",
611+
),
612+
],
613+
configure: async (device, coordinatorEndpoint) => {
614+
const endpoint = device.getEndpoint(1);
615+
await endpoint.bind("ssIasZone", coordinatorEndpoint);
616+
await endpoint.bind("genBasic", coordinatorEndpoint);
617+
await endpoint.bind("fallDetectionOwon", coordinatorEndpoint);
618+
},
619+
},
482620
];

0 commit comments

Comments
 (0)