iobroker.panasonic-comfort-cloud 1.2.5 → 2.0.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2019 marc <lammers.marc@gmail.com>
3
+ Copyright (c) 2021 marc <marc@lammers.dev>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -12,85 +12,56 @@
12
12
 
13
13
  ## panasonic-comfort-cloud adapter for ioBroker
14
14
 
15
- Adapter for Panasonic Comfort Cloud
16
-
17
- ## Developer manual
18
- This section is intended for the developer. It can be deleted later
19
-
20
- ### Getting started
21
-
22
- You are almost done, only a few steps left:
23
- 1. Create a new repository on GitHub with the name `ioBroker.panasonic-comfort-cloud`
24
- 1. Initialize the current folder as a new git repository:
25
- ```bash
26
- git init
27
- git add .
28
- git commit -m "Initial commit"
29
- ```
30
- 1. Link your local repository with the one on GitHub:
31
- ```bash
32
- git remote add origin https://github.com/marc2016/ioBroker.panasonic-comfort-cloud
33
- ```
34
-
35
- 1. Push all files to the GitHub repo:
36
- ```bash
37
- git push origin master
38
- ```
39
- 1. Head over to [src/main.ts](src/main.ts) and start programming!
40
-
41
- ### Scripts in `package.json`
42
- Several npm scripts are predefined for your convenience. You can run them using `npm run <scriptname>`
43
- | Script name | Description |
44
- |-------------|----------------------------------------------------------|
45
- | `build` | Re-compile the TypeScript sources. |
46
- | `watch` | Re-compile the TypeScript sources and watch for changes. |
47
- | `test:ts` | Executes the tests you defined in `*.test.ts` files. |
48
- | `test:package` | Ensures your `package.json` and `io-package.json` are valid. |
49
- | `test:unit` | Tests the adapter startup with unit tests (fast, but might require module mocks to work). |
50
- | `test:integration`| Tests the adapter startup with an actual instance of ioBroker. |
51
- | `test` | Performs a minimal test run on package files and your tests. |
52
- | `coverage` | Generates code coverage using your test files. |
53
-
54
- ### Writing tests
55
- When done right, testing code is invaluable, because it gives you the
56
- confidence to change your code while knowing exactly if and when
57
- something breaks. A good read on the topic of test-driven development
58
- is https://hackernoon.com/introduction-to-test-driven-development-tdd-61a13bc92d92.
59
- Although writing tests before the code might seem strange at first, but it has very
60
- clear upsides.
61
-
62
- The template provides you with basic tests for the adapter startup and package files.
63
- It is recommended that you add your own tests into the mix.
64
-
65
- ### Publishing the adapter
66
- See the documentation of [ioBroker.repositories](https://github.com/ioBroker/ioBroker.repositories#requirements-for-adapter-to-get-added-to-the-latest-repository).
67
-
68
- ### Test the adapter manually on a local ioBroker installation
69
- In order to install the adapter locally without publishing, the following steps are recommended:
70
- 1. Create a tarball from your dev directory:
71
- ```bash
72
- npm pack
73
- ```
74
- 1. Upload the resulting file to your ioBroker host
75
- 1. Install it locally (The paths are different on Windows):
76
- ```bash
77
- cd /opt/iobroker
78
- npm i /path/to/tarball.tgz
79
- ```
80
-
81
- For later updates, the above procedure is not necessary. Just do the following:
82
- 1. Overwrite the changed files in the adapter directory (`/opt/iobroker/node_modules/iobroker.panasonic-comfort-cloud`)
83
- 1. Execute `iobroker upload panasonic-comfort-cloud` on the ioBroker host
15
+ Adapter to control devices in the Panasonic Comfort Cloud. It uses REST calls which are extracetd from the official Comfort Cloud app.
16
+ To use the a adpter you need to enter your username and password in the configuration. They are used to authenticate access to the Comfort Cloud. Information of all devices is automatically retrieved and inserted as an object. The adpter polls the device information cyclically (see interval in the settings) and sends commands directly to the cloud.
17
+
18
+ With the method used, only one client can be logged on with the account at a time.
19
+ It is recommended that a second account, for which the devices have been shared, is used.
20
+
84
21
 
85
22
  ## Changelog
86
23
 
87
- ### 0.0.1
88
- * (marc) initial release
24
+ ### 2.0.0
25
+ * Added js-controller 3 dependency.
26
+ * Added username and password to protectedNative and password to encryptedNative.
27
+ * Added connection info.
28
+ * Changed schdule to timeout for refresh.
29
+ * Fixes for async await pattern.
30
+
31
+ ### 1.2.9
32
+ * Error handling for get device added.
33
+
34
+ ### 1.2.8
35
+ * Fixed bug in Comfort Cloud client.
36
+
37
+ ### 1.2.7
38
+ * Comfort Cloud client updated.
39
+
40
+ ### 1.2.6
41
+ * Fixed bug that guid is null in device creation.
42
+
43
+ ### 1.2.5
44
+ * *Comfort Cloud client updated.
45
+
46
+ ### 1.2.4
47
+ * Fixed bug with undefined guid. Log messages added.
48
+
49
+ ### 1.2.3
50
+ * Set parameters only for writable states.
51
+
52
+ ### 1.2.2
53
+ * *Fixed error handling and added stack info.
54
+
55
+ ### 1.2.1
56
+ * Fixed bug in refesh device method.
57
+
58
+ ### 1.2.0
59
+ * States insideTemperature, outTemperature and Nanoe added.
89
60
 
90
61
  ## License
91
62
  MIT License
92
63
 
93
- Copyright (c) 2019 marc <lammers.marc@gmail.com>
64
+ Copyright (c) 2021 marc <marc@lammers.dev>
94
65
 
95
66
  Permission is hereby granted, free of charge, to any person obtaining a copy
96
67
  of this software and associated documentation files (the "Software"), to deal
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.translateText = exports.isArray = exports.isObject = void 0;
12
13
  const axios_1 = require("axios");
13
14
  /**
14
15
  * Tests whether the given variable is a real object and not an Array
@@ -19,7 +20,7 @@ function isObject(it) {
19
20
  // typeof null === 'object'
20
21
  // typeof [] === 'object'
21
22
  // [] instanceof Object === true
22
- return Object.prototype.toString.call(it) === "[object Object]";
23
+ return Object.prototype.toString.call(it) === '[object Object]';
23
24
  }
24
25
  exports.isObject = isObject;
25
26
  /**
@@ -29,7 +30,7 @@ exports.isObject = isObject;
29
30
  function isArray(it) {
30
31
  if (Array.isArray != null)
31
32
  return Array.isArray(it);
32
- return Object.prototype.toString.call(it) === "[object Array]";
33
+ return Object.prototype.toString.call(it) === '[object Array]';
33
34
  }
34
35
  exports.isArray = isArray;
35
36
  /**
@@ -39,16 +40,16 @@ exports.isArray = isArray;
39
40
  */
40
41
  function translateText(text, targetLang) {
41
42
  return __awaiter(this, void 0, void 0, function* () {
42
- if (targetLang === "en")
43
+ if (targetLang === 'en')
43
44
  return text;
44
45
  try {
45
46
  const url = `http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}&ie=UTF-8&oe=UTF-8`;
46
- const response = yield axios_1.default({ url, timeout: 5000 });
47
+ const response = yield (0, axios_1.default)({ url, timeout: 5000 });
47
48
  if (isArray(response.data)) {
48
49
  // we got a valid response
49
50
  return response.data[0][0][0];
50
51
  }
51
- throw new Error("Invalid response for translate request");
52
+ throw new Error('Invalid response for translate request');
52
53
  }
53
54
  catch (e) {
54
55
  throw new Error(`Could not translate to "${targetLang}": ${e}`);
package/build/main.js CHANGED
@@ -16,67 +16,77 @@ Object.defineProperty(exports, "__esModule", { value: true });
16
16
  // you need to create an adapter
17
17
  const utils = require("@iobroker/adapter-core");
18
18
  const panasonic_comfort_cloud_client_1 = require("panasonic-comfort-cloud-client");
19
- const node_schedule_1 = require("node-schedule");
20
19
  const _ = require("lodash");
20
+ const REFRESH_INTERVAL_IN_MINUTES_DEFAULT = 5;
21
21
  const comfortCloudClient = new panasonic_comfort_cloud_client_1.ComfortCloudClient();
22
22
  class PanasonicComfortCloud extends utils.Adapter {
23
23
  constructor(options = {}) {
24
- super(Object.assign(Object.assign({}, options), { name: "panasonic-comfort-cloud" }));
25
- this.on("ready", this.onReady.bind(this));
26
- this.on("objectChange", this.onObjectChange.bind(this));
27
- this.on("stateChange", this.onStateChange.bind(this));
24
+ super(Object.assign(Object.assign({}, options), { name: 'panasonic-comfort-cloud' }));
25
+ this.refreshIntervalInMinutes = REFRESH_INTERVAL_IN_MINUTES_DEFAULT;
26
+ this.readonlyStateNames = [];
27
+ this.on('ready', this.onReady.bind(this));
28
+ this.on('objectChange', this.onObjectChange.bind(this));
29
+ this.on('stateChange', this.onStateChange.bind(this));
28
30
  // this.on('message', this.onMessage.bind(this));
29
- this.on("unload", this.onUnload.bind(this));
31
+ this.on('unload', this.onUnload.bind(this));
30
32
  }
31
33
  /**
32
34
  * Is called when databases are connected and adapter received configuration.
33
35
  */
34
36
  onReady() {
35
- var _a;
37
+ var _a, _b, _c, _d;
36
38
  return __awaiter(this, void 0, void 0, function* () {
37
- const refreshInterval = (_a = this.config.refreshInterval) !== null && _a !== void 0 ? _a : 5;
38
- this.refreshJob = node_schedule_1.scheduleJob(`*/${refreshInterval} * * * *`, this.refreshDevices.bind(this));
39
- this.subscribeStates("*");
40
- try {
41
- this.log.debug(`Try to login with username ${this.config.username}.`);
42
- yield comfortCloudClient.login(this.config.username, this.config.password);
43
- this.log.info("Login successful.");
44
- this.log.debug("Create devices.");
45
- const groups = yield comfortCloudClient.getGroups();
46
- this.createDevices(groups);
39
+ this.refreshIntervalInMinutes = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.refreshInterval) !== null && _b !== void 0 ? _b : REFRESH_INTERVAL_IN_MINUTES_DEFAULT;
40
+ this.subscribeStates('*');
41
+ this.setState('info.connection', false, true);
42
+ if (!((_c = this.config) === null || _c === void 0 ? void 0 : _c.username) || !((_d = this.config) === null || _d === void 0 ? void 0 : _d.password)) {
43
+ this.log.error('Can not start without username or password. Please open config.');
47
44
  }
48
- catch (error) {
49
- this.handleClientError(error);
45
+ else {
46
+ try {
47
+ this.log.debug(`Try to login with username ${this.config.username}.`);
48
+ yield comfortCloudClient.login(this.config.username, this.config.password);
49
+ this.log.info('Login successful.');
50
+ this.setState('info.connection', true, true);
51
+ this.log.debug('Create devices.');
52
+ const groups = yield comfortCloudClient.getGroups();
53
+ yield this.createDevices(groups);
54
+ this.setupRefreshTimeout();
55
+ }
56
+ catch (error) {
57
+ yield this.handleClientError(error);
58
+ }
50
59
  }
51
60
  });
52
61
  }
53
62
  refreshDeviceStates(device) {
54
- this.log.debug(`Refresh device ${device.name} (${device.guid}).`);
55
- this.log.debug(`${device.name}: guid => ${device.guid}.`);
56
- this.setStateChangedAsync(`${device.name}.guid`, device.guid, true);
57
- this.log.debug(`${device.name}: operate => ${device.operate}.`);
58
- this.setStateChangedAsync(`${device.name}.operate`, device.operate, true);
59
- this.log.debug(`${device.name}: temperatureSet => ${device.temperatureSet}.`);
60
- this.setStateChangedAsync(`${device.name}.temperatureSet`, device.temperatureSet, true);
61
- this.log.debug(`${device.name}: insideTemperature => ${device.insideTemperature}.`);
62
- this.setStateChangedAsync(`${device.name}.insideTemperature`, device.insideTemperature, true);
63
- this.log.debug(`${device.name}: outTemperature => ${device.outTemperature}.`);
64
- this.setStateChangedAsync(`${device.name}.outTemperature`, device.outTemperature, true);
65
- this.log.debug(`${device.name}: airSwingLR => ${device.airSwingLR}.`);
66
- this.setStateChangedAsync(`${device.name}.airSwingLR`, device.airSwingLR, true);
67
- this.log.debug(`${device.name}: airSwingUD => ${device.airSwingUD}.`);
68
- this.setStateChangedAsync(`${device.name}.airSwingUD`, device.airSwingUD, true);
69
- this.log.debug(`${device.name}: fanAutoMode => ${device.fanAutoMode}.`);
70
- this.setStateChangedAsync(`${device.name}.fanAutoMode`, device.fanAutoMode, true);
71
- this.log.debug(`${device.name}: ecoMode => ${device.ecoMode}.`);
72
- this.setStateChangedAsync(`${device.name}.ecoMode`, device.ecoMode, true);
73
- this.log.debug(`${device.name}: operationMode => ${device.operationMode}.`);
74
- this.setStateChangedAsync(`${device.name}.operationMode`, device.operationMode, true);
75
- this.log.debug(`${device.name}: fanSpeed => ${device.fanSpeed}.`);
76
- this.setStateChangedAsync(`${device.name}.fanSpeed`, device.fanSpeed, true);
77
- this.log.debug(`${device.name}: actualNanoe => ${device.actualNanoe}.`);
78
- this.setStateChangedAsync(`${device.name}.actualNanoe`, device.actualNanoe, true);
79
- this.log.debug(`Refresh device ${device.name} finished.`);
63
+ return __awaiter(this, void 0, void 0, function* () {
64
+ this.log.debug(`Refresh device ${device.name} (${device.guid}).`);
65
+ this.log.debug(`${device.name}: guid => ${device.guid}.`);
66
+ this.log.debug(`${device.name}: operate => ${device.operate}.`);
67
+ yield this.setStateChangedAsync(`${device.name}.operate`, device.operate, true);
68
+ this.log.debug(`${device.name}: temperatureSet => ${device.temperatureSet}.`);
69
+ yield this.setStateChangedAsync(`${device.name}.temperatureSet`, device.temperatureSet, true);
70
+ this.log.debug(`${device.name}: insideTemperature => ${device.insideTemperature}.`);
71
+ yield this.setStateChangedAsync(`${device.name}.insideTemperature`, device.insideTemperature, true);
72
+ this.log.debug(`${device.name}: outTemperature => ${device.outTemperature}.`);
73
+ yield this.setStateChangedAsync(`${device.name}.outTemperature`, device.outTemperature, true);
74
+ this.log.debug(`${device.name}: airSwingLR => ${device.airSwingLR}.`);
75
+ yield this.setStateChangedAsync(`${device.name}.airSwingLR`, device.airSwingLR, true);
76
+ this.log.debug(`${device.name}: airSwingUD => ${device.airSwingUD}.`);
77
+ yield this.setStateChangedAsync(`${device.name}.airSwingUD`, device.airSwingUD, true);
78
+ this.log.debug(`${device.name}: fanAutoMode => ${device.fanAutoMode}.`);
79
+ yield this.setStateChangedAsync(`${device.name}.fanAutoMode`, device.fanAutoMode, true);
80
+ this.log.debug(`${device.name}: ecoMode => ${device.ecoMode}.`);
81
+ yield this.setStateChangedAsync(`${device.name}.ecoMode`, device.ecoMode, true);
82
+ this.log.debug(`${device.name}: operationMode => ${device.operationMode}.`);
83
+ yield this.setStateChangedAsync(`${device.name}.operationMode`, device.operationMode, true);
84
+ this.log.debug(`${device.name}: fanSpeed => ${device.fanSpeed}.`);
85
+ yield this.setStateChangedAsync(`${device.name}.fanSpeed`, device.fanSpeed, true);
86
+ this.log.debug(`${device.name}: actualNanoe => ${device.actualNanoe}.`);
87
+ yield this.setStateChangedAsync(`${device.name}.actualNanoe`, device.actualNanoe, true);
88
+ this.log.debug(`Refresh device ${device.name} finished.`);
89
+ });
80
90
  }
81
91
  refreshDevice(guid, deviceName) {
82
92
  return __awaiter(this, void 0, void 0, function* () {
@@ -88,18 +98,19 @@ class PanasonicComfortCloud extends utils.Adapter {
88
98
  if (!device.name) {
89
99
  device.name = deviceName;
90
100
  }
91
- this.refreshDeviceStates(device);
101
+ yield this.refreshDeviceStates(device);
92
102
  }
93
103
  catch (error) {
94
- this.handleClientError(error);
104
+ yield this.handleClientError(error);
95
105
  }
96
106
  });
97
107
  }
98
108
  refreshDevices() {
99
109
  return __awaiter(this, void 0, void 0, function* () {
100
110
  try {
101
- this.log.debug("Refresh all devices.");
111
+ this.log.debug('Refresh all devices.');
102
112
  const groups = yield comfortCloudClient.getGroups();
113
+ this.setState('info.connection', true, true);
103
114
  const devices = _.flatMap(groups, g => g.devices);
104
115
  const deviceInfos = _.map(devices, d => { return { guid: d.guid, name: d.name }; });
105
116
  yield Promise.all(deviceInfos.map((deviceInfo) => __awaiter(this, void 0, void 0, function* () {
@@ -107,12 +118,12 @@ class PanasonicComfortCloud extends utils.Adapter {
107
118
  if (device != null) {
108
119
  device.name = deviceInfo.name;
109
120
  device.guid = deviceInfo.guid;
110
- this.refreshDeviceStates(device);
121
+ yield this.refreshDeviceStates(device);
111
122
  }
112
123
  })));
113
124
  }
114
125
  catch (error) {
115
- this.handleClientError(error);
126
+ yield this.handleClientError(error);
116
127
  }
117
128
  });
118
129
  }
@@ -126,36 +137,49 @@ class PanasonicComfortCloud extends utils.Adapter {
126
137
  const deviceInfos = _.map(devicesFromService, d => { return { guid: d.guid, name: d.name }; });
127
138
  yield Promise.all(deviceInfos.map((deviceInfo) => __awaiter(this, void 0, void 0, function* () {
128
139
  this.log.debug(`Device info from group ${deviceInfo.guid}, ${deviceInfo.name}.`);
129
- const device = yield comfortCloudClient.getDevice(deviceInfo.guid);
140
+ let device = null;
141
+ try {
142
+ device = yield comfortCloudClient.getDevice(deviceInfo.guid);
143
+ }
144
+ catch (error) {
145
+ yield this.handleClientError(error);
146
+ }
130
147
  if (device != null) {
131
148
  if (_.includes(names, deviceInfo.name)) {
132
149
  return;
133
150
  }
134
151
  this.createDevice(deviceInfo.name);
135
- this.createState(deviceInfo.name, "", "guid", { role: "text", write: false, def: device.guid }, undefined);
136
- this.createState(deviceInfo.name, "", "operate", {
137
- role: "state",
152
+ this.createState(deviceInfo.name, '', 'guid', { role: 'info.address', write: false, def: deviceInfo.guid, type: 'string' }, undefined);
153
+ this.readonlyStateNames.push('guid');
154
+ this.createState(deviceInfo.name, '', 'operate', {
155
+ role: 'switch.power',
138
156
  states: { 0: panasonic_comfort_cloud_client_1.Power[0], 1: panasonic_comfort_cloud_client_1.Power[1] },
139
157
  write: true,
140
158
  def: device.operate,
159
+ type: 'string',
141
160
  }, undefined);
142
- this.createState(deviceInfo.name, "", "temperatureSet", {
143
- role: "level.temperature",
161
+ this.createState(deviceInfo.name, '', 'temperatureSet', {
162
+ role: 'level.temperature',
144
163
  write: true,
145
164
  def: device.temperatureSet,
165
+ type: 'number',
146
166
  }, undefined);
147
- this.createState(deviceInfo.name, "", "insideTemperature", {
148
- role: "state",
167
+ this.createState(deviceInfo.name, '', 'insideTemperature', {
168
+ role: 'level.temperature',
149
169
  write: false,
150
170
  def: device.insideTemperature,
171
+ type: 'number',
151
172
  }, undefined);
152
- this.createState(deviceInfo.name, "", "outTemperature", {
153
- role: "state",
173
+ this.readonlyStateNames.push('insideTemperature');
174
+ this.createState(deviceInfo.name, '', 'outTemperature', {
175
+ role: 'level.temperature',
154
176
  write: false,
155
177
  def: device.outTemperature,
178
+ type: 'number',
156
179
  }, undefined);
157
- this.createState(deviceInfo.name, "", "airSwingLR", {
158
- role: "state",
180
+ this.readonlyStateNames.push('outTemperature');
181
+ this.createState(deviceInfo.name, '', 'airSwingLR', {
182
+ role: 'state',
159
183
  states: {
160
184
  0: panasonic_comfort_cloud_client_1.AirSwingLR[0],
161
185
  1: panasonic_comfort_cloud_client_1.AirSwingLR[1],
@@ -165,9 +189,10 @@ class PanasonicComfortCloud extends utils.Adapter {
165
189
  },
166
190
  write: true,
167
191
  def: device.airSwingLR,
192
+ type: 'string',
168
193
  }, undefined);
169
- this.createState(deviceInfo.name, "", "airSwingUD", {
170
- role: "state",
194
+ this.createState(deviceInfo.name, '', 'airSwingUD', {
195
+ role: 'state',
171
196
  states: {
172
197
  0: panasonic_comfort_cloud_client_1.AirSwingUD[0],
173
198
  1: panasonic_comfort_cloud_client_1.AirSwingUD[1],
@@ -177,9 +202,10 @@ class PanasonicComfortCloud extends utils.Adapter {
177
202
  },
178
203
  write: true,
179
204
  def: device.airSwingUD,
205
+ type: 'string',
180
206
  }, undefined);
181
- this.createState(deviceInfo.name, "", "fanAutoMode", {
182
- role: "state",
207
+ this.createState(deviceInfo.name, '', 'fanAutoMode', {
208
+ role: 'state',
183
209
  states: {
184
210
  0: panasonic_comfort_cloud_client_1.FanAutoMode[0],
185
211
  1: panasonic_comfort_cloud_client_1.FanAutoMode[1],
@@ -188,15 +214,17 @@ class PanasonicComfortCloud extends utils.Adapter {
188
214
  },
189
215
  write: true,
190
216
  def: device.fanAutoMode,
217
+ type: 'string',
191
218
  }, undefined);
192
- this.createState(deviceInfo.name, "", "ecoMode", {
193
- role: "state",
219
+ this.createState(deviceInfo.name, '', 'ecoMode', {
220
+ role: 'state',
194
221
  states: { 0: panasonic_comfort_cloud_client_1.EcoMode[0], 1: panasonic_comfort_cloud_client_1.EcoMode[1], 2: panasonic_comfort_cloud_client_1.EcoMode[2] },
195
222
  write: true,
196
223
  def: device.ecoMode,
224
+ type: 'string',
197
225
  }, undefined);
198
- this.createState(deviceInfo.name, "", "operationMode", {
199
- role: "state",
226
+ this.createState(deviceInfo.name, '', 'operationMode', {
227
+ role: 'state',
200
228
  states: {
201
229
  0: panasonic_comfort_cloud_client_1.OperationMode[0],
202
230
  1: panasonic_comfort_cloud_client_1.OperationMode[1],
@@ -206,9 +234,10 @@ class PanasonicComfortCloud extends utils.Adapter {
206
234
  },
207
235
  write: true,
208
236
  def: device.operationMode,
237
+ type: 'string',
209
238
  }, undefined);
210
- this.createState(deviceInfo.name, "", "fanSpeed", {
211
- role: "state",
239
+ this.createState(deviceInfo.name, '', 'fanSpeed', {
240
+ role: 'state',
212
241
  states: {
213
242
  0: panasonic_comfort_cloud_client_1.FanSpeed[0],
214
243
  1: panasonic_comfort_cloud_client_1.FanSpeed[1],
@@ -219,9 +248,10 @@ class PanasonicComfortCloud extends utils.Adapter {
219
248
  },
220
249
  write: true,
221
250
  def: device.fanSpeed,
251
+ type: 'string',
222
252
  }, undefined);
223
- this.createState(deviceInfo.name, "", "actualNanoe", {
224
- role: "state",
253
+ this.createState(deviceInfo.name, '', 'actualNanoe', {
254
+ role: 'state',
225
255
  states: {
226
256
  0: panasonic_comfort_cloud_client_1.NanoeMode[0],
227
257
  1: panasonic_comfort_cloud_client_1.NanoeMode[1],
@@ -231,16 +261,17 @@ class PanasonicComfortCloud extends utils.Adapter {
231
261
  },
232
262
  write: true,
233
263
  def: device.actualNanoe,
264
+ type: 'string',
234
265
  }, undefined);
235
266
  this.log.info(`Device ${deviceInfo.name} created.`);
236
267
  }
237
268
  })));
238
- this.log.debug("Device creation completed.");
269
+ this.log.debug('Device creation completed.');
239
270
  });
240
271
  }
241
272
  updateDevice(deviceName, stateName, state) {
242
273
  return __awaiter(this, void 0, void 0, function* () {
243
- if (stateName == 'guid') {
274
+ if (this.readonlyStateNames.includes(stateName)) {
244
275
  return;
245
276
  }
246
277
  if (!state.ack) {
@@ -263,7 +294,7 @@ class PanasonicComfortCloud extends utils.Adapter {
263
294
  yield this.refreshDevice(guidState === null || guidState === void 0 ? void 0 : guidState.val, deviceName);
264
295
  }
265
296
  catch (error) {
266
- this.handleClientError(error);
297
+ yield this.handleClientError(error);
267
298
  }
268
299
  }
269
300
  });
@@ -272,10 +303,10 @@ class PanasonicComfortCloud extends utils.Adapter {
272
303
  * Is called when adapter shuts down - callback has to be called under any circumstances!
273
304
  */
274
305
  onUnload(callback) {
275
- var _a;
276
306
  try {
277
- this.log.info("cleaned everything up...");
278
- (_a = this.refreshJob) === null || _a === void 0 ? void 0 : _a.cancel();
307
+ if (this.refreshTimeout)
308
+ clearTimeout(this.refreshTimeout);
309
+ this.log.info('cleaned everything up...');
279
310
  callback();
280
311
  }
281
312
  catch (e) {
@@ -299,35 +330,63 @@ class PanasonicComfortCloud extends utils.Adapter {
299
330
  * Is called if a subscribed state changes
300
331
  */
301
332
  onStateChange(id, state) {
302
- if (state) {
303
- const elements = id.split(".");
304
- const deviceName = elements[elements.length - 2];
305
- const stateName = elements[elements.length - 1];
306
- this.updateDevice(deviceName, stateName, state);
307
- // The state was changed
308
- this.log.info(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
309
- }
310
- else {
311
- // The state was deleted
312
- this.log.info(`state ${id} deleted`);
313
- }
333
+ return __awaiter(this, void 0, void 0, function* () {
334
+ if (state) {
335
+ const elements = id.split('.');
336
+ const deviceName = elements[elements.length - 2];
337
+ const stateName = elements[elements.length - 1];
338
+ try {
339
+ yield this.updateDevice(deviceName, stateName, state);
340
+ }
341
+ catch (error) {
342
+ yield this.handleClientError(error);
343
+ }
344
+ // The state was changed
345
+ this.log.info(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
346
+ }
347
+ else {
348
+ // The state was deleted
349
+ this.log.info(`state ${id} deleted`);
350
+ }
351
+ });
314
352
  }
315
353
  handleClientError(error) {
316
354
  return __awaiter(this, void 0, void 0, function* () {
317
- this.log.debug("Try to handle error.");
355
+ this.log.debug('Try to handle error.');
318
356
  if (error instanceof panasonic_comfort_cloud_client_1.TokenExpiredError) {
319
357
  this.log.info(`Token of comfort cloud client expired. Trying to login again. Code=${error.code}. Stack: ${error.stack}`);
358
+ this.setState('info.connection', false, true);
320
359
  yield comfortCloudClient.login(this.config.username, this.config.password);
321
- this.log.info("Login successful.");
360
+ this.setState('info.connection', true, true);
361
+ this.log.info('Login successful.');
322
362
  }
323
363
  else if (error instanceof panasonic_comfort_cloud_client_1.ServiceError) {
364
+ this.setState('info.connection', false, true);
324
365
  this.log.error(`Service error: ${error.message}. Code=${error.code}. Stack: ${error.stack}`);
325
366
  }
326
- else {
367
+ else if (error instanceof Error) {
327
368
  this.log.error(`Unknown error: ${error}. Stack: ${error.stack}`);
328
369
  }
329
370
  });
330
371
  }
372
+ setupRefreshTimeout() {
373
+ this.log.debug('setupRefreshTimeout');
374
+ const refreshIntervalInMilliseconds = this.refreshIntervalInMinutes * 60 * 1000;
375
+ this.log.debug(`refreshIntervalInMilliseconds=${refreshIntervalInMilliseconds}`);
376
+ this.refreshTimeout = setTimeout(this.refreshTimeoutFunc.bind(this), refreshIntervalInMilliseconds);
377
+ }
378
+ refreshTimeoutFunc() {
379
+ return __awaiter(this, void 0, void 0, function* () {
380
+ this.log.debug(`refreshTimeoutFunc started.`);
381
+ try {
382
+ yield this.refreshDevices();
383
+ this.setupRefreshTimeout();
384
+ }
385
+ catch (error) {
386
+ yield this.handleClientError(error);
387
+ }
388
+ });
389
+ }
331
390
  }
332
391
  if (module.parent) {
333
392
  // Export the constructor in compact mode
package/io-package.json CHANGED
@@ -1,8 +1,28 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "panasonic-comfort-cloud",
4
- "version": "1.2.5",
4
+ "version": "2.0.0",
5
5
  "news": {
6
+ "2.0.0": {
7
+ "en": "Added js-controller 3 dependency.\nAdded username and password to protectedNative and password to encryptedNative.\nAdded connection info.\nChanged schdule to timeout for refresh.\nFixes for async await pattern.",
8
+ "de": "js-controller 3 als Abhängigkeit eingefügt.\nusername und password zu protectedNative und password zu encryptedNative hinzugefügt.\nconnection info als Status eingefügt.\nSchudle durch timeout für die Geräteaktualisierung erstezt.\nAsync und await überall korrekt berücksichtig."
9
+ },
10
+ "1.2.9": {
11
+ "en": "Error handling for get device added.",
12
+ "de": "Fehlerbehandlung beim Abrufen des Geräts hinzugefügt."
13
+ },
14
+ "1.2.8": {
15
+ "en": "Fixed bug in Comfort Cloud client.",
16
+ "de": "Fehler in Comfort Cloud client behoben."
17
+ },
18
+ "1.2.7": {
19
+ "en": "Comfort Cloud client updated.",
20
+ "de": "Comfort Cloud client aktualisiert."
21
+ },
22
+ "1.2.6": {
23
+ "en": "Fixed bug that guid is null in device creation.",
24
+ "de": "Fehler behoben, dass die GUID bei der Geräteerstellung null ist."
25
+ },
6
26
  "1.2.5": {
7
27
  "en": "Comfort Cloud client updated.",
8
28
  "de": "Comfort Cloud client aktualisiert."
@@ -85,6 +105,8 @@
85
105
  "keywords": ["air condition"],
86
106
  "license": "MIT",
87
107
  "platform": "Javascript/Node.js",
108
+ "connectionType": "cloud",
109
+ "dataSource": "poll",
88
110
  "main": "build/main.js",
89
111
  "icon": "panasonic-comfort-cloud.png",
90
112
  "enabled": true,
@@ -95,17 +117,51 @@
95
117
  "type": "climate-control",
96
118
  "compact": true,
97
119
  "materialize": true,
120
+ "globalDependencies": [
121
+ {
122
+ "admin": ">=4.0.9"
123
+ }
124
+ ],
98
125
  "dependencies": [
99
126
  {
100
- "js-controller": ">=1.4.2"
127
+ "js-controller": ">=3.0.0"
101
128
  }
102
129
  ]
103
130
  },
104
131
  "native": {
105
132
  "username": "",
106
- "password": ""
133
+ "password": "",
134
+ "refreshInterval": 5
107
135
  },
108
- "protectedNative": ["password"],
136
+ "protectedNative": [
137
+ "username",
138
+ "password"
139
+ ],
140
+ "encryptedNative": [
141
+ "password"
142
+ ],
109
143
  "objects": [],
110
- "instanceObjects": []
144
+ "instanceObjects": [
145
+ {
146
+ "_id": "info",
147
+ "type": "channel",
148
+ "common": {
149
+ "name": "Information"
150
+ },
151
+ "native": {}
152
+ },
153
+ {
154
+ "_id": "info.connection",
155
+ "type": "state",
156
+ "common": {
157
+ "role": "indicator.connected",
158
+ "name": "Device or service connected",
159
+ "type": "boolean",
160
+ "read": true,
161
+ "write": false,
162
+ "def": false
163
+ },
164
+ "native": {}
165
+ }
166
+ ]
111
167
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.panasonic-comfort-cloud",
3
- "version": "1.2.5",
3
+ "version": "2.0.0",
4
4
  "description": "Adapter for Panasonic Comfort Cloud",
5
5
  "author": {
6
6
  "name": "marc",
@@ -16,16 +16,14 @@
16
16
  "url": "https://github.com/marc2016/ioBroker.panasonic-comfort-cloud"
17
17
  },
18
18
  "dependencies": {
19
- "@iobroker/adapter-core": "^2.3.1",
19
+ "@iobroker/adapter-core": "^2.5.1",
20
20
  "@types/lodash": "^4.14.149",
21
- "@types/node-schedule": "^1.3.0",
22
21
  "lodash": "^4.17.15",
23
- "node-schedule": "^1.3.2",
24
- "panasonic-comfort-cloud-client": "^1.1.2",
22
+ "panasonic-comfort-cloud-client": "^1.1.4",
25
23
  "ts-enum-util": "^4.0.1"
26
24
  },
27
25
  "devDependencies": {
28
- "@iobroker/testing": "^1.3.0",
26
+ "@iobroker/testing": "^2.5.4",
29
27
  "@types/chai": "^4.2.4",
30
28
  "@types/chai-as-promised": "^7.1.2",
31
29
  "@types/gulp": "^4.0.6",
@@ -48,7 +46,7 @@
48
46
  "sinon-chai": "^3.3.0",
49
47
  "source-map-support": "^0.5.16",
50
48
  "ts-node": "^8.4.1",
51
- "typescript": "^3.8.3"
49
+ "typescript": "^4.5.4"
52
50
  },
53
51
  "main": "build/main.js",
54
52
  "scripts": {
@@ -60,7 +58,7 @@
60
58
  "test:ts": "mocha --opts test/mocha.custom.opts",
61
59
  "test:package": "mocha test/package --exit",
62
60
  "test:unit": "mocha test/unit --exit",
63
- "test:integration": "mocha test/integration --exit",
61
+ "test:integration": "mocha test/integration --timeout 120000 --exit",
64
62
  "test": "npm run test:ts && npm run test:package",
65
63
  "lint": "eslint --ext .ts src"
66
64
  },
@@ -1,32 +0,0 @@
1
- ---
2
- name: Bug report
3
- about: Something is not working as it should
4
- title: ''
5
- labels: ''
6
- assignees: ''
7
- ---
8
-
9
- **Describe the bug**
10
- A clear and concise description of what the bug is.
11
-
12
- **To Reproduce**
13
- Steps to reproduce the behavior:
14
- 1. Go to '...'
15
- 2. Click on '...'
16
- 3. Scroll down to '....'
17
- 4. See error
18
-
19
- **Expected behavior**
20
- A clear and concise description of what you expected to happen.
21
-
22
- **Screenshots & Logfiles**
23
- If applicable, add screenshots and logfiles to help explain your problem.
24
-
25
- **Versions:**
26
- - Adapter version: <adapter-version>
27
- - JS-Controller version: <js-controller-version> <!-- determine this with `iobroker -v` on the console -->
28
- - Node version: <node-version> <!-- determine this with `node -v` on the console -->
29
- - Operating system: <os-name>
30
-
31
- **Additional context**
32
- Add any other context about the problem here.
package/iob_npm.done DELETED
@@ -1 +0,0 @@
1
-