Skip to content

Commit 6f462fa

Browse files
committed
Add Sunricher SR-2421-Z2D8C support
1 parent bb0e118 commit 6f462fa

1 file changed

Lines changed: 113 additions & 1 deletion

File tree

src/devices/sunricher.ts

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import * as namron from "../lib/namron";
1212
import * as reporting from "../lib/reporting";
1313
import {payload} from "../lib/reporting";
1414
import * as sunricher from "../lib/sunricher";
15-
import type {DefinitionWithExtend, Fz, KeyValue, ModernExtend, Tz, Zh} from "../lib/types";
15+
import type {DefinitionWithExtend, DummyDevice, Expose, Fz, KeyValue, ModernExtend, Tz, Zh} from "../lib/types";
1616
import * as utils from "../lib/utils";
1717
import {addActionGroup, hasAlreadyProcessedMessage, postfixWithEndpointName} from "../lib/utils";
1818

@@ -21,6 +21,16 @@ const e = exposes.presets;
2121
const ea = exposes.access;
2222

2323
const sunricherManufacturerCode = 0x1224;
24+
const sunricherDaliEndpointIds = [1, 2, 3, 4, 5, 6, 7, 8] as const;
25+
const sunricherDaliEndpointNames = ["l1", "l2", "l3", "l4", "l5", "l6", "l7", "l8"];
26+
const sunricherDaliDimmableLightDeviceId = 0x0101;
27+
const sunricherDaliColorTempLightDeviceId = 0x010c;
28+
const sunricherDaliColorTempRange: [number, number] = [150, 500];
29+
30+
interface SunricherDaliEndpointInfo {
31+
id: number;
32+
deviceID?: number;
33+
}
2434

2535
export interface SunricherHvacThermostat {
2636
attributes: {
@@ -257,6 +267,93 @@ async function syncTimeWithTimeZone(endpoint: Zh.Endpoint) {
257267
}
258268
}
259269

270+
function sunricherDaliIsEnabledEndpoint(endpoint: Zh.Endpoint) {
271+
return (
272+
sunricherDaliEndpointIds.includes(endpoint.ID as (typeof sunricherDaliEndpointIds)[number]) &&
273+
[sunricherDaliDimmableLightDeviceId, sunricherDaliColorTempLightDeviceId].includes(endpoint.deviceID)
274+
);
275+
}
276+
277+
function sunricherDaliIsColorTempEndpoint(endpoint: {deviceID?: number}) {
278+
return endpoint.deviceID === sunricherDaliColorTempLightDeviceId;
279+
}
280+
281+
function sunricherDaliEndpointName(endpointID: number) {
282+
return `l${endpointID}`;
283+
}
284+
285+
function sunricherDaliEnabledEndpoints(device: Zh.Device) {
286+
return device.endpoints.filter(sunricherDaliIsEnabledEndpoint);
287+
}
288+
289+
function sunricherDaliEndpointInfos(device: Zh.Device | DummyDevice): SunricherDaliEndpointInfo[] {
290+
if ("isDummyDevice" in device) {
291+
return sunricherDaliEndpointIds.map((id) => ({id, deviceID: sunricherDaliColorTempLightDeviceId}));
292+
}
293+
294+
return sunricherDaliEnabledEndpoints(device).map((endpoint) => ({id: endpoint.ID, deviceID: endpoint.deviceID}));
295+
}
296+
297+
function sunricherDaliEndpoint(device: Zh.Device) {
298+
return Object.fromEntries(sunricherDaliEnabledEndpoints(device).map((endpoint) => [sunricherDaliEndpointName(endpoint.ID), endpoint.ID]));
299+
}
300+
301+
function sunricherDaliExposes(device: Zh.Device | DummyDevice): Expose[] {
302+
return sunricherDaliEndpointInfos(device).map((endpoint) => {
303+
const expose = e.light().withBrightness();
304+
305+
if (sunricherDaliIsColorTempEndpoint(endpoint)) {
306+
expose.withColorTemp(sunricherDaliColorTempRange);
307+
}
308+
309+
return expose.withEndpoint(sunricherDaliEndpointName(endpoint.id));
310+
});
311+
}
312+
313+
async function sunricherDaliReadInitialState(endpoint: Zh.Endpoint) {
314+
await endpoint.read("genOnOff", ["onOff"]);
315+
await endpoint.read("genLevelCtrl", ["currentLevel"]);
316+
317+
if (sunricherDaliIsColorTempEndpoint(endpoint)) {
318+
await endpoint.read("lightingColorCtrl", ["colorMode", "colorTemperature"]);
319+
}
320+
}
321+
322+
function sunricherDaliController(): ModernExtend {
323+
return {
324+
fromZigbee: [fz.on_off, fz.brightness, fz.level_config, fz.color_colortemp],
325+
toZigbee: [
326+
{...tz.light_onoff_brightness, endpoints: sunricherDaliEndpointNames},
327+
tz.ignore_transition,
328+
tz.level_config,
329+
tz.ignore_rate,
330+
tz.light_brightness_move,
331+
tz.light_brightness_step,
332+
tz.light_colortemp,
333+
tz.light_colortemp_move,
334+
tz.light_colortemp_step,
335+
tz.light_color_mode,
336+
tz.light_color_options,
337+
],
338+
exposes: [sunricherDaliExposes],
339+
endpoint: sunricherDaliEndpoint,
340+
meta: {multiEndpoint: true},
341+
configure: [
342+
async (device, coordinatorEndpoint) => {
343+
for (const endpoint of sunricherDaliEnabledEndpoints(device)) {
344+
const clusters = sunricherDaliIsColorTempEndpoint(endpoint)
345+
? ["genOnOff", "genLevelCtrl", "lightingColorCtrl"]
346+
: ["genOnOff", "genLevelCtrl"];
347+
348+
await reporting.bind(endpoint, coordinatorEndpoint, clusters);
349+
await sunricherDaliReadInitialState(endpoint);
350+
}
351+
},
352+
],
353+
isModernExtend: true,
354+
};
355+
}
356+
260357
export const definitions: DefinitionWithExtend[] = [
261358
{
262359
zigbeeModel: ["HK-DIM-PIR"],
@@ -1964,6 +2061,21 @@ export const definitions: DefinitionWithExtend[] = [
19642061
description: "Zigbee micro smart dimmer",
19652062
extend: [m.light({configureReporting: true}), m.electricityMeter(), sunricher.extend.externalSwitchType(), sunricher.extend.minimumPWM()],
19662063
},
2064+
{
2065+
fingerprint: [
2066+
{
2067+
type: "Router",
2068+
modelID: "Light",
2069+
manufacturerID: sunricherManufacturerCode,
2070+
manufacturerName: "Sunricher",
2071+
priority: 1,
2072+
},
2073+
],
2074+
model: "SR-2421-Z2D8C",
2075+
vendor: "Sunricher",
2076+
description: "Zigbee to DALI controller",
2077+
extend: [sunricherDaliController()],
2078+
},
19672079
{
19682080
zigbeeModel: ["HK-ZD-DIM-A"],
19692081
model: "SRP-ZG9105-CC",

0 commit comments

Comments
 (0)