@smarterhomeapp/homebridge-vantage 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Homebridge Vantage Controls
2
+ [![verified-by-homebridge](https://img.shields.io/badge/homebridge-verified-blueviolet?color=%23491F59&style=flat)](https://github.com/homebridge/homebridge/wiki/Verified-Plugins)
2
3
 
3
- A [Homebridge](https://homebridge.io/) plugin for Vantage Controls InFusion system.
4
+ A [Homebridge](https://homebridge.io/) plugin for Vantage Controls InFusion system.
5
+ This plugin replaces the one sold on smarterhome.io allowing anyone to download on the platform of their choice.
4
6
 
5
7
  ## Features
6
8
 
@@ -20,7 +22,7 @@ npm install -g homebridge
20
22
 
21
23
  2. Install this plugin:
22
24
  ```bash
23
- npm install -g homebridge-vantage
25
+ npm install -g @smarterhomeapp/homebridge-vantage
24
26
  ```
25
27
 
26
28
  3. Add the platform to your `config.json`:
@@ -74,7 +76,7 @@ npm install -g homebridge-vantage
74
76
 
75
77
  1. Clone the repository:
76
78
  ```bash
77
- git clone https://github.com/yourusername/homebridge-vantage.git
79
+ git clone https://github.com/smarterhomeapp/homebridge-vantage.git
78
80
  cd homebridge-vantage
79
81
  ```
80
82
 
@@ -125,5 +127,5 @@ MIT License - see LICENSE file for details.
125
127
  ## Support
126
128
 
127
129
  For issues and questions:
128
- - [GitHub Issues](https://github.com/yourusername/homebridge-vantage/issues)
129
- - [Homebridge Community](https://github.com/homebridge/homebridge)
130
+ - [GitHub Issues](https://github.com/smarterhomeapp/homebridge-vantage/issues)
131
+ - [Homebridge Community](https://github.com/homebridge/homebridge)
package/dist/platform.js CHANGED
@@ -219,7 +219,7 @@ class VantagePlatform {
219
219
  const svc = acc.getService(S.Thermostat);
220
220
  if (!svc || !dev)
221
221
  return;
222
- this.log.info(`thermostatIndoorModeChange (VID=${vid}, Name=${dev.name}, Mode=${mode}, TargetTemp=${targetTemp})`);
222
+ this.log.debug(`thermostatIndoorModeChange (VID=${vid}, Name=${dev.name}, Mode=${mode}, TargetTemp=${targetTemp})`);
223
223
  // this.log.info(`Before update - dev.mode=${dev.mode}, dev.targetTemp=${dev.targetTemp}, dev.heating=${dev.heating}, dev.cooling=${dev.cooling}`);
224
224
  if (targetTemp === -1) {
225
225
  // Only update mode when targetTemp is -1 (mode change without temperature)
@@ -1,2 +1,2 @@
1
- export declare const PLUGIN_NAME = "homebridge-vantage";
1
+ export declare const PLUGIN_NAME = "@smarterhomeapp/homebridge-vantage";
2
2
  export declare const PLATFORM_NAME = "VantageControls";
package/dist/settings.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PLATFORM_NAME = exports.PLUGIN_NAME = void 0;
4
- exports.PLUGIN_NAME = 'homebridge-vantage';
4
+ exports.PLUGIN_NAME = '@smarterhomeapp/homebridge-vantage';
5
5
  exports.PLATFORM_NAME = 'VantageControls';
@@ -29,6 +29,10 @@ export declare class VantageInfusion extends EventEmitter {
29
29
  /** XML/config discovery; returns devices (replaces old Discover/DiscoverSSL) */
30
30
  discoverDevices(): Promise<VantageDevice[]>;
31
31
  private processConfigurationXml;
32
+ /** Escape text for XML (same as sanitize in index.js). */
33
+ private static escapeXml;
34
+ /** Build cache XML from readObjects (same structure + sanitization as original index.js). */
35
+ private buildCacheXml;
32
36
  private pullConfigurationXml;
33
37
  setBrightness(vid: string, level: number): Promise<void>;
34
38
  setHue(vid: string, hue: number): Promise<void>;
@@ -48,6 +48,7 @@ const TYPE_THERMO = [
48
48
  'Tekmar.tN4_Gateway_482_Zone_-_Slab_Only_CHILD',
49
49
  'Tekmar.tN4_Gateway_482_Zone_CHILD',
50
50
  'Legrand.MH_HVAC_Control_CHILD',
51
+ 'Vantage.HVAC-IU-Zone_CHILD'
51
52
  ];
52
53
  const TYPE_BLIND = [
53
54
  'Blind',
@@ -93,8 +94,13 @@ class VantageInfusion extends events_1.EventEmitter {
93
94
  this.isInsecureCfg = false;
94
95
  }
95
96
  else {
96
- this.isInsecureCmd = await this.portUsable(3001, 'STATUS ALL\n');
97
- this.isInsecureCfg = await this.portUsable(2001, '<IIntrospection><GetInterfaces><call></call></GetInterfaces></IIntrospection>\n');
97
+ // Probe sequentially to avoid controller session limits
98
+ const cmdResult = await this.portUsable(3001, 'STATUS ALL\n');
99
+ // brief backoff before probing config port
100
+ await new Promise((r) => setTimeout(r, 150));
101
+ const cfgResult = await this.portUsable(2001, '<IIntrospection><GetInterfaces><call></call></GetInterfaces></IIntrospection>\n');
102
+ this.isInsecureCmd = !!cmdResult;
103
+ this.isInsecureCfg = !!cfgResult;
98
104
  }
99
105
  // Legacy-style port logs
100
106
  this.opts.log.info(this.isInsecureCmd ? 'Using insecure port: 3001' : 'Using SSL port: 3010');
@@ -104,21 +110,46 @@ class VantageInfusion extends events_1.EventEmitter {
104
110
  portUsable(port, probe) {
105
111
  return new Promise((resolve) => {
106
112
  let sawData = false;
113
+ this.opts.log.debug(`Testing ${this.opts.ipaddress}:${port} with probe: "${probe.trim()}"`);
107
114
  const sock = net.connect({ host: this.opts.ipaddress, port }, () => {
115
+ this.opts.log.debug(`Connected to ${this.opts.ipaddress}:${port}`);
116
+ // improve send behavior
117
+ sock.setNoDelay(true);
118
+ // keep a post-connect timeout; fail if no reply in time
119
+ sock.setTimeout(8000);
120
+ // write immediately
121
+ this.opts.log.debug(`Sending initial probe on port ${port}`);
108
122
  sock.write(probe);
123
+ // send a second probe shortly after to coax a response
124
+ setTimeout(() => {
125
+ if (!sawData) {
126
+ this.opts.log.debug(`Re-sending probe on port ${port}`);
127
+ sock.write(probe);
128
+ }
129
+ }, 150);
130
+ });
131
+ sock.once('connect', () => {
132
+ this.opts.log.debug(`Port ${port} connection established`);
109
133
  });
110
- sock.setTimeout(5000, () => sock.destroy());
111
- sock.once('connect', () => sock.setTimeout(0));
112
- sock.on('data', () => {
134
+ sock.on('data', (data) => {
135
+ const text = data.toString();
136
+ this.opts.log.debug(`Port ${port} received data: ${text.slice(0, 200).replace(/\r/g, '\\r').replace(/\n/g, '\\n')}`);
113
137
  sawData = true;
114
138
  resolve(true);
115
139
  sock.destroy();
116
140
  });
141
+ sock.on('timeout', () => {
142
+ this.opts.log.debug(`Port ${port} post-connect timeout`);
143
+ sock.destroy();
144
+ });
117
145
  sock.on('close', () => {
146
+ this.opts.log.debug(`Port ${port} closed, sawData: ${sawData}`);
118
147
  if (!sawData)
119
148
  resolve(false);
120
149
  });
121
- sock.on('error', () => {
150
+ sock.on('error', (err) => {
151
+ const msg = err instanceof Error ? `${err.code || ''} ${err.message}` : String(err);
152
+ this.opts.log.debug(`Port ${port} error: ${msg}`);
122
153
  /* resolve on close path */
123
154
  });
124
155
  });
@@ -383,6 +414,25 @@ class VantageInfusion extends events_1.EventEmitter {
383
414
  }
384
415
  return filtered;
385
416
  }
417
+ /** Escape text for XML (same as sanitize in index.js). */
418
+ static escapeXml(s) {
419
+ return String(s)
420
+ .replace(/&/g, '&amp;')
421
+ .replace(/</g, '&lt;')
422
+ .replace(/>/g, '&gt;')
423
+ .replace(/"/g, '&quot;')
424
+ .replace(/'/g, '&apos;');
425
+ }
426
+ /** Build cache XML from readObjects (same structure + sanitization as original index.js). */
427
+ buildCacheXml(readObjects) {
428
+ const frag = readObjects.map((o) => {
429
+ const k = Object.keys(o)[0];
430
+ const v = o[k];
431
+ const body = Object.keys(v).map((kk) => `<${kk}>${VantageInfusion.escapeXml(String(v[kk]))}</${kk}>`).join('');
432
+ return `<Object><${k}>${body}</${k}></Object>`;
433
+ }).join('');
434
+ return `<Project><Objects>${frag}</Objects></Project>`;
435
+ }
386
436
  pullConfigurationXml() {
387
437
  return new Promise((resolve) => {
388
438
  const finishOnce = (socket, xml) => {
@@ -426,15 +476,7 @@ class VantageInfusion extends events_1.EventEmitter {
426
476
  return;
427
477
  }
428
478
  if (readObjects.length && Date.now() - lastProgress > 2000) {
429
- // Build minimal XML with what we gathered
430
- const xml = '<Project><Objects>' +
431
- readObjects.map((o) => {
432
- const k = Object.keys(o)[0];
433
- const v = o[k];
434
- const body = Object.keys(v).map((kk) => `<${kk}>${String(v[kk])}</${kk}>`).join('');
435
- return `<Object><${k}>${body}</${k}></Object>`;
436
- }).join('') +
437
- '</Objects></Project>';
479
+ const xml = this.buildCacheXml(readObjects);
438
480
  clearInterval(watchdog);
439
481
  finishOnce(socket, xml);
440
482
  }
@@ -542,14 +584,7 @@ class VantageInfusion extends events_1.EventEmitter {
542
584
  }
543
585
  // ----- END CONDITION -----
544
586
  if (shouldBreak) {
545
- const xml = '<Project><Objects>' +
546
- readObjects.map((o) => {
547
- const k = Object.keys(o)[0];
548
- const v = o[k];
549
- const body = Object.keys(v).map((kk) => `<${kk}>${String(v[kk])}</${kk}>`).join('');
550
- return `<Object><${k}>${body}</${k}></Object>`;
551
- }).join('') +
552
- '</Objects></Project>';
587
+ const xml = this.buildCacheXml(readObjects);
553
588
  clearLoginFallback();
554
589
  clearInterval(watchdog);
555
590
  finishOnce(socket, xml);
@@ -559,30 +594,16 @@ class VantageInfusion extends events_1.EventEmitter {
559
594
  socket.on('end', () => {
560
595
  this.opts.log.info("ending" + shouldBreak);
561
596
  if (!finishOnce._done && readObjects.length) {
562
- const xml = '<Project><Objects>' +
563
- readObjects.map((o) => {
564
- const k = Object.keys(o)[0];
565
- const v = o[k];
566
- const body = Object.keys(v).map((kk) => `<${kk}>${String(v[kk])}</${kk}>`).join('');
567
- return `<Object><${k}>${body}</${k}></Object>`;
568
- }).join('') +
569
- '</Objects></Project>';
597
+ const xml = this.buildCacheXml(readObjects);
570
598
  clearLoginFallback();
571
599
  clearInterval(watchdog);
572
600
  finishOnce(socket, xml);
573
601
  }
574
602
  });
575
603
  socket.on('close', () => {
576
- this.opts.log.info("closing" + shouldBreak);
604
+ this.opts.log.debug("closing " + shouldBreak);
577
605
  if (!finishOnce._done && readObjects.length) {
578
- const xml = '<Project><Objects>' +
579
- readObjects.map((o) => {
580
- const k = Object.keys(o)[0];
581
- const v = o[k];
582
- const body = Object.keys(v).map((kk) => `<${kk}>${String(v[kk])}</${kk}>`).join('');
583
- return `<Object><${k}>${body}</${k}></Object>`;
584
- }).join('') +
585
- '</Objects></Project>';
606
+ const xml = this.buildCacheXml(readObjects);
586
607
  clearLoginFallback();
587
608
  clearInterval(watchdog);
588
609
  finishOnce(socket, xml);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smarterhomeapp/homebridge-vantage",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Homebridge plugin for Vantage InFusion (dynamic platform, HB 1.6+ / 2.0 ready)",
5
5
  "license": "MIT",
6
6
  "keywords": [