Skip to content

Commit 66072f4

Browse files
feat(add): 07504L (#9005)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent efee803 commit 66072f4

File tree

1 file changed

+166
-0
lines changed

1 file changed

+166
-0
lines changed

src/devices/immax.ts

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,119 @@
11
import * as fz from "../converters/fromZigbee";
22
import * as tz from "../converters/toZigbee";
3+
import {repInterval} from "../lib/constants";
34
import * as exposes from "../lib/exposes";
45
import * as legacy from "../lib/legacy";
56
import * as m from "../lib/modernExtend";
67
import * as reporting from "../lib/reporting";
78
import * as tuya from "../lib/tuya";
89
import type {DefinitionWithExtend} from "../lib/types";
10+
import type {Fz, KeyValueAny, Tz} from "../lib/types";
11+
import * as utils from "../lib/utils";
912

1013
const e = exposes.presets;
1114
const ea = exposes.access;
1215

16+
const tzLocal = {
17+
ts0219_duration: {
18+
key: ["duration"],
19+
convertSet: async (entity, key, value, meta) => {
20+
await entity.write("ssIasWd", {maxDuration: value});
21+
},
22+
convertGet: async (entity, key, meta) => {
23+
await entity.read("ssIasWd", ["maxDuration"]);
24+
},
25+
} satisfies Tz.Converter,
26+
ts0219_volume: {
27+
key: ["volume"],
28+
convertSet: async (entity, key, value, meta) => {
29+
utils.assertNumber(value);
30+
await entity.write(
31+
"ssIasWd",
32+
{2: {value: utils.mapNumberRange(value, 0, 100, 100, 0), type: 0x20}},
33+
utils.getOptions(meta.mapped, entity),
34+
);
35+
},
36+
convertGet: async (entity, key, meta) => {
37+
await entity.read("ssIasWd", [0x0002]);
38+
},
39+
} satisfies Tz.Converter,
40+
ts0219_light: {
41+
key: ["light"],
42+
convertSet: async (entity, key, value, meta) => {
43+
await entity.write("ssIasWd", {1: {value: value, type: 0x20}}, utils.getOptions(meta.mapped, entity));
44+
},
45+
convertGet: async (entity, key, meta) => {
46+
await entity.read("ssIasWd", [0x0001]);
47+
},
48+
} satisfies Tz.Converter,
49+
ts0219_alarm: {
50+
key: ["alarm"],
51+
convertGet: async (entity, key, meta) => {
52+
await entity.read("ssIasZone", ["zoneStatus"]);
53+
},
54+
convertSet: async (entity, key, value, meta) => {
55+
const OFF = 0;
56+
const ALARM = 16;
57+
const info = value ? ALARM : OFF;
58+
//only startwarninginfo is used, rest of params are ignored (stored values from device are used instead)
59+
await entity.command(
60+
"ssIasWd",
61+
"startWarning",
62+
{startwarninginfo: info, warningduration: 0, strobedutycycle: 0, strobelevel: 0},
63+
utils.getOptions(meta.mapped, entity),
64+
);
65+
},
66+
} satisfies Tz.Converter,
67+
};
68+
69+
const fzLocal = {
70+
ts0219ssIasWd: {
71+
cluster: "ssIasWd",
72+
type: ["attributeReport", "readResponse"],
73+
convert: (model, msg, publish, options, meta) => {
74+
const result: KeyValueAny = {};
75+
//max duration
76+
if (msg.data.maxDuration !== undefined) {
77+
result.duration = msg.data.maxDuration;
78+
}
79+
if (msg.data["0"] !== undefined) {
80+
result.duration = msg.data["0"];
81+
}
82+
//light
83+
if (msg.data["1"] !== undefined) {
84+
result.light = msg.data["1"];
85+
}
86+
//volume
87+
if (msg.data["2"] !== undefined) {
88+
result.volume = utils.mapNumberRange(msg.data["2"], 100, 0, 0, 100);
89+
}
90+
return result;
91+
},
92+
} satisfies Fz.Converter,
93+
ts0219genBasic: {
94+
cluster: "genBasic",
95+
type: ["attributeReport", "readResponse"],
96+
convert: (model, msg, publish, options, meta) => {
97+
const result: KeyValueAny = {};
98+
if (msg.data.powerSource !== undefined) {
99+
result.power_source = msg.data.powerSource === 2 ? "mains" : "battery";
100+
}
101+
return result;
102+
},
103+
} satisfies Fz.Converter,
104+
ts0219ssIasZone: {
105+
cluster: "ssIasZone",
106+
type: ["attributeReport", "readResponse"],
107+
convert: (model, msg, publish, options, meta) => {
108+
const result: KeyValueAny = {};
109+
if (msg.data.zoneStatus !== undefined) {
110+
result.alarm = msg.data.zoneStatus === 17;
111+
}
112+
return result;
113+
},
114+
} satisfies Fz.Converter,
115+
};
116+
13117
export const definitions: DefinitionWithExtend[] = [
14118
{
15119
fingerprint: tuya.fingerprint("TS011F", ["_TZ3000_jak16dll"]),
@@ -235,6 +339,68 @@ export const definitions: DefinitionWithExtend[] = [
235339
],
236340
extend: [m.illuminance()],
237341
},
342+
{
343+
zigbeeModel: ["TS0219"],
344+
model: "07504L",
345+
vendor: "Immax",
346+
description: "Neo outdoor smart siren (IP65)",
347+
fromZigbee: [fzLocal.ts0219ssIasWd, fz.battery, fzLocal.ts0219genBasic, fzLocal.ts0219ssIasZone],
348+
exposes: [
349+
e.battery(),
350+
e.battery_low(),
351+
e.battery_voltage(),
352+
e.binary("alarm", ea.ALL, true, false),
353+
e
354+
.numeric("volume", ea.ALL)
355+
.withValueMin(0)
356+
.withValueMax(50)
357+
.withDescription("Volume of siren")
358+
.withPreset("off", 0, "off")
359+
.withPreset("low", 5, "low volume")
360+
.withPreset("medium", 25, "medium volume")
361+
.withPreset("high", 50, "high volume"),
362+
e.numeric("duration", ea.ALL).withValueMin(0).withValueMax(3600).withUnit("s").withDescription("Duration of alarm"),
363+
e
364+
.numeric("light", ea.ALL)
365+
.withValueMin(0)
366+
.withValueMax(100)
367+
.withDescription("Strobe light level")
368+
.withPreset("off", 0, "off light")
369+
.withPreset("low", 30, "low light")
370+
.withPreset("medium", 60, "medium light")
371+
.withPreset("high", 100, "high light"),
372+
e.enum("power_source", ea.STATE, ["mains", "battery"]).withDescription("The current power source"),
373+
],
374+
toZigbee: [tzLocal.ts0219_alarm, tzLocal.ts0219_duration, tzLocal.ts0219_volume, tzLocal.ts0219_light, tz.power_source],
375+
meta: {disableDefaultResponse: true},
376+
configure: async (device, coordinatorEndpoint) => {
377+
const endpoint = device.getEndpoint(1);
378+
await reporting.bind(endpoint, coordinatorEndpoint, ["genPowerCfg", "ssIasZone", "ssIasWd"]);
379+
await reporting.batteryVoltage(endpoint);
380+
await reporting.batteryPercentageRemaining(endpoint);
381+
//configure reporting for zoneStatus to update alarm state (when alarm goes off)
382+
await endpoint.configureReporting(
383+
"ssIasZone",
384+
[
385+
{
386+
attribute: "zoneStatus",
387+
minimumReportInterval: 0,
388+
maximumReportInterval: repInterval.MAX,
389+
reportableChange: 1,
390+
},
391+
],
392+
{},
393+
);
394+
395+
await endpoint.read("genBasic", ["powerSource"]);
396+
await endpoint.read("ssIasZone", ["zoneState", "iasCieAddr", "zoneId", "zoneStatus"]);
397+
await endpoint.read("ssIasWd", ["maxDuration", 0x0002, 0x0001]);
398+
},
399+
extend: [
400+
//fix reported as Router
401+
m.forceDeviceType({type: "EndDevice"}),
402+
],
403+
},
238404
{
239405
fingerprint: tuya.fingerprint("TS0601", ["_TZE200_n9clpsht", "_TZE200_nyvavzbj", "_TZE200_moycceze"]),
240406
model: "07505L",

0 commit comments

Comments
 (0)