homebridge-viessmann-vicare 2.0.6 β†’ 2.0.7

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.
@@ -114,86 +114,10 @@ export declare class AuthManager {
114
114
  }
115
115
  }
116
116
 
117
- private openBrowser(url: string): void {
118
- const { execFile, exec } = require('child_process');
119
117
 
120
- const tryExecFile = (cmd: string, args: string[], env: NodeJS.ProcessEnv) =>
121
- new Promise<void>((resolve, reject) => {
122
- execFile(cmd, args, { env }, (err: any) => err ? reject(err) : resolve());
123
- });
124
-
125
- const tryExec = (command: string, env: NodeJS.ProcessEnv) =>
126
- new Promise<void>((resolve, reject) => {
127
- exec(command, { env }, (err: any) => err ? reject(err) : resolve());
128
- });
129
-
130
- // Copia pulita dell'ambiente
131
- const env: NodeJS.ProcessEnv = { ...process.env };
132
-
133
- // Migliora compatibilitΓ  Linux (X/Wayland) senza toccare il servizio
134
- if (process.platform === 'linux') {
135
- if (!env.DISPLAY) {
136
- // fallback tipico X11
137
- env.DISPLAY = ':0';
138
- }
139
- if (!env.XDG_RUNTIME_DIR && typeof process.getuid === 'function') {
140
- env.XDG_RUNTIME_DIR = `/run/user/${process.getuid()}`;
141
- }
142
- }
143
-
144
-
145
- const tryLinux = async () => {
146
- // 1) Standard freedesktop
147
- try { await tryExecFile('xdg-open', [url], env); this.log.debug('🌐 xdg-open OK'); return; } catch {}
148
-
149
- // 2) Gio / GVFS
150
- try { await tryExec(`gio open "${url}"`, env); this.log.debug('🌐 gio open OK'); return; } catch {}
151
-
152
- // 3) Debian-like helpers
153
- try { await tryExecFile('sensible-browser', [url], env); this.log.debug('🌐 sensible-browser OK'); return; } catch {}
154
- try { await tryExecFile('x-www-browser', [url], env); this.log.debug('🌐 x-www-browser OK'); return; } catch {}
155
-
156
- // 4) xdg-desktop-portal (DBus OpenURI) β€” compatibile Wayland/X
157
- try {
158
- await tryExec(
159
- `gdbus call --session ` +
160
- `--dest org.freedesktop.portal.Desktop ` +
161
- `--object-path /org/freedesktop/portal/desktop ` +
162
- `--method org.freedesktop.portal.OpenURI.OpenURI "" "${url}" "{}"`,
163
- env
164
- );
165
- this.log.debug('🌐 xdg-desktop-portal OpenURI OK');
166
- return;
167
- } catch {}
168
-
169
- // 5) backend specifici (best-effort, non bloccanti)
170
- try { await tryExecFile('kde-open5', [url], env); this.log.debug('🌐 kde-open5 OK'); return; } catch {}
171
- try { await tryExecFile('gnome-open', [url], env); this.log.debug('🌐 gnome-open OK'); return; } catch {}
172
-
173
- // Nessun metodo disponibile/riuscito
174
- throw new Error('No Linux opener succeeded');
175
- };
176
-
177
- (async () => {
178
- try {
179
- switch (process.platform) {
180
- case 'darwin':
181
- await tryExec(`open "${url}"`, env);
182
- break;
183
- case 'win32':
184
- await tryExec(`start "" "${url}"`, env);
185
- break;
186
- default:
187
- await tryLinux();
188
- }
189
- this.log.info('βœ… Tentativo di apertura browser completato');
190
- } catch (e: any) {
191
- this.log.warn(`⚠️ Impossibile aprire automaticamente il browser (${e?.message || e}). Apri l’URL manualmente.`);
192
- }
193
- })();
194
- }
195
118
  **/
196
119
  private openBrowser;
120
+ private tryOpenBrowserDirect;
197
121
  private exchangeCodeForTokens;
198
122
  refreshAccessToken(): Promise<void>;
199
123
  private setTokens;
@@ -1 +1 @@
1
- {"version":3,"file":"auth-manager.d.ts","sourceRoot":"","sources":["../src/auth-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth-manager.d.ts","sourceRoot":"","sources":["../src/auth-manager.ts"],"names":[],"mappings":"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"}
@@ -422,41 +422,56 @@ class AuthManager {
422
422
  async performFullAuth() {
423
423
  return new Promise((resolve, reject) => {
424
424
  const authUrl = this.buildAuthUrl();
425
- this.log.info('='.repeat(80));
426
- this.log.info('πŸ” VIESSMANN OAUTH AUTHENTICATION');
427
- this.log.info('='.repeat(80));
428
- this.log.info('⚠️ CRITICAL: You have only 20 seconds after browser authorization!');
429
- this.log.info('');
430
- this.log.info('πŸ”— Please open this URL in your browser to authenticate:');
431
- this.log.info('');
432
- this.log.info(authUrl);
433
- this.log.info('');
434
- this.log.info('⏳ Waiting for authentication callback...');
435
- const authTimeout = this.config.authTimeout || 300000;
436
- this.log.info(`⏰ This process will timeout after ${authTimeout / 1000} seconds.`);
437
- this.log.info('='.repeat(80));
425
+ const isSystemdService = !!(process.env.SYSTEMD_EXEC_PID || process.env.INVOCATION_ID);
426
+ if (isSystemdService) {
427
+ // Messaggio semplificato per servizi
428
+ this.log.info('='.repeat(80));
429
+ this.log.info('πŸ” VIESSMANN AUTHENTICATION REQUIRED');
430
+ this.log.info('='.repeat(80));
431
+ }
432
+ else {
433
+ // Messaggio dettagliato per installazioni desktop
434
+ this.log.info('='.repeat(80));
435
+ this.log.info('πŸ” VIESSMANN OAUTH AUTHENTICATION');
436
+ this.log.info('='.repeat(80));
437
+ this.log.info('⚠️ CRITICAL: You have only 20 seconds after browser authorization!');
438
+ this.log.info('');
439
+ }
438
440
  // Set timeout for authentication process
441
+ const authTimeout = this.config.authTimeout || 300000;
439
442
  this.authTimeout = setTimeout(() => {
440
443
  this.stopAuthServer();
441
444
  reject(new Error(`Authentication timeout - no response received within ${authTimeout / 1000} seconds`));
442
445
  }, authTimeout);
443
446
  // Start local server to capture callback
444
447
  this.startAuthServer((code, error) => {
445
- if (this.authTimeout) {
446
- clearTimeout(this.authTimeout);
447
- this.authTimeout = undefined;
448
- }
449
448
  if (error) {
449
+ if (this.authTimeout) {
450
+ clearTimeout(this.authTimeout);
451
+ this.authTimeout = undefined;
452
+ }
450
453
  reject(error);
451
454
  return;
452
455
  }
453
456
  if (code) {
454
457
  this.exchangeCodeForTokens(code)
455
- .then(() => resolve())
456
- .catch(reject);
458
+ .then(() => {
459
+ if (this.authTimeout) {
460
+ clearTimeout(this.authTimeout);
461
+ this.authTimeout = undefined;
462
+ }
463
+ resolve();
464
+ })
465
+ .catch((err) => {
466
+ if (this.authTimeout) {
467
+ clearTimeout(this.authTimeout);
468
+ this.authTimeout = undefined;
469
+ }
470
+ reject(err);
471
+ });
457
472
  }
458
473
  });
459
- // Auto-open browser if possible
474
+ // Try to open browser (will show URL if systemd service)
460
475
  this.openBrowser(authUrl);
461
476
  });
462
477
  }
@@ -472,10 +487,13 @@ class AuthManager {
472
487
  return `${this.authURL}/authorize?${params.toString()}`;
473
488
  }
474
489
  startAuthServer(callback) {
490
+ // Verifica se server Γ¨ giΓ  attivo
491
+ if (this.authServer && this.authServer.listening) {
492
+ this.log.debug('🟒 Auth server already listening, reusing existing server');
493
+ return;
494
+ }
475
495
  this.authServer = http.createServer((req, res) => {
476
496
  const url = new URL(req.url, `http://localhost:${this.config.redirectPort || 4200}`);
477
- this.log.warn(`🟒 Auth server listening on http://${this.hostIp}:${this.config.redirectPort || 4200}/`);
478
- this.log.warn(`πŸ‘‰ If the browser does not open automatically, open this URL manually from another device:\nhttp://${this.hostIp}:${this.config.redirectPort || 4200}/login`);
479
497
  if (url.pathname === '/') {
480
498
  const code = url.searchParams.get('code');
481
499
  const error = url.searchParams.get('error');
@@ -483,42 +501,60 @@ class AuthManager {
483
501
  const errorDescription = url.searchParams.get('error_description') || error;
484
502
  res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
485
503
  res.end(`
486
- <html>
487
- <head><title>Authentication Failed</title></head>
488
- <body style="font-family: -apple-system, sans-serif; text-align: center; padding: 50px;">
489
- <h1 style="color: #ff4757;">❌ Authentication Failed</h1>
490
- <p>${errorDescription}</p>
491
- <p>Please close this window and check your Homebridge logs.</p>
492
- </body>
493
- </html>
494
- `);
504
+ <html>
505
+ <head><title>Authentication Failed</title></head>
506
+ <body style="font-family: -apple-system, sans-serif; text-align: center; padding: 50px;">
507
+ <h1 style="color: #ff4757;">❌ Authentication Failed</h1>
508
+ <p>${errorDescription}</p>
509
+ <p>Please close this window and check your Homebridge logs.</p>
510
+ </body>
511
+ </html>
512
+ `);
495
513
  callback(undefined, new Error(`OAuth error: ${errorDescription}`));
496
- //this.stopAuthServer();
514
+ // πŸ†• Chiudi dopo un delay per far vedere la pagina
515
+ setTimeout(() => this.stopAuthServer(), 3000);
497
516
  return;
498
517
  }
499
518
  if (code) {
500
519
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
501
520
  res.end(`
502
- <html>
503
- <head><title>Authentication Successful</title></head>
504
- <body style="font-family: -apple-system, sans-serif; text-align: center; padding: 50px;">
505
- <h1 style="color: #2ed573;">βœ… Authentication Successful!</h1>
506
- <p>You can now close this window.</p>
507
- <p>Homebridge will continue setup automatically.</p>
508
- </body>
509
- </html>
510
- `);
521
+ <html>
522
+ <head><title>Authentication Successful</title></head>
523
+ <body style="font-family: -apple-system, sans-serif; text-align: center; padding: 50px;">
524
+ <h1 style="color: #2ed573;">βœ… Authentication Successful!</h1>
525
+ <p>You can now close this window.</p>
526
+ <p>Homebridge will continue setup automatically.</p>
527
+ </body>
528
+ </html>
529
+ `);
511
530
  callback(code);
512
- //this.stopAuthServer();
531
+ // πŸ†• Chiudi dopo un delay per far vedere la pagina
532
+ setTimeout(() => this.stopAuthServer(), 3000);
513
533
  return;
514
534
  }
515
535
  }
516
- // Handle other requests
536
+ // Handle other requests - root path senza parametri
537
+ if (url.pathname === '/') {
538
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
539
+ res.end(`
540
+ <html>
541
+ <head><title>Viessmann Authentication</title></head>
542
+ <body style="font-family: -apple-system, sans-serif; text-align: center; padding: 50px;">
543
+ <h1>πŸ” Viessmann Authentication</h1>
544
+ <p>Waiting for authentication callback...</p>
545
+ <p>If you see this page, the authentication server is working correctly.</p>
546
+ <p style="color: #666; font-size: 14px;">Server: ${this.hostIp}:${this.config.redirectPort || 4200}</p>
547
+ </body>
548
+ </html>
549
+ `);
550
+ return;
551
+ }
552
+ // 404 per altre richieste
517
553
  res.writeHead(404, { 'Content-Type': 'text/plain' });
518
554
  res.end('Not found');
519
555
  });
520
556
  this.authServer.listen(this.config.redirectPort || 4200, '0.0.0.0', () => {
521
- this.log.debug(`🌐 Auth server listening on ${this.hostIp}:${this.config.redirectPort || 4200}`);
557
+ this.log.info(`🟒 Auth server ready at: http://${this.hostIp}:${this.config.redirectPort || 4200}/`);
522
558
  });
523
559
  this.authServer.on('error', (error) => {
524
560
  this.log.error('❌ Auth server error:', error);
@@ -571,285 +607,57 @@ class AuthManager {
571
607
  }
572
608
  }
573
609
 
574
- private openBrowser(url: string): void {
575
- const { execFile, exec } = require('child_process');
576
610
 
577
- const tryExecFile = (cmd: string, args: string[], env: NodeJS.ProcessEnv) =>
578
- new Promise<void>((resolve, reject) => {
579
- execFile(cmd, args, { env }, (err: any) => err ? reject(err) : resolve());
580
- });
581
-
582
- const tryExec = (command: string, env: NodeJS.ProcessEnv) =>
583
- new Promise<void>((resolve, reject) => {
584
- exec(command, { env }, (err: any) => err ? reject(err) : resolve());
585
- });
586
-
587
- // Copia pulita dell'ambiente
588
- const env: NodeJS.ProcessEnv = { ...process.env };
589
-
590
- // Migliora compatibilitΓ  Linux (X/Wayland) senza toccare il servizio
591
- if (process.platform === 'linux') {
592
- if (!env.DISPLAY) {
593
- // fallback tipico X11
594
- env.DISPLAY = ':0';
595
- }
596
- if (!env.XDG_RUNTIME_DIR && typeof process.getuid === 'function') {
597
- env.XDG_RUNTIME_DIR = `/run/user/${process.getuid()}`;
598
- }
599
- }
600
-
601
-
602
- const tryLinux = async () => {
603
- // 1) Standard freedesktop
604
- try { await tryExecFile('xdg-open', [url], env); this.log.debug('🌐 xdg-open OK'); return; } catch {}
605
-
606
- // 2) Gio / GVFS
607
- try { await tryExec(`gio open "${url}"`, env); this.log.debug('🌐 gio open OK'); return; } catch {}
608
-
609
- // 3) Debian-like helpers
610
- try { await tryExecFile('sensible-browser', [url], env); this.log.debug('🌐 sensible-browser OK'); return; } catch {}
611
- try { await tryExecFile('x-www-browser', [url], env); this.log.debug('🌐 x-www-browser OK'); return; } catch {}
612
-
613
- // 4) xdg-desktop-portal (DBus OpenURI) β€” compatibile Wayland/X
614
- try {
615
- await tryExec(
616
- `gdbus call --session ` +
617
- `--dest org.freedesktop.portal.Desktop ` +
618
- `--object-path /org/freedesktop/portal/desktop ` +
619
- `--method org.freedesktop.portal.OpenURI.OpenURI "" "${url}" "{}"`,
620
- env
621
- );
622
- this.log.debug('🌐 xdg-desktop-portal OpenURI OK');
623
- return;
624
- } catch {}
625
-
626
- // 5) backend specifici (best-effort, non bloccanti)
627
- try { await tryExecFile('kde-open5', [url], env); this.log.debug('🌐 kde-open5 OK'); return; } catch {}
628
- try { await tryExecFile('gnome-open', [url], env); this.log.debug('🌐 gnome-open OK'); return; } catch {}
629
-
630
- // Nessun metodo disponibile/riuscito
631
- throw new Error('No Linux opener succeeded');
632
- };
633
-
634
- (async () => {
635
- try {
636
- switch (process.platform) {
637
- case 'darwin':
638
- await tryExec(`open "${url}"`, env);
639
- break;
640
- case 'win32':
641
- await tryExec(`start "" "${url}"`, env);
642
- break;
643
- default:
644
- await tryLinux();
645
- }
646
- this.log.info('βœ… Tentativo di apertura browser completato');
647
- } catch (e: any) {
648
- this.log.warn(`⚠️ Impossibile aprire automaticamente il browser (${e?.message || e}). Apri l’URL manualmente.`);
649
- }
650
- })();
651
- }
652
611
  **/
653
612
  openBrowser(url) {
654
- const { execFile, exec } = require('child_process');
655
- // Ambiente pulito senza toccare DISPLAY
656
- const env = { ...process.env };
657
- // Helper per eseguire comandi con timeout
658
- const execWithTimeout = (command, args = [], timeout = 5000) => {
659
- return new Promise((resolve, reject) => {
660
- const child = args.length > 0
661
- ? execFile(command, args, { env, timeout })
662
- : exec(command, { env, timeout });
663
- child.on('error', reject);
664
- child.on('exit', (code) => {
665
- if (code === 0)
666
- resolve();
667
- else
668
- reject(new Error(`Exit code ${code}`));
669
- });
670
- });
671
- };
672
- const detectDesktopEnvironment = () => {
673
- const xdg = env.XDG_CURRENT_DESKTOP?.toLowerCase() || '';
674
- const session = env.DESKTOP_SESSION?.toLowerCase() || '';
675
- const gdmsession = env.GDMSESSION?.toLowerCase() || '';
676
- // Controlla tutti i possibili indicatori
677
- const indicators = [xdg, session, gdmsession].join(':');
678
- if (indicators.includes('gnome'))
679
- return 'gnome';
680
- if (indicators.includes('kde') || indicators.includes('plasma'))
681
- return 'kde';
682
- if (indicators.includes('xfce'))
683
- return 'xfce';
684
- if (indicators.includes('lxde') || indicators.includes('lxqt'))
685
- return 'lxde';
686
- if (indicators.includes('mate'))
687
- return 'mate';
688
- if (indicators.includes('cinnamon'))
689
- return 'cinnamon';
690
- if (indicators.includes('labwc') || indicators.includes('wlroots'))
691
- return 'wayland';
692
- if (indicators.includes('wayfire') || indicators.includes('sway'))
693
- return 'wayland';
694
- return 'unknown';
695
- };
696
- const tryLinuxMethods = async () => {
697
- const desktop = detectDesktopEnvironment();
698
- this.log.debug(`πŸ–₯️ Desktop environment detected: ${desktop} (XDG_CURRENT_DESKTOP=${env.XDG_CURRENT_DESKTOP})`);
699
- // 1. Metodo universale - xdg-open (funziona con tutti i DE moderni)
700
- try {
701
- await execWithTimeout('xdg-open', [url]);
702
- this.log.info('βœ… Browser aperto con xdg-open');
703
- return true;
704
- }
705
- catch { }
706
- // 2. Metodi specifici per desktop environment
707
- switch (desktop) {
708
- case 'gnome':
709
- try {
710
- await execWithTimeout('gnome-open', [url]);
711
- this.log.info('βœ… Browser aperto con gnome-open');
712
- return true;
713
- }
714
- catch { }
715
- try {
716
- await execWithTimeout('gio', ['open', url]);
717
- this.log.info('βœ… Browser aperto con gio open');
718
- return true;
719
- }
720
- catch { }
721
- break;
722
- case 'kde':
723
- try {
724
- await execWithTimeout('kde-open5', [url]);
725
- this.log.info('βœ… Browser aperto con kde-open5');
726
- return true;
727
- }
728
- catch { }
729
- try {
730
- await execWithTimeout('kde-open', [url]);
731
- this.log.info('βœ… Browser aperto con kde-open');
732
- return true;
733
- }
734
- catch { }
735
- break;
736
- case 'xfce':
737
- try {
738
- await execWithTimeout('exo-open', [url]);
739
- this.log.info('βœ… Browser aperto con exo-open');
740
- return true;
741
- }
742
- catch { }
743
- break;
744
- case 'lxde':
745
- try {
746
- await execWithTimeout('pcmanfm', [url]);
747
- this.log.info('βœ… Browser aperto con pcmanfm');
748
- return true;
749
- }
750
- catch { }
751
- break;
752
- case 'mate':
753
- try {
754
- await execWithTimeout('mate-open', [url]);
755
- this.log.info('βœ… Browser aperto con mate-open');
756
- return true;
757
- }
758
- catch { }
759
- break;
760
- case 'cinnamon':
761
- try {
762
- await execWithTimeout('cinnamon-open', [url]);
763
- this.log.info('βœ… Browser aperto con cinnamon-open');
764
- return true;
765
- }
766
- catch { }
767
- break;
768
- case 'wayland':
769
- // Portal DBus (metodo nativo Wayland - funziona con labwc)
770
- try {
771
- const escapedUrl = url.replace(/"/g, '\\"');
772
- await execWithTimeout('gdbus', [
773
- 'call', '--session',
774
- '--dest', 'org.freedesktop.portal.Desktop',
775
- '--object-path', '/org/freedesktop/portal/desktop',
776
- '--method', 'org.freedesktop.portal.OpenURI.OpenURI',
777
- '', escapedUrl, '{}'
778
- ]);
779
- this.log.info('βœ… Browser aperto con xdg-desktop-portal (Wayland)');
780
- return true;
781
- }
782
- catch { }
783
- break;
784
- }
785
- // 3. Fallback: Helper generici Debian/Ubuntu/Raspberry Pi OS
786
- const helpers = ['sensible-browser', 'x-www-browser', 'www-browser'];
787
- for (const helper of helpers) {
788
- try {
789
- await execWithTimeout(helper, [url]);
790
- this.log.info(`βœ… Browser aperto con ${helper}`);
791
- return true;
792
- }
793
- catch { }
794
- }
795
- // 4. Ultimo tentativo: Browser diretti
796
- const browsers = [
797
- 'chromium-browser', // Raspberry Pi OS default
798
- 'chromium',
799
- 'firefox',
800
- 'firefox-esr', // Debian/Raspberry Pi OS
801
- 'google-chrome',
802
- 'microsoft-edge',
803
- 'brave-browser',
804
- 'opera'
805
- ];
806
- for (const browser of browsers) {
807
- try {
808
- await execWithTimeout(browser, [url]);
809
- this.log.info(`βœ… Browser aperto con ${browser}`);
810
- return true;
811
- }
812
- catch { }
813
- }
814
- return false;
815
- };
816
- (async () => {
817
- try {
818
- let success = false;
819
- switch (process.platform) {
820
- case 'darwin':
821
- await execWithTimeout('open', [url]);
822
- success = true;
823
- break;
824
- case 'win32':
825
- await execWithTimeout(`start "" "${url}"`);
826
- success = true;
827
- break;
828
- default: // Linux
829
- success = await tryLinuxMethods();
830
- }
831
- if (success) {
832
- this.log.info('🌐 Browser aperto con successo');
833
- }
834
- else {
835
- throw new Error('Tutti i metodi di apertura browser falliti');
836
- }
613
+ // πŸ†• Per servizi systemd: NON tentare di aprire automaticamente
614
+ // L'utente puΓ² aprire da qualsiasi dispositivo sulla rete
615
+ const isSystemdService = !!(process.env.SYSTEMD_EXEC_PID || process.env.INVOCATION_ID);
616
+ const isHomebridge = process.env.USER === 'homebridge';
617
+ // Se siamo in un servizio systemd o utente homebridge, non aprire automaticamente
618
+ if (isSystemdService || isHomebridge) {
619
+ this.log.info('='.repeat(80));
620
+ this.log.info('πŸ” AUTHENTICATION REQUIRED');
621
+ this.log.info('='.repeat(80));
622
+ this.log.info('');
623
+ this.log.info('πŸ“± Open this URL from ANY device on your network:');
624
+ this.log.info('');
625
+ this.log.info(` ${url}`);
626
+ this.log.info('');
627
+ this.log.info('βœ… You can open it from:');
628
+ this.log.info(' β€’ Your computer/laptop');
629
+ this.log.info(' β€’ Your smartphone/tablet');
630
+ this.log.info(' β€’ This Raspberry Pi (if you have a browser)');
631
+ this.log.info('');
632
+ this.log.info(`🌐 Auth server is listening on: ${this.hostIp}:${this.config.redirectPort || 4200}`);
633
+ this.log.info('⏳ Waiting for authentication...');
634
+ this.log.info('='.repeat(80));
635
+ return;
636
+ }
637
+ // Solo per installazioni non-systemd (es. macOS, sviluppo locale)
638
+ this.tryOpenBrowserDirect(url);
639
+ }
640
+ tryOpenBrowserDirect(url) {
641
+ const { exec } = require('child_process');
642
+ let command;
643
+ switch (process.platform) {
644
+ case 'darwin':
645
+ command = `open "${url}"`;
646
+ break;
647
+ case 'win32':
648
+ command = `start "" "${url}"`;
649
+ break;
650
+ default: // Linux desktop (non-systemd)
651
+ command = `xdg-open "${url}" 2>/dev/null || firefox "${url}" 2>/dev/null || chromium-browser "${url}" 2>/dev/null`;
652
+ }
653
+ exec(command, (error) => {
654
+ if (error) {
655
+ this.log.info('πŸ“± Please open the authentication URL manually in your browser');
837
656
  }
838
- catch (e) {
839
- this.log.warn('='.repeat(80));
840
- this.log.warn('⚠️ IMPOSSIBILE APRIRE IL BROWSER AUTOMATICAMENTE');
841
- this.log.warn('='.repeat(80));
842
- this.log.warn('πŸ“± Apri manualmente questo URL da QUALSIASI dispositivo nella tua rete:');
843
- this.log.warn('');
844
- this.log.warn(` ${url}`);
845
- this.log.warn('');
846
- this.log.warn('πŸ’‘ Puoi usare:');
847
- this.log.warn(' β€’ Computer/laptop sulla stessa rete');
848
- this.log.warn(' β€’ Smartphone/tablet');
849
- this.log.warn(' β€’ Qualsiasi browser moderno');
850
- this.log.warn('='.repeat(80));
657
+ else {
658
+ this.log.info('🌐 Opening browser...');
851
659
  }
852
- })();
660
+ });
853
661
  }
854
662
  async exchangeCodeForTokens(authCode) {
855
663
  const tokenData = new url_1.URLSearchParams({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homebridge-viessmann-vicare",
3
- "version": "2.0.6",
3
+ "version": "2.0.7",
4
4
  "description": "Complete Homebridge plugin for Viessmann ViCare heating systems with full control capabilities, advanced rate limiting protection, and intelligent cache management",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {