iobroker.sun2000 0.1.2-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/admin/i18n/de/translations.json +10 -0
- package/admin/i18n/en/translations.json +7 -0
- package/admin/i18n/es/translations.json +12 -0
- package/admin/i18n/fr/translations.json +12 -0
- package/admin/i18n/it/translations.json +12 -0
- package/admin/i18n/nl/translations.json +12 -0
- package/admin/i18n/pl/translations.json +12 -0
- package/admin/i18n/pt/translations.json +12 -0
- package/admin/i18n/ru/translations.json +12 -0
- package/admin/i18n/uk/translations.json +12 -0
- package/admin/i18n/zh-cn/translations.json +12 -0
- package/admin/jsonConfig.json +33 -0
- package/admin/sun2000.png +0 -0
- package/io-package.json +206 -0
- package/lib/adapter-config.d.ts +19 -0
- package/lib/modbus_connect.js +145 -0
- package/lib/register.js +761 -0
- package/lib/types.js +119 -0
- package/main.js +405 -0
- package/package.json +78 -0
package/lib/types.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const modbusErrorMessages =
|
|
2
|
+
[ 'Unknown error',
|
|
3
|
+
'Illegal function (device does not support this read/write function)',
|
|
4
|
+
'Illegal data address (register not supported by device)',
|
|
5
|
+
'Illegal data value (value cannot be written to this register)',
|
|
6
|
+
'Slave device failure (device reports internal error)',
|
|
7
|
+
'Acknowledge (requested data will be available later)',
|
|
8
|
+
'Slave device busy (retry request again later)'
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
const batteryStatus = [
|
|
12
|
+
'OFFLINE',
|
|
13
|
+
'STANDBY',
|
|
14
|
+
'RUNNING',
|
|
15
|
+
'FAULT',
|
|
16
|
+
'SLEEP_MODE'
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const dataRefreshRate = {
|
|
20
|
+
low : 'low',
|
|
21
|
+
high : 'high',
|
|
22
|
+
compare (refresh,fieldRefresh) {
|
|
23
|
+
if (!fieldRefresh && refresh !== this.high ) return true;
|
|
24
|
+
return (fieldRefresh === refresh );
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const deviceType = {
|
|
29
|
+
inverter : Symbol('inverter'),
|
|
30
|
+
meter : Symbol('meter'),
|
|
31
|
+
battery : Symbol('battery')
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
//export enum ModbusDatatype {
|
|
35
|
+
const dataType = {
|
|
36
|
+
//enums using Sympls
|
|
37
|
+
int16: Symbol('int16'),
|
|
38
|
+
int32: Symbol('int32'),
|
|
39
|
+
string: Symbol('string'),
|
|
40
|
+
uint16: Symbol('uint16'),
|
|
41
|
+
uint32: Symbol('uint32'),
|
|
42
|
+
|
|
43
|
+
size(type) {
|
|
44
|
+
switch (type) {
|
|
45
|
+
case this.int16:
|
|
46
|
+
return 1;
|
|
47
|
+
case this.int32:
|
|
48
|
+
return 2;
|
|
49
|
+
case this.uint16:
|
|
50
|
+
return 1;
|
|
51
|
+
case this.uint32:
|
|
52
|
+
return 2;
|
|
53
|
+
case this.string:
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
convert(array,type) {
|
|
59
|
+
switch (type) {
|
|
60
|
+
case this.int16:
|
|
61
|
+
return this.readSignedInt16(array);
|
|
62
|
+
case this.int32:
|
|
63
|
+
return this.readSignedInt32(array);
|
|
64
|
+
case this.uint16:
|
|
65
|
+
return this.readUnsignedInt16(array);
|
|
66
|
+
case this.uint32:
|
|
67
|
+
return this.readUnsignedInt32(array);
|
|
68
|
+
case this.string:
|
|
69
|
+
return this.readStr(array,array.length);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
// some helper functions
|
|
73
|
+
readUnsignedInt16(array)
|
|
74
|
+
{
|
|
75
|
+
return array[0];
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
readUnsignedInt32(array)
|
|
80
|
+
{
|
|
81
|
+
return array[0] * 256 * 256 + array[1];
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
readSignedInt16(array)
|
|
85
|
+
{
|
|
86
|
+
let value = 0;
|
|
87
|
+
if (array[0] > 32767) value = array[0] - 65535;
|
|
88
|
+
else value = array[0];
|
|
89
|
+
return value;
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
readSignedInt32(array)
|
|
93
|
+
{ let value = 0;
|
|
94
|
+
for (let i = 0; i < 2; i++) { value = (value << 16) | array[i]; }
|
|
95
|
+
return value;
|
|
96
|
+
},
|
|
97
|
+
readStr(array, length) {
|
|
98
|
+
const bytearray = [];
|
|
99
|
+
console.log(array);
|
|
100
|
+
for(let i = 0; i < length; i++)
|
|
101
|
+
{
|
|
102
|
+
if (array[i] > 0 ) {
|
|
103
|
+
bytearray.push(array[i] >> 8); //right shift
|
|
104
|
+
bytearray.push(array[i] & 0xff);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//const value = String.fromCharCode.apply(null, bytearray);
|
|
108
|
+
const value = String.fromCharCode(...bytearray);
|
|
109
|
+
return new String(value).trim();
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
module.exports = {
|
|
114
|
+
modbusErrorMessages,
|
|
115
|
+
batteryStatus,
|
|
116
|
+
dataRefreshRate,
|
|
117
|
+
deviceType,
|
|
118
|
+
dataType
|
|
119
|
+
};
|
package/main.js
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Created with @iobroker/create-adapter v2.5.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// The adapter-core module gives you access to the core ioBroker functions
|
|
8
|
+
// you need to create an adapter
|
|
9
|
+
const utils = require('@iobroker/adapter-core');
|
|
10
|
+
|
|
11
|
+
const Registers = require(__dirname + '/lib/register.js');
|
|
12
|
+
const ModbusConnect = require(__dirname + '/lib/modbus_connect.js');
|
|
13
|
+
const {dataRefreshRate} = require(__dirname + '/lib/types.js');
|
|
14
|
+
|
|
15
|
+
// Load your modules here, e.g.:
|
|
16
|
+
|
|
17
|
+
class Sun2000 extends utils.Adapter {
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {Partial<utils.AdapterOptions>} [options={}]
|
|
21
|
+
*/
|
|
22
|
+
constructor(options) {
|
|
23
|
+
super({
|
|
24
|
+
...options,
|
|
25
|
+
name: 'sun2000',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
this.lastTimeUpdated = 0;
|
|
29
|
+
this.lastStateUpdated = 0;
|
|
30
|
+
this.isConnected = false;
|
|
31
|
+
this.inverters = [];
|
|
32
|
+
this.settings = {
|
|
33
|
+
intervall : 30000,
|
|
34
|
+
address : '',
|
|
35
|
+
port : 520,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
this.on('ready', this.onReady.bind(this));
|
|
39
|
+
this.on('stateChange', this.onStateChange.bind(this));
|
|
40
|
+
// this.on('objectChange', this.onObjectChange.bind(this));
|
|
41
|
+
// this.on('message', this.onMessage.bind(this));
|
|
42
|
+
this.on('unload', this.onUnload.bind(this));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getInverterInfo(id) {
|
|
46
|
+
/*
|
|
47
|
+
const inverter = this.inverters.find((item) => item.modbusId == id);
|
|
48
|
+
return inverter;
|
|
49
|
+
*/
|
|
50
|
+
return this.inverters[id];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async initPath() {
|
|
54
|
+
this.log.debug('InitPath START....');
|
|
55
|
+
await this.extendObjectAsync('info', {
|
|
56
|
+
type: 'channel',
|
|
57
|
+
common: {
|
|
58
|
+
name: 'info',
|
|
59
|
+
role: 'info'
|
|
60
|
+
},
|
|
61
|
+
native: {}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await this.extendObjectAsync('info.connection', {
|
|
65
|
+
type: 'state',
|
|
66
|
+
common: {
|
|
67
|
+
name: 'Inverter connected',
|
|
68
|
+
type: 'boolean',
|
|
69
|
+
role: 'indicator.connected',
|
|
70
|
+
read: true,
|
|
71
|
+
write: false,
|
|
72
|
+
desc: 'Is the inverter connected?'
|
|
73
|
+
},
|
|
74
|
+
native: {},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
await this.extendObjectAsync('meter', {
|
|
78
|
+
type: 'device',
|
|
79
|
+
common: {
|
|
80
|
+
name: 'meter',
|
|
81
|
+
role: 'info'
|
|
82
|
+
},
|
|
83
|
+
native: {}
|
|
84
|
+
});
|
|
85
|
+
await this.extendObjectAsync('collected', {
|
|
86
|
+
type: 'channel',
|
|
87
|
+
common: {
|
|
88
|
+
name: 'collected',
|
|
89
|
+
role: 'info'
|
|
90
|
+
},
|
|
91
|
+
native: {}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
await this.extendObjectAsync('inverter', {
|
|
95
|
+
type: 'device',
|
|
96
|
+
common: {
|
|
97
|
+
name: 'meter',
|
|
98
|
+
role: 'info'
|
|
99
|
+
},
|
|
100
|
+
native: {}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
//ES6 use a for (const [index, item] of array.entries()) of loop
|
|
104
|
+
//for (const [i, item] of this.conf.entries()) {
|
|
105
|
+
for (const [i, item] of this.inverters.entries()) {
|
|
106
|
+
const path = 'inverter.'+String(i);
|
|
107
|
+
item.path = path;
|
|
108
|
+
await this.extendObjectAsync(path, {
|
|
109
|
+
type: 'channel',
|
|
110
|
+
common: {
|
|
111
|
+
name: 'modbus'+i,
|
|
112
|
+
role: 'indicator'
|
|
113
|
+
},
|
|
114
|
+
native: {}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
await this.extendObjectAsync(path+'.grid', {
|
|
118
|
+
type: 'channel',
|
|
119
|
+
common: {
|
|
120
|
+
name: 'grid',
|
|
121
|
+
role: 'info'
|
|
122
|
+
},
|
|
123
|
+
native: {}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
await this.extendObjectAsync(path+'.battery', {
|
|
127
|
+
type: 'channel',
|
|
128
|
+
common: {
|
|
129
|
+
name: 'battery',
|
|
130
|
+
role: 'info'
|
|
131
|
+
},
|
|
132
|
+
native: {}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
await this.extendObjectAsync(path+'.derived', {
|
|
136
|
+
type: 'channel',
|
|
137
|
+
common: {
|
|
138
|
+
name: 'derived',
|
|
139
|
+
role: 'indicator'
|
|
140
|
+
},
|
|
141
|
+
native: {}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
}
|
|
145
|
+
this.log.debug('InitPath STOP');
|
|
146
|
+
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async InitProcess() {
|
|
150
|
+
this.log.debug('InitProzess START');
|
|
151
|
+
this.state = new Registers(this);
|
|
152
|
+
this.modbusClient = new ModbusConnect(this,this.config.address,this.config.port);
|
|
153
|
+
try {
|
|
154
|
+
await this.initPath();
|
|
155
|
+
/*
|
|
156
|
+
await this.checkAndPrepare();
|
|
157
|
+
await processBatterie();
|
|
158
|
+
*/
|
|
159
|
+
} catch (err) {
|
|
160
|
+
this.log.warn(err);
|
|
161
|
+
}
|
|
162
|
+
this.dataPolling();
|
|
163
|
+
this.runWatchDog();
|
|
164
|
+
this.atMidnight();
|
|
165
|
+
this.log.debug('InitProzess STOP');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
atMidnight() {
|
|
169
|
+
const now = new Date();
|
|
170
|
+
const night = new Date(
|
|
171
|
+
now.getFullYear(),
|
|
172
|
+
now.getMonth(),
|
|
173
|
+
now.getDate() + 1, // the next day, ...
|
|
174
|
+
0, 0, 0 // ...at 00:00:00 hours
|
|
175
|
+
);
|
|
176
|
+
const msToMidnight = night.getTime() - now.getTime();
|
|
177
|
+
|
|
178
|
+
if (this.mitnightTimer) this.clearTimeout(this.mitnightTimer);
|
|
179
|
+
this.mitnightTimer = this.setTimeout(async () => {
|
|
180
|
+
await this.state.mitnightProcess(); // the function being called at midnight.
|
|
181
|
+
this.atMidnight(); // reset again next midnight.
|
|
182
|
+
}, msToMidnight);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async checkAndPrepare() {
|
|
186
|
+
// Time of Using charging and discharging periodes (siehe Table 5-6)
|
|
187
|
+
// tCDP[3]= 127
|
|
188
|
+
const tCDP = [1,0,1440,383,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; //nicht aus dem Netz laden
|
|
189
|
+
this.modbusClient.setID(this.inverters[0].modbusId); //Master Inverter
|
|
190
|
+
const data = await this.modbusClient.readHoldingRegisters(47086,4); //
|
|
191
|
+
/*
|
|
192
|
+
127 - Working mode settings
|
|
193
|
+
2 : Maximise self consumptions (default)
|
|
194
|
+
5 : Time Of Use(Luna) - hilfreich bei dynamischem Stromtarif (z.B Tibber)
|
|
195
|
+
*/
|
|
196
|
+
const workingMode = data[0]; // Working mode settings 2:Maximise self consumptio5=)
|
|
197
|
+
const chargeFromGrid = data[1]; // Voraussetzung für Netzbezug in den Speicher (Luna)
|
|
198
|
+
const gridChargeCutOff = data[2]/10; // Ab welcher Schwelle wird der Netzbezug beendet (default 50 %)
|
|
199
|
+
const storageModel = data[3]; // Modell/Herrsteller des Speichers, 2 : HUAWEI-LUNA2000
|
|
200
|
+
|
|
201
|
+
if (storageModel == 2) { //wurde nur mit Luna getestet!
|
|
202
|
+
if (workingMode != 5 || chargeFromGrid != 1 ) {
|
|
203
|
+
this.log.debug('Row '+data+' Workingmode '+workingMode+ ' Charge from Grid '+chargeFromGrid+ ' Grid Cut Off '+gridChargeCutOff+'%');
|
|
204
|
+
await this.modbusClient.writeRegisters(47086,[5,1,500]);
|
|
205
|
+
//await writeRegistersAsync(1,47086,[5,1,500]); //[TOU,chargeFromGrid,50%]
|
|
206
|
+
await this.modbusClient.writeRegisters(47255,tCDP);
|
|
207
|
+
//await writeRegistersAsync(1,47255,tCDP); //Plan:1,StartZeit:00:00,EndZeit: 24:00,Endladen/täglich
|
|
208
|
+
/* ggf. sinnvoll
|
|
209
|
+
await writeRegistersAsync(1,47075,[0,5000]); //max. charging power
|
|
210
|
+
await writeRegistersAsync(1,47077,[0,5000]); //max. discharging power
|
|
211
|
+
*/
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
runWatchDog() {
|
|
217
|
+
this.watchDogHandle && this.clearInterval(this.watchDogHandle);
|
|
218
|
+
this.watchDogHandle = this.setInterval( () => {
|
|
219
|
+
if (!this.lastTimeUpdated) this.lastUpdated = 0;
|
|
220
|
+
if (this.lastTimeUpdated > 0) {
|
|
221
|
+
const sinceLastUpdate = (new Date().getTime() - this.lastTimeUpdated)/1000;
|
|
222
|
+
this.log.debug('Watchdog: time to last update '+sinceLastUpdate+' sec');
|
|
223
|
+
const lastIsConnected = this.isConnected;
|
|
224
|
+
this.isConnected = this.lastStateUpdated > 0 && sinceLastUpdate < this.config.updateInterval*2;
|
|
225
|
+
this.log.debug('lastIsConncted '+lastIsConnected+' isConnectetd '+this.isConnected+' lastStateupdated '+this.lastStateUpdated);
|
|
226
|
+
|
|
227
|
+
if (this.isConnected !== lastIsConnected ) this.setState('info.connection', this.isConnected, true);
|
|
228
|
+
if (sinceLastUpdate > this.config.updateInterval*10) {
|
|
229
|
+
this.log.warn('watchdog: restart Adapter...');
|
|
230
|
+
this.restart();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
},30000);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Is called when databases are connected and adapter received configuration.
|
|
239
|
+
*/
|
|
240
|
+
async onReady() {
|
|
241
|
+
|
|
242
|
+
// Initialize your adapter here
|
|
243
|
+
await this.setStateAsync('info.ip', {val: this.config.address, ack: true});
|
|
244
|
+
await this.setStateAsync('info.port', {val: this.config.port, ack: true});
|
|
245
|
+
//await this.setStateAsync('info.modbusUpdateInterval', {val: this.config.updateInterval, ack: true});
|
|
246
|
+
await this.setStateAsync('info.modbusIds', {val: this.config.modbusIds, ack: true});
|
|
247
|
+
|
|
248
|
+
// Load user settings
|
|
249
|
+
if (this.config.address !== '' || this.config.port > 0 || this.config.updateInterval > 0 ) {
|
|
250
|
+
this.settings.intervall = this.config.updateInterval*1000;
|
|
251
|
+
this.settings.address = this.config.address;
|
|
252
|
+
this.settings.port = this.config.port;
|
|
253
|
+
this.settings.modbusIds = this.config.modbusIds.split(',').map((n) => {return Number(n);});
|
|
254
|
+
|
|
255
|
+
if (this.settings.modbusIds.length > 0 && this.settings.modbusIds.length < 6) {
|
|
256
|
+
if (this.settings.intervall < 10000*this.settings.modbusIds.length ) {
|
|
257
|
+
this.config.updateInterval = 10000*this.settings.modbusIds.length;
|
|
258
|
+
}
|
|
259
|
+
await this.setStateAsync('info.modbusUpdateInterval', {val: this.settings.intervall/1000, ack: true});
|
|
260
|
+
for (const [i,id] of this.settings.modbusIds.entries()) {
|
|
261
|
+
this.inverters.push({index: i, modbusId: id, meter: (i==0)});
|
|
262
|
+
}
|
|
263
|
+
await this.InitProcess();
|
|
264
|
+
} else {
|
|
265
|
+
this.log.error('*** Adapter deactivated, can\'t parse modbusIds ! ***');
|
|
266
|
+
this.setForeignState('system.' + this.namespace + '.alive', false);
|
|
267
|
+
}
|
|
268
|
+
} else {
|
|
269
|
+
this.log.error('*** Adapter deactivated, credentials missing in Adapter Settings ! ***');
|
|
270
|
+
this.setForeignState('system.' + this.namespace + '.alive', false);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async dataPolling() {
|
|
275
|
+
|
|
276
|
+
function timeLeft(target) {
|
|
277
|
+
const left = target - new Date().getTime();
|
|
278
|
+
if (left < 0) return 0;
|
|
279
|
+
return left;
|
|
280
|
+
}
|
|
281
|
+
const start = new Date().getTime();
|
|
282
|
+
|
|
283
|
+
this.log.debug('### DataPolling START <- '+ Math.round((start-this.lastTimeUpdated)/1000)+' sec ###');
|
|
284
|
+
if (this.lastTimeUpdated > 0 && (start-this.lastTimeUpdated)/1000 > this.config.updateInterval + 1) {
|
|
285
|
+
this.log.warn('time intervall '+(start-this.lastTimeUpdated)/1000+' sec');
|
|
286
|
+
}
|
|
287
|
+
this.lastTimeUpdated = start;
|
|
288
|
+
let stateUpdated = 0;
|
|
289
|
+
|
|
290
|
+
//ZielZeit
|
|
291
|
+
//nextTick = this.config.updateInterval*1000 - now % (this.config.updateInterval*1000);
|
|
292
|
+
|
|
293
|
+
const target = this.config.updateInterval*1000 - start % (this.config.updateInterval*1000) + start;
|
|
294
|
+
|
|
295
|
+
//High Loop
|
|
296
|
+
for (const item of this.inverters) {
|
|
297
|
+
this.modbusClient.setID(item.modbusId);
|
|
298
|
+
//this.log.info('### Left Time '+timeLeft/1000);
|
|
299
|
+
stateUpdated += await this.state.updateStates(item,this.modbusClient,dataRefreshRate.high,timeLeft(target));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (timeLeft(target) > 5000) {
|
|
303
|
+
await this.state.runProcessHooks(dataRefreshRate.high);
|
|
304
|
+
|
|
305
|
+
//Low Loop
|
|
306
|
+
for (const item of this.inverters) {
|
|
307
|
+
this.modbusClient.setID(item.modbusId);
|
|
308
|
+
//this.log.info('### Left Time '+timeLeft/1000);
|
|
309
|
+
stateUpdated += await this.state.updateStates(item,this.modbusClient,dataRefreshRate.low,timeLeft(target));
|
|
310
|
+
}
|
|
311
|
+
if (timeLeft(target) > 2000) {
|
|
312
|
+
await this.state.runProcessHooks(dataRefreshRate.low);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
this.lastStateUpdated = stateUpdated;
|
|
317
|
+
|
|
318
|
+
if (this.timer) this.clearTimeout(this.timer);
|
|
319
|
+
this.timer = this.setTimeout(() => {
|
|
320
|
+
this.dataPolling(); //recursiv
|
|
321
|
+
}, timeLeft(target));
|
|
322
|
+
this.log.debug('### DataPolling STOP ###');
|
|
323
|
+
//this.state.mitnightProcess();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Is called when adapter shuts down - callback has to be called under any circumstances!
|
|
328
|
+
* @param {() => void} callback
|
|
329
|
+
*/
|
|
330
|
+
onUnload(callback) {
|
|
331
|
+
try {
|
|
332
|
+
this.timer && this.clearTimeout(this.timer);
|
|
333
|
+
this.mitnightTimer && this.clearTimeout(this.mitnightTimer);
|
|
334
|
+
this.watchDogHandle && this.clearInterval(this.watchDogHandle);
|
|
335
|
+
this.modbusClient && this.modbusClient.close();
|
|
336
|
+
this.setState('info.connection', false, true);
|
|
337
|
+
this.log.info('cleaned everything up...');
|
|
338
|
+
callback();
|
|
339
|
+
} catch (e) {
|
|
340
|
+
callback();
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// If you need to react to object changes, uncomment the following block and the corresponding line in the constructor.
|
|
345
|
+
// You also need to subscribe to the objects with `this.subscribeObjects`, similar to `this.subscribeStates`.
|
|
346
|
+
// /**
|
|
347
|
+
// * Is called if a subscribed object changes
|
|
348
|
+
// * @param {string} id
|
|
349
|
+
// * @param {ioBroker.Object | null | undefined} obj
|
|
350
|
+
// */
|
|
351
|
+
// onObjectChange(id, obj) {
|
|
352
|
+
// if (obj) {
|
|
353
|
+
// // The object was changed
|
|
354
|
+
// this.log.info(`object ${id} changed: ${JSON.stringify(obj)}`);
|
|
355
|
+
// } else {
|
|
356
|
+
// // The object was deleted
|
|
357
|
+
// this.log.info(`object ${id} deleted`);
|
|
358
|
+
// }
|
|
359
|
+
// }
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Is called if a subscribed state changes
|
|
363
|
+
* @param {string} id
|
|
364
|
+
* @param {ioBroker.State | null | undefined} state
|
|
365
|
+
*/
|
|
366
|
+
onStateChange(id, state) {
|
|
367
|
+
if (state) {
|
|
368
|
+
// The state was changed
|
|
369
|
+
this.log.info(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
|
|
370
|
+
} else {
|
|
371
|
+
// The state was deleted
|
|
372
|
+
this.log.info(`state ${id} deleted`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// If you need to accept messages in your adapter, uncomment the following block and the corresponding line in the constructor.
|
|
377
|
+
// /**
|
|
378
|
+
// * Some message was sent to this instance over message box. Used by email, pushover, text2speech, ...
|
|
379
|
+
// * Using this method requires "common.messagebox" property to be set to true in io-package.json
|
|
380
|
+
// * @param {ioBroker.Message} obj
|
|
381
|
+
// */
|
|
382
|
+
// onMessage(obj) {
|
|
383
|
+
// if (typeof obj === 'object' && obj.message) {
|
|
384
|
+
// if (obj.command === 'send') {
|
|
385
|
+
// // e.g. send email or pushover or whatever
|
|
386
|
+
// this.log.info('send command');
|
|
387
|
+
|
|
388
|
+
// // Send response in callback if required
|
|
389
|
+
// if (obj.callback) this.sendTo(obj.from, obj.command, 'Message received', obj.callback);
|
|
390
|
+
// }
|
|
391
|
+
// }
|
|
392
|
+
// }
|
|
393
|
+
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (require.main !== module) {
|
|
397
|
+
// Export the constructor in compact mode
|
|
398
|
+
/**
|
|
399
|
+
* @param {Partial<utils.AdapterOptions>} [options={}]
|
|
400
|
+
*/
|
|
401
|
+
module.exports = (options) => new Sun2000(options);
|
|
402
|
+
} else {
|
|
403
|
+
// otherwise start the instance directly
|
|
404
|
+
new Sun2000();
|
|
405
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "iobroker.sun2000",
|
|
3
|
+
"version": "0.1.2-alpha.1",
|
|
4
|
+
"description": "sun2000",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "bolliy",
|
|
7
|
+
"email": "stephan@mante.info"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/bolliy/ioBroker.sun2000",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"ioBroker",
|
|
13
|
+
"template",
|
|
14
|
+
"Smart Home",
|
|
15
|
+
"home automation"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/bolliy/ioBroker.sun2000.git"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">= 16"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@iobroker/adapter-core": "^3.0.4",
|
|
26
|
+
"modbus-serial": "^8.0.13"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@alcalzone/release-script": "^3.6.0",
|
|
30
|
+
"@alcalzone/release-script-plugin-iobroker": "^3.6.0",
|
|
31
|
+
"@alcalzone/release-script-plugin-license": "^3.5.9",
|
|
32
|
+
"@alcalzone/release-script-plugin-manual-review": "^3.5.9",
|
|
33
|
+
"@iobroker/adapter-dev": "^1.2.0",
|
|
34
|
+
"@iobroker/testing": "^4.1.0",
|
|
35
|
+
"@tsconfig/node16": "^16.1.1",
|
|
36
|
+
"@types/chai": "^4.3.10",
|
|
37
|
+
"@types/chai-as-promised": "^7.1.8",
|
|
38
|
+
"@types/mocha": "^10.0.4",
|
|
39
|
+
"@types/node": "^20.10.6",
|
|
40
|
+
"@types/proxyquire": "^1.3.31",
|
|
41
|
+
"@types/sinon": "^17.0.1",
|
|
42
|
+
"@types/sinon-chai": "^3.2.12",
|
|
43
|
+
"chai": "^4.3.10",
|
|
44
|
+
"chai-as-promised": "^7.1.1",
|
|
45
|
+
"eslint": "^8.56.0",
|
|
46
|
+
"mocha": "^10.2.0",
|
|
47
|
+
"proxyquire": "^2.1.3",
|
|
48
|
+
"sinon": "^17.0.1",
|
|
49
|
+
"sinon-chai": "^3.7.0",
|
|
50
|
+
"typescript": "~5.3.3"
|
|
51
|
+
},
|
|
52
|
+
"main": "main.js",
|
|
53
|
+
"files": [
|
|
54
|
+
"admin{,/!(src)/**}/!(tsconfig|tsconfig.*|.eslintrc).{json,json5}",
|
|
55
|
+
"admin{,/!(src)/**}/*.{html,css,png,svg,jpg,js}",
|
|
56
|
+
"lib/",
|
|
57
|
+
"www/",
|
|
58
|
+
"io-package.json",
|
|
59
|
+
"LICENSE",
|
|
60
|
+
"main.js"
|
|
61
|
+
],
|
|
62
|
+
"scripts": {
|
|
63
|
+
"test:js": "mocha --config test/mocharc.custom.json \"{!(node_modules|test)/**/*.test.js,*.test.js,test/**/test!(PackageFiles|Startup).js}\"",
|
|
64
|
+
"test:package": "mocha test/package --exit",
|
|
65
|
+
"test:integration": "mocha test/integration --exit",
|
|
66
|
+
"test": "npm run test:js && npm run test:package",
|
|
67
|
+
"check": "tsc --noEmit -p tsconfig.check.json",
|
|
68
|
+
"lint": "eslint .",
|
|
69
|
+
"translate": "translate-adapter",
|
|
70
|
+
"release": "release-script",
|
|
71
|
+
"release-minor": "release-script minor --yes",
|
|
72
|
+
"release-major": "release-script major --yes"
|
|
73
|
+
},
|
|
74
|
+
"bugs": {
|
|
75
|
+
"url": "https://github.com/bolliy/ioBroker.sun2000/issues"
|
|
76
|
+
},
|
|
77
|
+
"readmeFilename": "README.md"
|
|
78
|
+
}
|