Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

idea with weekdays programs for tuya-moes thermostats #8009

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
66 changes: 22 additions & 44 deletions src/devices/moes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@ const fzZosung = zosung.fzZosung;
const tzZosung = zosung.tzZosung;
const ez = zosung.presetsZosung;

const exposesLocal = {
hour: (name: string) => e.numeric(name, ea.STATE_SET).withUnit('h').withValueMin(0).withValueMax(23),
minute: (name: string) => e.numeric(name, ea.STATE_SET).withUnit('m').withValueMin(0).withValueMax(59),
program_temperature: (name: string) => e.numeric(name, ea.STATE_SET).withUnit('°C').withValueMin(5).withValueMax(35).withValueStep(0.5),
};
const programText = (name: string) => e.text(name, ea.STATE_SET).withPattern(/\d\d:\d\d\/\d\d°C/, 'Template is: 00:00/00°C');

const definitions: DefinitionWithExtend[] = [
{
Expand Down Expand Up @@ -122,7 +118,7 @@ const definitions: DefinitionWithExtend[] = [
legacy.tz.moes_thermostat_deadzone_temperature,
legacy.tz.moes_thermostat_max_temperature_limit,
legacy.tz.moes_thermostat_min_temperature_limit,
legacy.tz.moes_thermostat_program_schedule,
legacy.tz.moes_thermostat_program_schedule_v2,
Copy link
Owner

Choose a reason for hiding this comment

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

Let's not call it v2 but update the existing one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is just draft but if you like the idea I'll change both moes/moesS thermostats for using this one with proper namings
(made pr as draft)

],
whiteLabel: [tuya.whitelabel('Moes', 'BHT-002/BHT-006', 'Smart heating thermostat', ['_TZE204_aoclfnxz'])],
exposes: (device, options) => {
Expand All @@ -143,44 +139,26 @@ const definitions: DefinitionWithExtend[] = [
.withPreset(['hold', 'program']),
e.temperature_sensor_select(['IN', 'AL', 'OU']),
e
.composite('program', 'program', ea.STATE_SET)
.withDescription('Time of day and setpoint to use when in program mode')
.withFeature(exposesLocal.hour('weekdays_p1_hour'))
.withFeature(exposesLocal.minute('weekdays_p1_minute'))
.withFeature(exposesLocal.program_temperature('weekdays_p1_temperature'))
.withFeature(exposesLocal.hour('weekdays_p2_hour'))
.withFeature(exposesLocal.minute('weekdays_p2_minute'))
.withFeature(exposesLocal.program_temperature('weekdays_p2_temperature'))
.withFeature(exposesLocal.hour('weekdays_p3_hour'))
.withFeature(exposesLocal.minute('weekdays_p3_minute'))
.withFeature(exposesLocal.program_temperature('weekdays_p3_temperature'))
.withFeature(exposesLocal.hour('weekdays_p4_hour'))
.withFeature(exposesLocal.minute('weekdays_p4_minute'))
.withFeature(exposesLocal.program_temperature('weekdays_p4_temperature'))
.withFeature(exposesLocal.hour('saturday_p1_hour'))
.withFeature(exposesLocal.minute('saturday_p1_minute'))
.withFeature(exposesLocal.program_temperature('saturday_p1_temperature'))
.withFeature(exposesLocal.hour('saturday_p2_hour'))
.withFeature(exposesLocal.minute('saturday_p2_minute'))
.withFeature(exposesLocal.program_temperature('saturday_p2_temperature'))
.withFeature(exposesLocal.hour('saturday_p3_hour'))
.withFeature(exposesLocal.minute('saturday_p3_minute'))
.withFeature(exposesLocal.program_temperature('saturday_p3_temperature'))
.withFeature(exposesLocal.hour('saturday_p4_hour'))
.withFeature(exposesLocal.minute('saturday_p4_minute'))
.withFeature(exposesLocal.program_temperature('saturday_p4_temperature'))
.withFeature(exposesLocal.hour('sunday_p1_hour'))
.withFeature(exposesLocal.minute('sunday_p1_minute'))
.withFeature(exposesLocal.program_temperature('sunday_p1_temperature'))
.withFeature(exposesLocal.hour('sunday_p2_hour'))
.withFeature(exposesLocal.minute('sunday_p2_minute'))
.withFeature(exposesLocal.program_temperature('sunday_p2_temperature'))
.withFeature(exposesLocal.hour('sunday_p3_hour'))
.withFeature(exposesLocal.minute('sunday_p3_minute'))
.withFeature(exposesLocal.program_temperature('sunday_p3_temperature'))
.withFeature(exposesLocal.hour('sunday_p4_hour'))
.withFeature(exposesLocal.minute('sunday_p4_minute'))
.withFeature(exposesLocal.program_temperature('sunday_p4_temperature')),
.composite('program_v2', 'program_v2', ea.STATE_SET)
.withDescription(
'PROGRAMMING MODE ⏱ - In this mode, ' +
'the device executes a preset week programming temperature time and temperature. ' +
'You can set up to 4 stages of temperature every for WEEKDAY ➀➁➂➃➄, SATURDAY ➅ and SUNDAY ➆.',
)
.withFeature(programText('weekdays_p1'))
.withFeature(programText('weekdays_p2'))
.withFeature(programText('weekdays_p3'))
.withFeature(programText('weekdays_p4'))

.withFeature(programText('saturday_p1'))
.withFeature(programText('saturday_p2'))
.withFeature(programText('saturday_p3'))
.withFeature(programText('saturday_p4'))

.withFeature(programText('sunday_p1'))
.withFeature(programText('sunday_p2'))
.withFeature(programText('sunday_p3'))
.withFeature(programText('sunday_p4')),
];
},
onEvent: tuya.onEventSetLocalTime,
Expand Down
23 changes: 19 additions & 4 deletions src/lib/exposes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {Access, Range} from './types';
import {getLabelFromName} from './utils';

export type Feature = Numeric | Binary | Enum | Composite | List | Text;
export type FeatureList = Numeric | Binary | Composite | Text;

export class Base {
name: string;
Expand Down Expand Up @@ -196,11 +197,11 @@ export class Binary extends Base {

export class List extends Base {
property: string = '';
item_type: Numeric | Binary | Composite | Text;
item_type: FeatureList;
length_min?: number;
length_max?: number;

constructor(name: string, access: number, itemType: Numeric | Binary | Composite | Text) {
constructor(name: string, access: number, itemType: FeatureList) {
super();
this.type = 'list';
this.name = name;
Expand Down Expand Up @@ -310,6 +311,8 @@ export class Enum extends Base {

export class Text extends Base {
property: string = '';
pattern?: RegExp;
patternComment?: string;

constructor(name: string, access: number) {
super();
Expand All @@ -320,6 +323,18 @@ export class Text extends Base {
this.access = access;
}

withPattern(pattern: RegExp, comment: string): Text {
this.pattern = pattern;
this.patternComment = comment;
return this;
}

checkPatternMatch() {
if (!this.pattern.test(this.property)) {
throw new Error('String format pattern check failed:' + this.label + '\n' + this.patternComment);
}
}

clone(): Text {
const clone = new Text(this.name, this.access);
this.copy(clone);
Expand Down Expand Up @@ -887,7 +902,7 @@ export const presets = {
light: () => new Light(),
numeric: (name: string, access: number) => new Numeric(name, access),
text: (name: string, access: number) => new Text(name, access),
list: (name: string, access: number, itemType: Feature) => new List(name, access, itemType),
list: (name: string, access: number, itemType: FeatureList) => new List(name, access, itemType),
switch_: () => new Switch(),
// Specific
ac_frequency: () =>
Expand Down Expand Up @@ -1362,5 +1377,5 @@ exports.light = () => new Light();
exports.numeric = (name: string, access: number) => new Numeric(name, access);
exports.switch = () => new Switch();
exports.text = (name: string, access: number) => new Text(name, access);
exports.list = (name: string, access: number, itemType: Feature) => new List(name, access, itemType);
exports.list = (name: string, access: number, itemType: FeatureList) => new List(name, access, itemType);
exports.lock = () => new Lock();
46 changes: 46 additions & 0 deletions src/lib/legacy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6531,6 +6531,52 @@ const toZigbee2 = {
await sendDataPointRaw(entity, dataPoints.moesSchedule, payload);
},
} satisfies Tz.Converter,
moes_thermostat_program_schedule_v2: {
key: ['program_v2'],
convertSet: async (entity, key, value: any, meta) => {
if (!meta.state.program) {
logger.warning(`Existing program state not set.`, 'zhc:legacy:tz:moes_bht_002');
return;
}

/* Merge modified value into existing state and send all over in one go */
const newProgram = {
// @ts-expect-error ignore
...meta.state.program,
...value,
};

const getNumbers = (input: exposes.Text): number[] => {
input.checkPatternMatch();
Copy link
Owner

Choose a reason for hiding this comment

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

I don't understand what this does, how can input be exposes.Text here? Isn't it a string?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

aww
Sure. I've just started to dive into it and missed some core concepts. I've thought there is component and not clean result.

But maybe you'll suggest where can I make this check? I wanted to try to incapsulate all info inside component but not sure where this information can be used. As I see a lot of this stuff doing in z2m-frontend?

Copy link
Owner

Choose a reason for hiding this comment

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

I would you do a simple regex check here instead of input.checkPatternMatch();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can but main idea was to improve something and provide kind of better incapsulated solution


const hourTemperature = input.property.split('/');
const hourMinute = hourTemperature[0].split(':', 2);
const clamp = (value: number, min: number, max: number): number => {
return Math.max(min, Math.min(max, value));
};

return [clamp(parseInt(hourMinute[0]), 0, 23), clamp(parseInt(hourMinute[1]), 0, 59), clamp(parseInt(hourTemperature[1]) * 2, 5, 35)];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think here is not needed exception. Clamp is more than enough.

};

const payload = getNumbers(newProgram.weekdays_p1)
.concat(getNumbers(newProgram.weekdays_p2))
.concat(getNumbers(newProgram.weekdays_p2))
.concat(getNumbers(newProgram.weekdays_p3))

.concat(getNumbers(newProgram.weekdays_p4))
.concat(getNumbers(newProgram.saturday_p1))
.concat(getNumbers(newProgram.saturday_p2))
.concat(getNumbers(newProgram.saturday_p3))
.concat(getNumbers(newProgram.saturday_p4))

.concat(getNumbers(newProgram.sunday_p1))
.concat(getNumbers(newProgram.sunday_p2))
.concat(getNumbers(newProgram.sunday_p3))
.concat(getNumbers(newProgram.sunday_p4));

await sendDataPointRaw(entity, dataPoints.moesSchedule, payload);
},
} satisfies Tz.Converter,
moesS_thermostat_system_mode: {
key: ['system_mode'],
convertSet: async (entity, key, value, meta) => {
Expand Down
Loading