homebridge-withings-environment-data 1.2.0 → 1.3.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/CHANGELOG.md CHANGED
@@ -6,6 +6,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## [1.3.0] - 2026-08-03
10
+
11
+ ### Added
12
+ - Optional MQTT publishing: when "Publish to MQTT" is enabled and a broker
13
+ Host is configured, every successful poll publishes a retained message
14
+ to topic `withingsenv/ws-50` shaped like
15
+ `{"temperature": 25.2, "co2_levels": 674}`
16
+ - "Expose sensors as HomeKit Accessories" toggle (default on): when off,
17
+ the plugin still polls (and still publishes to MQTT, if enabled) but
18
+ doesn't create or update a HomeKit accessory
19
+
20
+ ### Changed
21
+ - Config UI reorganized: Name, then an expanded "Authentication" fieldset,
22
+ the Expose/Publish checkboxes, a collapsed "MQTT" fieldset, a collapsed
23
+ "Air Quality" fieldset (shorter field titles, fewer descriptions), Poll
24
+ Interval, No Response After Missed Polls, Stale Data Warning Threshold,
25
+ ntfy.sh Notification Topic (renamed from "ntfy Topic (optional)")
26
+ - `displayName` set in `package.json` so Homebridge Config UI shows
27
+ "Withings Environment Data" in the Plugins list instead of the raw npm
28
+ package name
29
+ - Shortened a few config field descriptions (No Response After Missed
30
+ Polls, Stale Data Warning Threshold, ntfy.sh Notification Topic)
31
+
32
+ ## [1.2.1] - 2026-08-02
33
+
34
+ ### Changed
35
+ - The stale-data log warning now logs on every poll while data remains
36
+ stale (matching how missed poll cycles are already logged), instead of
37
+ only once per stale streak.
38
+
39
+ ### Fixed
40
+ - The stale-data ntfy notification no longer re-fires every time
41
+ Homebridge restarts while the same stale reading is still the newest one
42
+ on record. The "already notified about this" state, and the last-known
43
+ reading date it applies to, are now both persisted to disk instead of
44
+ living only in memory (a first fix persisted only the former, which
45
+ still misfired if the first poll after a restart happened to fail), and
46
+ still reset correctly once a fresher reading comes in.
47
+
9
48
  ## [1.2.0] - 2026-07-31
10
49
 
11
50
  ### Added
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Homebridge Withings Environment Data v1.2.0
1
+ # Withings Environment Data v1.3.0
2
2
 
3
3
  **This Homebridge plugin has been 100% vibe coded with Claude.**
4
4
 
@@ -48,6 +48,17 @@ accessory:
48
48
  These appear automatically in the Home app after Homebridge restarts, no
49
49
  further setup needed. Readings update on the poll interval set below.
50
50
 
51
+ HomeKit exposure and MQTT publishing are independent and can each be
52
+ turned on or off in Configuration:
53
+
54
+ - **Expose sensors as HomeKit Accessories** (default on): when off, the
55
+ plugin still polls Withings (and still publishes to MQTT, if enabled),
56
+ but doesn't create or update any HomeKit accessory.
57
+ - **Publish to MQTT** (default off): when on, every successful poll
58
+ publishes a retained message to topic `withingsenv/ws-50` on the
59
+ configured broker, shaped like
60
+ `{"temperature": 25.2, "co2_levels": 674}`.
61
+
51
62
  ### Configuration
52
63
 
53
64
  All settings are entered through the Homebridge Config UI (Plugins tab,
@@ -69,15 +80,21 @@ Fields:
69
80
  matching the scale's own upload cadence).
70
81
  - **CO2 Detected Threshold (ppm)**: ppm above which the CarbonDioxideSensor
71
82
  reports "abnormal" (default 1000).
72
- - **Air Quality: Excellent/Good/Fair/Inferior below (ppm)**: the four ppm
73
- boundaries the AirQualitySensor category is based on (defaults 800, 1000,
74
- 1500, 2000).
83
+ - **Excellent/Good/Fair/Inferior below (ppm)**: the four ppm boundaries the
84
+ AirQualitySensor category is based on (defaults 800, 1000, 1500, 2000).
85
+ Anything above the Inferior boundary is reported as Poor.
86
+ - **Expose sensors as HomeKit Accessories**: see [Usage](#usage) above.
87
+ Default on.
88
+ - **Publish to MQTT / Host / Port / Username / Password**: see
89
+ [Usage](#usage) above. Publishing is off by default; Port defaults to
90
+ 1883. Username/Password are optional, for brokers that require auth.
75
91
  - **Stale Data Warning Threshold (hours)**: if the newest reading from the
76
92
  scale itself (not the plugin's poll) is older than this many hours — e.g.
77
- nobody's stood on the scale in a while — a warning is logged, the sensors
78
- show "No Response" in the Home app, and (if ntfy Topic is set) a
79
- notification is sent. Default 4. This is separate from poll failures.
80
- - **ntfy Topic (optional)**: if set, sends a push notification via
93
+ nobody's stood on the scale in a while — a warning is logged on every
94
+ poll for as long as it stays stale, the sensors show "No Response" in the
95
+ Home app, and (if ntfy Topic is set) a single notification is sent for
96
+ that stale reading. Default 4. This is separate from poll failures.
97
+ - **ntfy.sh Notification Topic** (optional): if set, sends a push notification via
81
98
  [ntfy.sh](https://ntfy.sh) to this topic the first time a poll fails
82
99
  (not repeated on every subsequent failure in the same streak; only once
83
100
  a poll succeeds again does the next failure trigger a fresh
@@ -12,6 +12,43 @@
12
12
  "default": "Withings Environment",
13
13
  "required": true
14
14
  },
15
+ "co2DetectedThresholdPpm": {
16
+ "title": "CO2 Detected Threshold (ppm)",
17
+ "type": "integer",
18
+ "default": 1000,
19
+ "minimum": 400
20
+ },
21
+ "airQualityExcellentMaxPpm": {
22
+ "title": "Excellent below (ppm)",
23
+ "type": "integer",
24
+ "default": 800,
25
+ "minimum": 1
26
+ },
27
+ "airQualityGoodMaxPpm": {
28
+ "title": "Good below (ppm)",
29
+ "type": "integer",
30
+ "default": 1000,
31
+ "minimum": 1
32
+ },
33
+ "airQualityFairMaxPpm": {
34
+ "title": "Fair below (ppm)",
35
+ "type": "integer",
36
+ "default": 1500,
37
+ "minimum": 1
38
+ },
39
+ "airQualityInferiorMaxPpm": {
40
+ "title": "Inferior below (ppm)",
41
+ "type": "integer",
42
+ "default": 2000,
43
+ "minimum": 1,
44
+ "description": "Anything above is reported as Poor."
45
+ },
46
+ "pollIntervalMinutes": {
47
+ "title": "Poll Interval (minutes)",
48
+ "type": "integer",
49
+ "default": 30,
50
+ "minimum": 5
51
+ },
15
52
  "email": {
16
53
  "title": "Withings Email",
17
54
  "type": "string",
@@ -37,65 +74,103 @@
37
74
  "required": true,
38
75
  "description": "The value of that same 2fa_token_<hash> cookie"
39
76
  },
40
- "pollIntervalMinutes": {
41
- "title": "Poll Interval (minutes)",
42
- "type": "integer",
43
- "default": 30,
44
- "minimum": 5
77
+ "exposeAsHomekitAccessories": {
78
+ "title": "Expose sensors as HomeKit Accessories",
79
+ "type": "boolean",
80
+ "default": true
45
81
  },
46
- "co2DetectedThresholdPpm": {
47
- "title": "CO2 Detected Threshold (ppm)",
48
- "type": "integer",
49
- "default": 1000,
50
- "minimum": 400
82
+ "publishToMqtt": {
83
+ "title": "Publish to MQTT",
84
+ "type": "boolean",
85
+ "default": false,
86
+ "description": "Publish sensor data to configured MQTT broker. Topic name: \"withingsenv/ws-50\""
51
87
  },
52
- "airQualityExcellentMaxPpm": {
53
- "title": "Air Quality: Excellent below (ppm)",
54
- "type": "integer",
55
- "default": 800,
56
- "minimum": 1,
57
- "description": "CO2 readings below this are reported as Excellent air quality."
88
+ "mqttHost": {
89
+ "title": "Host",
90
+ "type": "string",
91
+ "description": "Broker hostname or IP address."
58
92
  },
59
- "airQualityGoodMaxPpm": {
60
- "title": "Air Quality: Good below (ppm)",
93
+ "mqttPort": {
94
+ "title": "Port",
61
95
  "type": "integer",
62
- "default": 1000,
63
- "minimum": 1,
64
- "description": "CO2 readings below this are reported as Good."
96
+ "default": 1883
65
97
  },
66
- "airQualityFairMaxPpm": {
67
- "title": "Air Quality: Fair below (ppm)",
68
- "type": "integer",
69
- "default": 1500,
70
- "minimum": 1,
71
- "description": "CO2 readings below this are reported as Fair."
98
+ "mqttUsername": {
99
+ "title": "Username",
100
+ "type": "string"
72
101
  },
73
- "airQualityInferiorMaxPpm": {
74
- "title": "Air Quality: Inferior below (ppm)",
75
- "type": "integer",
76
- "default": 2000,
77
- "minimum": 1,
78
- "description": "CO2 readings below this are reported as Inferior. Anything above is reported as Poor."
102
+ "mqttPassword": {
103
+ "title": "Password",
104
+ "type": "string",
105
+ "x-schema-form": {
106
+ "type": "password"
107
+ }
79
108
  },
80
109
  "noResponseAfterMissedPolls": {
81
110
  "title": "No Response After Missed Polls",
82
111
  "type": "integer",
83
112
  "default": 2,
84
113
  "minimum": 0,
85
- "description": "Consecutive failed polls to tolerate (keeping the last known readings) before the Home app shows \"No Response\". 0 means show it immediately on any failure."
114
+ "description": "Consecutive failed polls to tolerate before the Home app shows \"No Response\"."
86
115
  },
87
116
  "staleDataWarningThresholdHours": {
88
117
  "title": "Stale Data Warning Threshold (hours)",
89
118
  "type": "integer",
90
119
  "default": 4,
91
120
  "minimum": 1,
92
- "description": "If the newest CO2/temperature reading from the scale itself is older than this many hours (e.g. nobody's stood on it), log a warning, show \"No Response\" in the Home app, and (if ntfy Topic is set) send a notification. This is separate from poll failures above."
121
+ "description": "If the newest CO2/temperature reading from the scale itself is older than this many hours, log a warning, show \"No Response\" in the Home app, and send a notification."
93
122
  },
94
123
  "ntfyTopic": {
95
- "title": "ntfy Topic (optional)",
124
+ "title": "ntfy.sh Notification Topic",
96
125
  "type": "string",
97
- "description": "If set, sends a push notification via ntfy.sh to this topic the first time a poll fails (not repeated on every subsequent failure in the same streak). Leave blank to disable."
126
+ "description": "If set, sends a push notification via ntfy.sh the first time a poll fails."
98
127
  }
99
128
  }
100
- }
129
+ },
130
+ "layout": [
131
+ "name",
132
+ {
133
+ "type": "fieldset",
134
+ "title": "Authentication",
135
+ "expandable": true,
136
+ "expanded": true,
137
+ "items": [
138
+ "email",
139
+ "password",
140
+ "trustCookieName",
141
+ "trustCookieValue"
142
+ ]
143
+ },
144
+ "exposeAsHomekitAccessories",
145
+ "publishToMqtt",
146
+ {
147
+ "type": "fieldset",
148
+ "title": "MQTT",
149
+ "expandable": true,
150
+ "expanded": false,
151
+ "items": [
152
+ "mqttHost",
153
+ "mqttPort",
154
+ "mqttUsername",
155
+ "mqttPassword"
156
+ ]
157
+ },
158
+ {
159
+ "type": "fieldset",
160
+ "title": "Air Quality",
161
+ "expandable": true,
162
+ "expanded": false,
163
+ "items": [
164
+ "co2DetectedThresholdPpm",
165
+ "airQualityExcellentMaxPpm",
166
+ "airQualityGoodMaxPpm",
167
+ "airQualityFairMaxPpm",
168
+ "airQualityInferiorMaxPpm"
169
+ ]
170
+ },
171
+ "pollIntervalMinutes",
172
+ "noResponseAfterMissedPolls",
173
+ "staleDataWarningThresholdHours",
174
+ "ntfyTopic"
175
+ ]
101
176
  }
package/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const mqtt = require('mqtt');
3
4
  const { login, resumeSession } = require('./lib/login');
4
5
  const { discoverDevice } = require('./lib/discover');
5
6
 
@@ -16,6 +17,7 @@ const APPPFM = 'web';
16
17
  const WINDOW_HOURS = 48;
17
18
 
18
19
  const PLATFORM_NAME = 'WithingsEnvironmentData';
20
+ const MQTT_TOPIC = 'withingsenv/ws-50';
19
21
 
20
22
  async function fetchLatest({ cookieHeader, sessionToken, deviceId, userId }) {
21
23
  const enddate = Math.floor(Date.now() / 1000);
@@ -103,12 +105,6 @@ class WithingsEnvironmentDataPlatform {
103
105
  // missed poll — reset once a poll succeeds again.
104
106
  this.hasNotifiedFailure = false;
105
107
 
106
- // Unix seconds of the newest reading actually seen so far (from the
107
- // Withings measurement itself, not from a successful poll) — used to
108
- // detect the scale going quiet rather than the plugin failing to poll.
109
- // Warn/notify once per stale streak, reset once a fresher reading comes in.
110
- this.lastReadingDate = null;
111
- this.staleDataWarned = false;
112
108
  this.warnedMissingReadingDate = false;
113
109
 
114
110
  // The long-lived (~1 week) session_key that lets us skip email/password/2FA
@@ -117,26 +113,48 @@ class WithingsEnvironmentDataPlatform {
117
113
  // it, since repeatedly hitting the password endpoint appears to be heavily
118
114
  // throttled by Withings.
119
115
  this.sessionStatePath = path.join(this.api.user.storagePath(), SESSION_STATE_FILENAME);
120
- this.sessionKey = this.loadSessionKey();
116
+ const state = this.loadPersistedState();
117
+ this.sessionKey = state.sessionKey ?? null;
118
+ // Unix seconds of the newest reading actually seen so far (from the Withings
119
+ // measurement itself, not from a successful poll) — used to detect the scale
120
+ // going quiet rather than the plugin failing to poll. Persisted and restored
121
+ // immediately on boot (rather than waiting on the first poll to repopulate
122
+ // it) so a restart can't be mistaken for "no reading yet" and wrongly clear
123
+ // an active stale-warning below.
124
+ this.lastReadingDate = state.lastReadingDate ?? null;
125
+ // The readingDate (unix seconds) we last sent a stale-data *notification* for,
126
+ // or null if not currently in a notified state. The log warning itself repeats
127
+ // every poll while data stays stale (like missed-poll-cycle failures do), but
128
+ // this gates the ntfy notification to once per distinct stale reading, and is
129
+ // persisted so a Homebridge restart doesn't re-notify about a reading we've
130
+ // already notified about — it only resets once a fresher reading comes in.
131
+ this.staleNotifiedForReadingDate = state.staleNotifiedForReadingDate ?? null;
121
132
 
122
133
  this.api.on('didFinishLaunching', () => this.discoverDevices());
123
134
  }
124
135
 
125
- loadSessionKey() {
136
+ loadPersistedState() {
126
137
  try {
127
138
  const raw = fs.readFileSync(this.sessionStatePath, 'utf8');
128
- return JSON.parse(raw).sessionKey ?? null;
139
+ return JSON.parse(raw);
129
140
  } catch (err) {
130
141
  if (err.code !== 'ENOENT') {
131
142
  this.log.warn(`Could not read session state file: ${err.message}`);
132
143
  }
133
- return null;
144
+ return {};
134
145
  }
135
146
  }
136
147
 
137
- saveSessionKey(sessionKey) {
148
+ persistState() {
138
149
  try {
139
- fs.writeFileSync(this.sessionStatePath, JSON.stringify({ sessionKey }));
150
+ fs.writeFileSync(
151
+ this.sessionStatePath,
152
+ JSON.stringify({
153
+ sessionKey: this.sessionKey,
154
+ lastReadingDate: this.lastReadingDate,
155
+ staleNotifiedForReadingDate: this.staleNotifiedForReadingDate,
156
+ })
157
+ );
140
158
  } catch (err) {
141
159
  this.log.warn(`Could not persist session state file: ${err.message}`);
142
160
  }
@@ -161,7 +179,7 @@ class WithingsEnvironmentDataPlatform {
161
179
  if (result.sessionKey) {
162
180
  if (result.sessionKey !== this.sessionKey) {
163
181
  this.sessionKey = result.sessionKey;
164
- this.saveSessionKey(result.sessionKey);
182
+ this.persistState();
165
183
  this.log.info('Withings full login succeeded; cached the new session for future polls.');
166
184
  } else {
167
185
  this.log.info('Withings full login succeeded; session token unchanged.');
@@ -182,15 +200,54 @@ class WithingsEnvironmentDataPlatform {
182
200
  const uuid = this.api.hap.uuid.generate('withings-environment-data');
183
201
  let accessory = this.accessories.find((acc) => acc.UUID === uuid);
184
202
 
185
- if (!accessory) {
186
- accessory = new this.api.platformAccessory(this.config.name || 'Withings Environment', uuid);
187
- this.api.registerPlatformAccessories('homebridge-withings-environment-data', PLATFORM_NAME, [accessory]);
203
+ if (this.getExposeAsHomekitAccessories()) {
204
+ if (!accessory) {
205
+ accessory = new this.api.platformAccessory(this.config.name || 'Withings Environment', uuid);
206
+ this.api.registerPlatformAccessories('homebridge-withings-environment-data', PLATFORM_NAME, [accessory]);
207
+ }
208
+ this.setupServices(accessory);
209
+ } else if (accessory) {
210
+ // Previously exposed, now toggled off — remove the stale accessory rather
211
+ // than leaving it registered but never updated.
212
+ this.api.unregisterPlatformAccessories('homebridge-withings-environment-data', PLATFORM_NAME, [accessory]);
188
213
  }
189
214
 
190
- this.setupServices(accessory);
215
+ this.connectMqtt();
191
216
  this.startPolling();
192
217
  }
193
218
 
219
+ getExposeAsHomekitAccessories() {
220
+ return this.config.exposeAsHomekitAccessories !== false;
221
+ }
222
+
223
+ getPublishToMqtt() {
224
+ return this.config.publishToMqtt === true;
225
+ }
226
+
227
+ connectMqtt() {
228
+ if (!this.getPublishToMqtt()) return;
229
+
230
+ if (!this.config.mqttHost) {
231
+ this.log.warn('Publish to MQTT is enabled but no MQTT Host is configured; MQTT publishing is disabled.');
232
+ return;
233
+ }
234
+
235
+ const port = Number(this.config.mqttPort);
236
+ const url = `mqtt://${this.config.mqttHost}:${Number.isFinite(port) && port > 0 ? port : 1883}`;
237
+ this.mqttClient = mqtt.connect(url, {
238
+ username: this.config.mqttUsername || undefined,
239
+ password: this.config.mqttPassword || undefined,
240
+ });
241
+ this.mqttClient.on('error', (err) => this.log.warn(`MQTT connection error: ${err.message}`));
242
+ this.api.on('shutdown', () => this.mqttClient.end());
243
+ }
244
+
245
+ publishMqttReading() {
246
+ if (!this.mqttClient) return;
247
+ const payload = JSON.stringify({ temperature: this.lastReading.temperature, co2_levels: this.lastReading.co2 });
248
+ this.mqttClient.publish(MQTT_TOPIC, payload, { retain: true });
249
+ }
250
+
194
251
  setupServices(accessory) {
195
252
  this.infoService = accessory.getService(this.Service.AccessoryInformation);
196
253
  if (this.infoService) {
@@ -354,16 +411,22 @@ class WithingsEnvironmentDataPlatform {
354
411
  async checkStaleData() {
355
412
  const stale = this.isDataStale();
356
413
 
357
- if (stale && !this.staleDataWarned) {
358
- this.staleDataWarned = true;
414
+ if (stale) {
359
415
  const { date, time } = this.formatStaleDateTime(this.lastReadingDate);
360
416
  this.log.warn(`Withings data is stale: last recorded at ${date} ${time}`);
361
- await this.sendNtfyNotification(
362
- `Temperature and/or CO2 readings haven't been updated since ${date} at ${time}.`,
363
- 'Homebridge: Withings Environment Data is out of date'
364
- );
365
- } else if (!stale && this.staleDataWarned) {
366
- this.staleDataWarned = false;
417
+
418
+ const alreadyNotifiedForThisReading = this.staleNotifiedForReadingDate === this.lastReadingDate;
419
+ if (!alreadyNotifiedForThisReading) {
420
+ this.staleNotifiedForReadingDate = this.lastReadingDate;
421
+ this.persistState();
422
+ await this.sendNtfyNotification(
423
+ `Temperature and/or CO2 readings haven't been updated since ${date} at ${time}.`,
424
+ 'Homebridge: Withings Environment Data is out of date'
425
+ );
426
+ }
427
+ } else if (this.staleNotifiedForReadingDate !== null) {
428
+ this.staleNotifiedForReadingDate = null;
429
+ this.persistState();
367
430
  this.log.info('Withings: fresh data has been recorded.');
368
431
  }
369
432
  }
@@ -411,34 +474,47 @@ class WithingsEnvironmentDataPlatform {
411
474
  'Withings measurement data has no recognizable date field; stale data detection is disabled until this is fixed.'
412
475
  );
413
476
  }
414
- } else if (readingDate !== null && readingDate !== undefined) {
477
+ } else if (readingDate !== null && readingDate !== undefined && readingDate !== this.lastReadingDate) {
415
478
  this.lastReadingDate = readingDate;
479
+ this.persistState();
416
480
  }
417
481
 
418
482
  if (co2 !== null && co2 !== undefined) {
419
483
  this.lastReading.co2 = co2;
420
484
  this.hasEverSucceeded = true;
421
- this.co2Service.updateCharacteristic(this.Characteristic.CarbonDioxideLevel, co2);
422
- this.co2Service.updateCharacteristic(
423
- this.Characteristic.CarbonDioxideDetected,
424
- co2 > co2Threshold
425
- ? this.Characteristic.CarbonDioxideDetected.CO2_LEVELS_ABNORMAL
426
- : this.Characteristic.CarbonDioxideDetected.CO2_LEVELS_NORMAL
427
- );
428
- this.airQualityService.updateCharacteristic(
429
- this.Characteristic.AirQuality,
430
- mapCo2ToAirQuality(co2, this.Characteristic.AirQuality, this.getAirQualityThresholds())
431
- );
485
+ if (this.co2Service) {
486
+ this.co2Service.updateCharacteristic(this.Characteristic.CarbonDioxideLevel, co2);
487
+ this.co2Service.updateCharacteristic(
488
+ this.Characteristic.CarbonDioxideDetected,
489
+ co2 > co2Threshold
490
+ ? this.Characteristic.CarbonDioxideDetected.CO2_LEVELS_ABNORMAL
491
+ : this.Characteristic.CarbonDioxideDetected.CO2_LEVELS_NORMAL
492
+ );
493
+ }
494
+ if (this.airQualityService) {
495
+ this.airQualityService.updateCharacteristic(
496
+ this.Characteristic.AirQuality,
497
+ mapCo2ToAirQuality(co2, this.Characteristic.AirQuality, this.getAirQualityThresholds())
498
+ );
499
+ }
432
500
  }
433
501
 
434
502
  if (temperature !== null && temperature !== undefined) {
435
503
  this.lastReading.temperature = temperature;
436
504
  this.hasEverSucceeded = true;
437
- this.temperatureService.updateCharacteristic(this.Characteristic.CurrentTemperature, temperature);
505
+ if (this.temperatureService) {
506
+ this.temperatureService.updateCharacteristic(this.Characteristic.CurrentTemperature, temperature);
507
+ }
508
+ }
509
+
510
+ if (hasReading) {
511
+ this.publishMqttReading();
438
512
  }
439
513
  }
440
514
 
441
515
  setFault(hasFault) {
516
+ if (!this.co2Service) return;
517
+
442
518
  const value = hasFault
443
519
  ? this.Characteristic.StatusFault.GENERAL_FAULT
444
520
  : this.Characteristic.StatusFault.NO_FAULT;
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-withings-environment-data",
3
- "version": "1.2.0",
3
+ "displayName": "Withings Environment Data",
4
+ "version": "1.3.0",
4
5
  "description": "Homebridge plugin exposing ambient CO2/air-quality and room temperature readings from a Withings WS-50 scale as HomeKit sensors",
5
6
  "main": "index.js",
6
7
  "files": [
@@ -23,6 +24,9 @@
23
24
  ],
24
25
  "author": "Luud Jacobs",
25
26
  "license": "MIT",
27
+ "dependencies": {
28
+ "mqtt": "^5.15.0"
29
+ },
26
30
  "homepage": "https://github.com/LuudJacobs/homebridge-withings-environment-data#readme",
27
31
  "bugs": {
28
32
  "url": "https://github.com/LuudJacobs/homebridge-withings-environment-data/issues"