Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
14c84bb
Add inching configuration to Tuya device
ohadvano May 4, 2026
8262a25
[autofix.ci] apply automated fixes
autofix-ci[bot] May 4, 2026
fed2e9f
Refactor comment in tuya.ts for clarity
ohadvano May 4, 2026
2f9d8d6
Refactor inching feature to use minutes and seconds
ohadvano May 4, 2026
0a0fab9
[autofix.ci] apply automated fixes
autofix-ci[bot] May 4, 2026
71e6364
Refactor value handling in inching state conversion
ohadvano May 4, 2026
e1ae0bc
Refactor value type handling in Tuya device logic
ohadvano May 4, 2026
1561b79
[autofix.ci] apply automated fixes
autofix-ci[bot] May 4, 2026
8d7a750
Increase maximum delay minutes from 1092 to 1440
ohadvano May 4, 2026
b35cff2
Add off_color feature to Tuya device configuration
ohadvano May 4, 2026
95565ef
Add indicator_mode to Tuya device configuration
ohadvano May 4, 2026
896750a
Add inchingSwitch2 feature with state and delay
ohadvano May 5, 2026
f341464
Remove inchingSwitch2 feature from tuya
ohadvano May 5, 2026
42dd598
Remove inching configuration from Tuya device
ohadvano May 5, 2026
1e51bbb
Add inchingSwitch2 configuration feature
ohadvano May 5, 2026
15abfbb
Add inchingSwitch2 to Tuya device exposes
ohadvano May 5, 2026
6e85078
Update tuya.ts
ohadvano May 5, 2026
2e342de
Change type of unknown properties to 'unknown'
ohadvano May 5, 2026
bff6028
[autofix.ci] apply automated fixes
autofix-ci[bot] May 5, 2026
2460974
Clean up comments in inchingSwitch2 methods
ohadvano May 5, 2026
0a886c2
Refactor indicator_mode exposure to use tuya method
ohadvano May 6, 2026
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
18 changes: 15 additions & 3 deletions src/devices/tuya.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26632,9 +26632,11 @@ export const definitions: DefinitionWithExtend[] = [
e.switch().setAccess("state", ea.STATE_SET),
e.power_on_behavior().withAccess(ea.STATE_SET),
e.numeric("countdown", ea.STATE_SET).withUnit("s").withValueMin(0).withValueMax(86400).withDescription("Countdown to turn off"),
tuya.exposes.indicatorModeNoneRelayPos(),
e.binary("backlight_mode", ea.STATE_SET, "ON", "OFF").withDescription("Backlight"),
e.enum("off_color", ea.STATE_SET, ["red", "blue", "green", "white", "yellow", "magenta", "cyan"]).withDescription("OFF Color"),
tuya.exposes.inchingSwitch2(),
e.enum("on_color", ea.STATE_SET, ["red", "blue", "green", "white", "yellow", "magenta", "cyan"]).withDescription("ON Color"),
e.enum("off_color", ea.STATE_SET, ["red", "blue", "green", "white", "yellow", "magenta", "cyan"]).withDescription("OFF Color"),
e.numeric("backlight_brightness", ea.STATE_SET).withValueMin(0).withValueMax(100).withDescription("Backlight Brightness"),
e.binary("child_lock", ea.STATE_SET, "ON", "OFF").withDescription("Child Lock"),
],
Expand All @@ -26643,12 +26645,22 @@ export const definitions: DefinitionWithExtend[] = [
[1, "state", tuya.valueConverter.onOff],
[7, "countdown", tuya.valueConverter.countdown],
[14, "power_on_behavior", tuya.valueConverter.powerOnBehaviorEnum],
[
15,
"indicator_mode",
tuya.valueConverterBasic.lookup({
none: tuya.enum(0),
relay: tuya.enum(1),
pos: tuya.enum(2),
}),
],
[16, "backlight_mode", tuya.valueConverter.onOff],
[19, "inching", tuya.valueConverter.inchingSwitch2],
[101, "child_lock", tuya.valueConverter.onOff],
[102, "backlight_brightness", tuya.valueConverter.raw],
[
103,
"off_color",
"on_color",
tuya.valueConverterBasic.lookup({
red: tuya.enum(0),
blue: tuya.enum(1),
Expand All @@ -26661,7 +26673,7 @@ export const definitions: DefinitionWithExtend[] = [
],
[
104,
"on_color",
"off_color",
tuya.valueConverterBasic.lookup({
red: tuya.enum(0),
blue: tuya.enum(1),
Expand Down
54 changes: 54 additions & 0 deletions src/lib/tuya.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,23 @@ interface Tuya4 {
commandResponses: never;
}

export interface InchingInput {
state?: string;
minutes?: number;
seconds?: number;
}

export interface MetaState {
state: {
inching?: {
state: string;
minutes: number;
seconds: number;
};
[key: string]: unknown;
};
}

export const dataTypes = {
raw: 0, // [ bytes ]
bool: 1, // [0/1]
Expand Down Expand Up @@ -374,6 +391,8 @@ export type ThermostatSchedule = KeyValue & {
};
};

export type FromValue = string | number[] | Uint8Array;

export function convertBufferToNumber(chunks: Buffer | number[]) {
let value = 0;
for (let i = 0; i < chunks.length; i++) {
Expand Down Expand Up @@ -720,6 +739,13 @@ const tuyaExposes = {
}
return x;
},
inchingSwitch2: () =>
e
.composite("inching", "inching", ea.STATE_SET)
.withDescription("Inching (auto delay shut down) configuration")
.withFeature(e.binary("state", ea.STATE_SET, "ON", "OFF").withDescription("Enable/disable inching"))
.withFeature(e.numeric("minutes", ea.STATE_SET).withUnit("m").withValueMin(0).withValueMax(1440).withDescription("Delay minutes"))
.withFeature(e.numeric("seconds", ea.STATE_SET).withUnit("s").withValueMin(0).withValueMax(59).withDescription("Delay seconds")),
};

export {tuyaExposes as exposes};
Expand Down Expand Up @@ -2036,6 +2062,34 @@ export const valueConverter = {
return Buffer.from(s, "hex").swap16().toString("utf16le").trim();
},
},
inchingSwitch2: {
to: (value: InchingInput, meta: MetaState) => {
const currentState = meta.state.inching || {state: "OFF", minutes: 1, seconds: 0};

const state = value.state !== undefined ? value.state : currentState.state;
const minutes = value.minutes !== undefined ? value.minutes : currentState.minutes;
const seconds = value.seconds !== undefined ? value.seconds : currentState.seconds;

let totalSeconds = Math.max(1, minutes * 60 + seconds);
if (totalSeconds > 65535) totalSeconds = 65535;

const buf = Buffer.alloc(3);
buf.writeUInt8(state === "ON" ? 1 : 0, 0);
buf.writeUInt16BE(totalSeconds, 1);

return buf.toString("base64");
},
from: (value: FromValue) => {
const buf = typeof value === "string" ? Buffer.from(value, "base64") : Buffer.from(value);
const state = buf.readUInt8(0) === 1 ? "ON" : "OFF";
const totalSeconds = buf.readUInt16BE(1);

const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;

return {state, minutes, seconds};
},
},
};

const tuyaTz = {
Expand Down