Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/devices/lumi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4339,6 +4339,39 @@ export const definitions: DefinitionWithExtend[] = [
}),
],
},
{
zigbeeModel: ["lumi.plug.aeu002"],
model: "WP-P09D",
vendor: "Aqara",
description: "Wall Outlet H2 UK",
extend: [
m.deviceEndpoints({endpoints: {1: 1, 2: 2, usb: 3}}),
m.forcePowerSource({powerSource: "Mains (single phase)"}),
lumiZigbeeOTA(),
lumiOnOff({
endpointNames: ["1", "2", "usb"],
powerOutageMemory: "enum",
deviceTemperature: false,
}),
// Power reporting for each socket is at a different endpoint to the socket switch state
lumi.lumiModernExtend.lumiActivePower({name: "total_power", description: "Total combined outlet power consumption", endpoint: 1}),
lumi.lumiModernExtend.lumiActivePower({name: "power_socket_1_and_usb", description: "Combined power of socket 1 and USB", endpoint: 2}),
lumi.lumiModernExtend.lumiActivePower({name: "power_socket_2", description: "Power of socket 2", endpoint: 3}),
lumiElectricityMeter({voltage: false}),
lumiMultiClick({description: "Multi-click mode for socket 1 button", endpointName: "1"}),
lumiMultiClick({description: "Multi-click mode for socket 2 button", endpointName: "2"}),
lumiAction({
endpointNames: ["1", "2"],
actionLookup: {hold: 0, single: 1, double: 2, release: 255},
}),
lumi.lumiModernExtend.lumiChildLock({endpointName: "1", description: "Socket 1 button lock"}),
lumi.lumiModernExtend.lumiChildLock({endpointName: "2", description: "Socket 2 button lock"}),
lumiOverloadProtection({valueMax: 3250}),
lumiLedIndicator(),
lumiFlipIndicatorLight(),
m.identify(),
],
},
{
zigbeeModel: ["lumi.light.acn032", "lumi.light.acn031"],
model: "CL-L02D",
Expand Down
84 changes: 79 additions & 5 deletions src/lib/lumi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2099,11 +2099,34 @@ export const lumiModernExtend = {
],
};
},
lumiOnOff: (args?: modernExtend.OnOffArgs & {operationMode?: boolean; powerOutageMemory?: "binary" | "enum"; lockRelay?: boolean}) => {
args = {operationMode: false, lockRelay: false, ...args};
lumiOnOff: (
args?: modernExtend.OnOffArgs & {
operationMode?: boolean;
powerOutageMemory?: "binary" | "enum";
lockRelay?: boolean;
deviceTemperature?: boolean;
powerOutageCount?: boolean;
},
) => {
args = {
operationMode: false,
lockRelay: false,
deviceTemperature: true,
powerOutageCount: true,
...args,
};

const result = modernExtend.onOff({powerOnBehavior: false, ...args});

result.fromZigbee.push(fromZigbee.lumi_specific);
result.exposes.push(e.device_temperature(), e.power_outage_count());

if (args.deviceTemperature) {
result.exposes.push(e.device_temperature());
}
if (args.powerOutageCount) {
result.exposes.push(e.power_outage_count());
}

if (args.powerOutageMemory === "binary") {
const extend = lumiModernExtend.lumiPowerOutageMemory();
result.toZigbee.push(...extend.toZigbee);
Expand Down Expand Up @@ -2493,8 +2516,46 @@ export const lumiModernExtend = {
zigbeeCommandOptions: {manufacturerCode},
...args,
}),
lumiElectricityMeter: (): ModernExtend => {
const exposes = [e.energy(), e.voltage(), e.current()];
lumiActivePower: (args: {name: string; description: string; endpoint: number}): ModernExtend => {
const {name, description, endpoint} = args;

const fromZigbee = [
{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use fz.electrical_measurement here?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@absent42 not sure if you read this, but I meant: const fromZigbee = [fz.electrical_measurement];

cluster: fz.electrical_measurement.cluster,
type: fz.electrical_measurement.type,
convert: (model, msg, publish, options, meta) => {
if (msg.endpoint.ID !== endpoint) return;
if (!("activePower" in msg.data)) return;

const power = msg.data.activePower;
if (typeof power !== "number") return;

return {[name]: power};
},
} satisfies Fz.Converter<"haElectricalMeasurement", undefined, ["attributeReport", "readResponse"]>,
];

const exposes = [e.numeric(name, ea.STATE).withUnit("W").withDescription(description)];

return {
isModernExtend: true,
fromZigbee,
exposes,
};
},
lumiElectricityMeter: (args?: {energy?: boolean; voltage?: boolean; current?: boolean}): ModernExtend => {
const options = {
energy: true,
voltage: true,
current: true,
...args,
};

const exposes = [];
if (options.energy) exposes.push(e.energy());
if (options.voltage) exposes.push(e.voltage());
if (options.current) exposes.push(e.current());

const fromZigbee = [
{
cluster: "manuSpecificLumi",
Expand All @@ -2516,6 +2577,19 @@ export const lumiModernExtend = {
valueMin: 100,
valueMax: 3840,
unit: "W",
access: "STATE_SET",
entityCategory: "config",
zigbeeCommandOptions: {manufacturerCode},
...args,
}),
lumiChildLock: (args?: Partial<modernExtend.BinaryArgs<"manuSpecificLumi">>) =>
modernExtend.binary({
name: "child_lock",
cluster: "manuSpecificLumi",
attribute: {ID: 0x0285, type: 0x20},
valueOn: [true, 1],
valueOff: [false, 0],
description: "Child lock (disables physical button)",
access: "ALL",
entityCategory: "config",
zigbeeCommandOptions: {manufacturerCode},
Expand Down