homebridge-hap-homematic 0.2.1 → 0.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.
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* (connection only).
|
|
15
15
|
* - state.devicesFile: devices.json (the mappings), loaded and saved
|
|
16
16
|
* through the /devices/get and /devices/put endpoints.
|
|
17
|
-
* - state.view: top-level view (
|
|
17
|
+
* - state.view: top-level view, shown as a tab (settings | channels).
|
|
18
18
|
* - state.nav: optional sub-view stack pushed on top of the base
|
|
19
19
|
* view. Pushing a sub-view (e.g. "edit channel") swaps the body
|
|
20
20
|
* until the user pops back. No modals.
|
|
@@ -69,7 +69,7 @@ const DEFAULT_BLOCK = () => ({
|
|
|
69
69
|
});
|
|
70
70
|
|
|
71
71
|
const state = {
|
|
72
|
-
view: '
|
|
72
|
+
view: 'settings',
|
|
73
73
|
/** Optional drill-down sub-view (replaces base view body until popped). */
|
|
74
74
|
nav: [],
|
|
75
75
|
cfg: DEFAULT_BLOCK(),
|
|
@@ -87,6 +87,8 @@ const state = {
|
|
|
87
87
|
filterType: { channels: 'all' }, // CCU channel type, e.g. SWITCH_VIRTUAL_RECEIVER, or 'all'
|
|
88
88
|
sort: { channels: 'name' }, // name | device | address | type
|
|
89
89
|
page: { channels: 1, picker: 1 },
|
|
90
|
+
/** false: hide channels that are not worth mapping (see hiddenChannels). */
|
|
91
|
+
showAll: false,
|
|
90
92
|
},
|
|
91
93
|
pluginVersion: 'unknown',
|
|
92
94
|
};
|
|
@@ -102,6 +104,54 @@ function channelLookup() {
|
|
|
102
104
|
return out;
|
|
103
105
|
}
|
|
104
106
|
|
|
107
|
+
const GROUP_RE = /^(SWITCH|SHUTTER|BLIND|DIMMER)_(TRANSMITTER|VIRTUAL_RECEIVER)$/;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Addresses of channels that the lists hide unless "Show all channels"
|
|
111
|
+
* is on:
|
|
112
|
+
* - channels no service can handle (MAINTENANCE, week profiles, …);
|
|
113
|
+
* - in an HmIP actuator group (one *_TRANSMITTER followed by its
|
|
114
|
+
* *_VIRTUAL_RECEIVERs) every channel but one. The one kept is the
|
|
115
|
+
* transmitter if a service handles it, else the first receiver. The
|
|
116
|
+
* plugin pairs them itself (src/ccu/pairing.ts), so mapping any of
|
|
117
|
+
* them gives the same accessory;
|
|
118
|
+
* - the CCU's own virtual remote (HM-RCV-50 / HmIP-RCV-50, 50 keys).
|
|
119
|
+
* Channels already in devices.json are never hidden.
|
|
120
|
+
*/
|
|
121
|
+
function hiddenChannels(device) {
|
|
122
|
+
const channels = sortBy(device.channels ?? [], (c) => c.index);
|
|
123
|
+
if (/RCV-50$/i.test(device.type ?? '')) return new Set(channels.map((c) => c.address));
|
|
124
|
+
const handled = new Set(state.services.channelServices.flatMap((s) => s.channelTypes));
|
|
125
|
+
const hidden = new Set();
|
|
126
|
+
for (let i = 0; i < channels.length; i++) {
|
|
127
|
+
const c = channels[i];
|
|
128
|
+
const m = GROUP_RE.exec(c.type ?? '');
|
|
129
|
+
if (!m || m[2] !== 'TRANSMITTER') {
|
|
130
|
+
// Receivers are handled with their transmitter below; a receiver
|
|
131
|
+
// without a transmitter before it stays visible.
|
|
132
|
+
const inGroup = m && channels.slice(0, i).some((p) => p.type === `${m[1]}_TRANSMITTER`);
|
|
133
|
+
if (!inGroup && !handled.has(c.type)) hidden.add(c.address);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const group = [c];
|
|
137
|
+
for (let j = i + 1; j < channels.length && channels[j].type === `${m[1]}_VIRTUAL_RECEIVER`; j++) group.push(channels[j]);
|
|
138
|
+
const keep = handled.has(c.type) ? c : (group[1] ?? c);
|
|
139
|
+
for (const g of group) if (g !== keep) hidden.add(g.address);
|
|
140
|
+
}
|
|
141
|
+
return hidden;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Union of hiddenChannels() over all discovered devices, minus mapped ones. */
|
|
145
|
+
function hiddenAddresses() {
|
|
146
|
+
if (state.ui.showAll) return new Set();
|
|
147
|
+
const mapped = new Set(configuredChannels().map((c) => c.address));
|
|
148
|
+
const out = new Set();
|
|
149
|
+
for (const d of state.discovered.devices ?? []) {
|
|
150
|
+
for (const a of hiddenChannels(d)) if (!mapped.has(a)) out.add(a);
|
|
151
|
+
}
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
154
|
+
|
|
105
155
|
const deviceMode = (dev) => dev.mode ?? (dev.service ? 'device' : 'perChannel');
|
|
106
156
|
const splitKey = (key) => { const i = key.indexOf(':'); return [key.slice(0, i), key.slice(i + 1)]; };
|
|
107
157
|
// Same fallback as the plugin: HmIP serials are 14 hex digits.
|
|
@@ -214,7 +264,11 @@ function devicesErrorHTML() {
|
|
|
214
264
|
function navigate(view) {
|
|
215
265
|
state.view = view;
|
|
216
266
|
state.nav = [];
|
|
217
|
-
|
|
267
|
+
document.querySelectorAll('#hgui-tabs [data-tab]').forEach((t) => {
|
|
268
|
+
const on = t.dataset.tab === view;
|
|
269
|
+
t.classList.toggle('active', on);
|
|
270
|
+
t.setAttribute('aria-selected', String(on));
|
|
271
|
+
});
|
|
218
272
|
rerender();
|
|
219
273
|
}
|
|
220
274
|
|
|
@@ -235,46 +289,10 @@ function rerender() {
|
|
|
235
289
|
// ---------------------------------------------------------------- top-level views
|
|
236
290
|
|
|
237
291
|
const VIEWS = {
|
|
238
|
-
dashboard: viewDashboard,
|
|
239
|
-
channels: viewChannels,
|
|
240
292
|
settings: viewSettings,
|
|
293
|
+
channels: viewChannels,
|
|
241
294
|
};
|
|
242
295
|
|
|
243
|
-
// ---------------------------------------------------------------- dashboard
|
|
244
|
-
|
|
245
|
-
function viewDashboard(host) {
|
|
246
|
-
const card = (label, value, sub = '') => `
|
|
247
|
-
<div class="col-sm-6 col-md-3 d-flex">
|
|
248
|
-
<div class="hgui-card flex-fill">
|
|
249
|
-
<div class="hgui-meta">${h(label)}</div>
|
|
250
|
-
<div class="display-6">${h(value)}</div>
|
|
251
|
-
<div class="hgui-meta">${h(sub)}</div>
|
|
252
|
-
</div>
|
|
253
|
-
</div>`;
|
|
254
|
-
const discoveryStatus = state.discovered.devices.length
|
|
255
|
-
? `${state.discovered.devices.length} devices`
|
|
256
|
-
: 'No discovery data yet — click "Discover" in the header to load from the CCU.';
|
|
257
|
-
host.innerHTML = `
|
|
258
|
-
<h4 class="my-3">Dashboard</h4>
|
|
259
|
-
<div class="row g-3 mb-3">
|
|
260
|
-
${card('Channels', configuredChannels().length, 'in HomeKit')}
|
|
261
|
-
${card('Devices', state.discovered.devices.length, 'discovered on the CCU')}
|
|
262
|
-
</div>
|
|
263
|
-
<div class="hgui-card">
|
|
264
|
-
<h6>Discovery</h6>
|
|
265
|
-
<p class="hgui-meta mb-0">${h(discoveryStatus)}</p>
|
|
266
|
-
</div>
|
|
267
|
-
<div class="hgui-card">
|
|
268
|
-
<h6>Quick links</h6>
|
|
269
|
-
<div class="d-flex gap-2 flex-wrap">
|
|
270
|
-
<button class="btn btn-outline-primary btn-sm" data-go="channels">Manage channels →</button>
|
|
271
|
-
<button class="btn btn-outline-secondary btn-sm" data-go="settings">Settings →</button>
|
|
272
|
-
</div>
|
|
273
|
-
</div>
|
|
274
|
-
`;
|
|
275
|
-
host.querySelectorAll('[data-go]').forEach((b) => b.addEventListener('click', () => navigate(b.dataset.go)));
|
|
276
|
-
}
|
|
277
|
-
|
|
278
296
|
// ---------------------------------------------------------------- channels view
|
|
279
297
|
|
|
280
298
|
function viewChannels(host) {
|
|
@@ -284,11 +302,26 @@ function viewChannels(host) {
|
|
|
284
302
|
// search input never gets recreated, never loses focus.
|
|
285
303
|
// Build the type filter option list from the discovered device tree
|
|
286
304
|
// so the dropdown reflects what's actually on this CCU.
|
|
305
|
+
const hidden = hiddenAddresses();
|
|
287
306
|
const channelTypes = new Set();
|
|
288
|
-
for (const d of state.discovered.devices)
|
|
307
|
+
for (const d of state.discovered.devices) {
|
|
308
|
+
for (const c of d.channels) if (c.type && c.index !== 0 && !hidden.has(c.address)) channelTypes.add(c.type);
|
|
309
|
+
}
|
|
289
310
|
|
|
290
311
|
host.innerHTML = `
|
|
291
|
-
<
|
|
312
|
+
<div class="hgui-card mt-3">
|
|
313
|
+
<h6>Which channels go to HomeKit?</h6>
|
|
314
|
+
<ol class="hgui-meta mb-2 ps-3">
|
|
315
|
+
<li>Click <strong>Discover</strong> to load the devices from the CCU.</li>
|
|
316
|
+
<li>Switch the filter to <em>Not in HomeKit</em>, pick a channel and click <strong>+ Add</strong>.
|
|
317
|
+
Give it a HomeKit name and choose the service (switch, outlet, light, blind, …).</li>
|
|
318
|
+
<li>Restart the child bridge to apply the changes.</li>
|
|
319
|
+
</ol>
|
|
320
|
+
<p class="hgui-meta mb-0">Only the channels worth mapping are listed. An HmIP actuator shows one
|
|
321
|
+
channel per output (e.g. HmIP-BROLL channel 3, HmIP-PS channel 2, HmIP-DRSI4 channels 5 / 9 / 13 / 17);
|
|
322
|
+
the plugin reads the state there and sends commands to the matching receiver channel itself.
|
|
323
|
+
Choose the channel and service once: changing either later creates a new accessory in Home.</p>
|
|
324
|
+
</div>
|
|
292
325
|
<div class="hgui-toolbar">
|
|
293
326
|
<input type="search" class="form-control" id="hgui-search-channels"
|
|
294
327
|
placeholder="Search by HomeKit name, CCU name, address, type…"
|
|
@@ -308,6 +341,10 @@ function viewChannels(host) {
|
|
|
308
341
|
<option value="address">Sort: Address</option>
|
|
309
342
|
<option value="type">Sort: Channel type</option>
|
|
310
343
|
</select>
|
|
344
|
+
<div class="form-check mb-0">
|
|
345
|
+
<input class="form-check-input" type="checkbox" id="hgui-showall-channels" ${state.ui.showAll ? 'checked' : ''} />
|
|
346
|
+
<label class="form-check-label" for="hgui-showall-channels">Show all channels</label>
|
|
347
|
+
</div>
|
|
311
348
|
<button class="btn btn-primary ms-auto" id="hgui-add-channel">+ Add channel</button>
|
|
312
349
|
<span class="hgui-meta" id="hgui-channels-count"></span>
|
|
313
350
|
</div>
|
|
@@ -338,6 +375,11 @@ function viewChannels(host) {
|
|
|
338
375
|
state.ui.sort.channels = e.target.value;
|
|
339
376
|
drawChannelRows();
|
|
340
377
|
});
|
|
378
|
+
$('hgui-showall-channels').addEventListener('change', (e) => {
|
|
379
|
+
state.ui.showAll = e.target.checked;
|
|
380
|
+
state.ui.page.channels = 1;
|
|
381
|
+
rerender(); // the type filter list depends on it too
|
|
382
|
+
});
|
|
341
383
|
$('hgui-add-channel').addEventListener('click', () => {
|
|
342
384
|
if (state.devicesErrors) {
|
|
343
385
|
toast('warning', 'Fix devices.json first', 'Add channel'); return;
|
|
@@ -366,6 +408,7 @@ function drawChannelRows() {
|
|
|
366
408
|
rows = configured.map((c) => ({ ...c, _info: cl.get(c.address), isConfigured: true }));
|
|
367
409
|
} else {
|
|
368
410
|
// 'all' or 'unconfigured' both need discovered data.
|
|
411
|
+
const hidden = hiddenAddresses();
|
|
369
412
|
rows = [];
|
|
370
413
|
for (const d of state.discovered.devices) {
|
|
371
414
|
// A whole-device mapping (e.g. irrigation) is one card, not N channels.
|
|
@@ -376,6 +419,7 @@ function drawChannelRows() {
|
|
|
376
419
|
}
|
|
377
420
|
for (const c of d.channels) {
|
|
378
421
|
if (c.index === 0) continue; // MAINTENANCE: not mappable
|
|
422
|
+
if (hidden.has(c.address)) continue;
|
|
379
423
|
const existing = configuredByAddr.get(c.address);
|
|
380
424
|
const isConfigured = !!existing;
|
|
381
425
|
if (filter === 'unconfigured' && isConfigured) continue;
|
|
@@ -508,8 +552,18 @@ function pagerHTML(kind, page, pageCount) {
|
|
|
508
552
|
|
|
509
553
|
function viewSettings(host) {
|
|
510
554
|
const m = state.cfg;
|
|
555
|
+
const stat = (label, value, sub) => `
|
|
556
|
+
<div class="hgui-card">
|
|
557
|
+
<div class="hgui-meta">${h(label)}</div>
|
|
558
|
+
<div class="hgui-stat-value">${h(value)}</div>
|
|
559
|
+
<div class="hgui-meta">${h(sub)}</div>
|
|
560
|
+
</div>`;
|
|
561
|
+
const discovered = state.discovered.devices.length;
|
|
511
562
|
host.innerHTML = `
|
|
512
|
-
<
|
|
563
|
+
<div class="hgui-stats mt-3">
|
|
564
|
+
${stat('Channels', configuredChannels().length, 'in HomeKit')}
|
|
565
|
+
${stat('Devices', discovered, discovered ? 'discovered on the CCU' : 'not discovered yet — click "Discover"')}
|
|
566
|
+
</div>
|
|
513
567
|
<div class="hgui-card">
|
|
514
568
|
<h6>CCU connection</h6>
|
|
515
569
|
<div class="row g-2 mb-2">
|
|
@@ -533,9 +587,12 @@ function viewSettings(host) {
|
|
|
533
587
|
<label class="form-label">Callback address (required)</label>
|
|
534
588
|
<input class="form-control" id="cf-callback-host" list="cf-callback-list" placeholder="192.168.0.13" value="${h(m.eventServer.callbackHost ?? '')}" />
|
|
535
589
|
<datalist id="cf-callback-list">${state.network.map((n) => `<option value="${h(n.address)}">${h(n.name)}</option>`).join('')}</datalist>
|
|
536
|
-
<div class="hgui-hint">IP of this Homebridge host as seen from the CCU. The CCU sends its events to <code>http://<address>:<port></code
|
|
537
|
-
|
|
538
|
-
|
|
590
|
+
<div class="hgui-hint">IP of this Homebridge host as seen from the CCU. The CCU sends its events to <code>http://<address>:<port></code>.</div>
|
|
591
|
+
${state.network.length ? `<div class="hgui-hint"><details>
|
|
592
|
+
<summary>Local addresses of this host (${state.network.length})</summary>
|
|
593
|
+
<div class="mt-1">${state.network.map((n) => `<a href="#" data-pick-ip="${h(n.address)}">${h(n.address)}</a> (${h(n.name)})`).join(', ')}</div>
|
|
594
|
+
<div class="mt-1">Click one to use it. In Docker these are container addresses — use the host's IP instead.</div>
|
|
595
|
+
</details></div>` : ''}
|
|
539
596
|
</div>
|
|
540
597
|
</div>
|
|
541
598
|
</div>
|
|
@@ -634,7 +691,8 @@ function subviewHeader(host, title, breadcrumb) {
|
|
|
634
691
|
// --- pickChannel: searchable picker
|
|
635
692
|
|
|
636
693
|
function subPickChannel(host) {
|
|
637
|
-
subviewHeader(host, 'Add channel — pick from CCU'
|
|
694
|
+
subviewHeader(host, 'Add channel — pick from CCU',
|
|
695
|
+
state.ui.showAll ? 'all channels' : 'channels worth mapping ("Show all channels" lists every one)');
|
|
638
696
|
const body = $('hgui-sub-body');
|
|
639
697
|
body.innerHTML = `
|
|
640
698
|
<div class="hgui-toolbar">
|
|
@@ -658,9 +716,13 @@ function drawPicker() {
|
|
|
658
716
|
const host = $('hgui-picker-host');
|
|
659
717
|
const pagerHost = $('hgui-picker-pager');
|
|
660
718
|
const configuredByAddr = new Map(configuredChannels().map((c) => [c.address, c]));
|
|
719
|
+
const hidden = hiddenAddresses();
|
|
661
720
|
const all = [];
|
|
662
721
|
for (const d of state.discovered.devices) {
|
|
663
|
-
for (const c of d.channels)
|
|
722
|
+
for (const c of d.channels) {
|
|
723
|
+
if (c.index === 0 || hidden.has(c.address)) continue;
|
|
724
|
+
all.push({ device: d, channel: c });
|
|
725
|
+
}
|
|
664
726
|
}
|
|
665
727
|
const q = state.ui.search.picker.trim().toLowerCase();
|
|
666
728
|
let rows = q
|
|
@@ -1001,7 +1063,8 @@ async function init() {
|
|
|
1001
1063
|
}
|
|
1002
1064
|
|
|
1003
1065
|
// Header listeners.
|
|
1004
|
-
|
|
1066
|
+
document.querySelectorAll('#hgui-tabs [data-tab]').forEach((t) =>
|
|
1067
|
+
t.addEventListener('click', () => navigate(t.dataset.tab)));
|
|
1005
1068
|
$('hgui-discover-btn').addEventListener('click', onDiscover);
|
|
1006
1069
|
$('hgui-test-btn').addEventListener('click', onTestConnection);
|
|
1007
1070
|
|
|
@@ -1010,15 +1073,13 @@ async function init() {
|
|
|
1010
1073
|
if (e.key === 'Escape' && state.nav.length) popNav();
|
|
1011
1074
|
});
|
|
1012
1075
|
|
|
1013
|
-
// Initial tab:
|
|
1014
|
-
//
|
|
1015
|
-
// background Discover so the channels tab is
|
|
1016
|
-
// populated by the time the user clicks them.
|
|
1076
|
+
// Initial tab: Settings until a CCU is configured, then Channels with
|
|
1077
|
+
// a background Discover so the list fills in on its own.
|
|
1017
1078
|
const isConfigured = !!state.cfg.ccuIp && state.cfg.ccuIp.length > 0;
|
|
1018
1079
|
if (!isConfigured) {
|
|
1019
1080
|
navigate('settings');
|
|
1020
1081
|
} else {
|
|
1021
|
-
navigate('
|
|
1082
|
+
navigate('channels');
|
|
1022
1083
|
// Fire-and-forget; failures fall back to the empty state with a
|
|
1023
1084
|
// hint already in place. No spinner — user can still navigate.
|
|
1024
1085
|
onDiscover().catch(() => { /* errors already toasted by onDiscover */ });
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
.hgui-empty { color: var(--bs-secondary-color); font-style: italic; padding: 2rem; text-align: center; border: 1px dashed var(--bs-border-color-translucent); border-radius: 8px; margin-top: 1rem; }
|
|
91
91
|
.hgui-pager { display: flex; gap: 0.25rem; align-items: center; justify-content: center; padding: 0.75rem 0; flex-wrap: wrap; }
|
|
92
92
|
|
|
93
|
-
/* Cards (used in
|
|
93
|
+
/* Cards (used in settings / sub-views) --------------------------- */
|
|
94
94
|
.hgui-card { background: var(--bs-tertiary-bg); color: var(--bs-body-color); border: 1px solid var(--bs-border-color-translucent); border-radius: 8px; padding: 1rem; margin-bottom: 0.75rem; }
|
|
95
95
|
.hgui-card h6 { margin-bottom: 0.5rem; }
|
|
96
96
|
.hgui-meta { color: var(--bs-secondary-color); font-size: 0.85rem; }
|
|
@@ -100,22 +100,37 @@
|
|
|
100
100
|
@media (max-width: 700px) { .hgui-form-row { grid-template-columns: 1fr; } }
|
|
101
101
|
.hgui-form-row label { padding-top: 0.5rem; font-weight: 500; color: var(--bs-body-color); }
|
|
102
102
|
.hgui-form-row .hgui-hint { color: var(--bs-secondary-color); font-size: 0.825rem; margin-top: 0.25rem; }
|
|
103
|
+
.hgui-hint { color: var(--bs-secondary-color); font-size: 0.825rem; margin-top: 0.25rem; }
|
|
104
|
+
.hgui-hint details summary { cursor: pointer; color: var(--bs-link-color); }
|
|
105
|
+
|
|
106
|
+
/* Tabs: keep the host's tab look, make the active tab readable in both themes. */
|
|
107
|
+
#hgui-tabs .nav-link { color: var(--bs-secondary-color); }
|
|
108
|
+
#hgui-tabs .nav-link.active { color: var(--bs-body-color); background: var(--bs-body-bg); }
|
|
109
|
+
|
|
110
|
+
/* Status tiles at the top of Settings. */
|
|
111
|
+
.hgui-stats { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 0.75rem; margin-bottom: 0.75rem; }
|
|
112
|
+
.hgui-stats .hgui-card { margin-bottom: 0; }
|
|
113
|
+
.hgui-stat-value { font-size: 1.75rem; font-weight: 600; line-height: 1.2; }
|
|
103
114
|
</style>
|
|
104
115
|
|
|
105
116
|
<div id="hgui-app">
|
|
106
117
|
|
|
107
|
-
<!-- Header:
|
|
118
|
+
<!-- Header: action buttons, then the view tabs ------------------------ -->
|
|
108
119
|
<header class="hgui-header">
|
|
109
|
-
<select id="hgui-view-select" class="form-select form-select-sm" style="min-width: 200px; max-width: 220px">
|
|
110
|
-
<option value="dashboard">📊 Dashboard</option>
|
|
111
|
-
<option value="channels">💡 Channels</option>
|
|
112
|
-
<option value="settings">⚙️ Settings</option>
|
|
113
|
-
</select>
|
|
114
120
|
<button id="hgui-discover-btn" class="btn btn-primary btn-sm">Discover</button>
|
|
115
121
|
<button id="hgui-test-btn" class="btn btn-outline-secondary btn-sm">Test CCU</button>
|
|
116
122
|
<span id="hgui-test-result" class="small"></span>
|
|
117
123
|
<span class="hgui-version ms-auto" id="hgui-version-banner"></span>
|
|
118
124
|
</header>
|
|
125
|
+
<!-- Plain Bootstrap tab styling; switching is done in app.js (no Bootstrap JS). -->
|
|
126
|
+
<ul class="nav nav-tabs" id="hgui-tabs" role="tablist">
|
|
127
|
+
<li class="nav-item" role="presentation">
|
|
128
|
+
<button class="nav-link" type="button" role="tab" data-tab="settings">⚙️ Settings</button>
|
|
129
|
+
</li>
|
|
130
|
+
<li class="nav-item" role="presentation">
|
|
131
|
+
<button class="nav-link" type="button" role="tab" data-tab="channels">💡 Channels</button>
|
|
132
|
+
</li>
|
|
133
|
+
</ul>
|
|
119
134
|
|
|
120
135
|
<main class="hgui-main">
|
|
121
136
|
<div id="hgui-view-host"></div>
|
package/homebridge-ui/server.js
CHANGED
|
@@ -20,6 +20,9 @@ var HomebridgePluginUiServer = class {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
});
|
|
23
|
+
process.on("disconnect", () => {
|
|
24
|
+
process.kill(process.pid, "SIGTERM");
|
|
25
|
+
});
|
|
23
26
|
}
|
|
24
27
|
get homebridgeStoragePath() {
|
|
25
28
|
return process.env.HOMEBRIDGE_STORAGE_PATH;
|
|
@@ -143,14 +146,6 @@ var RequestError = class _RequestError extends Error {
|
|
|
143
146
|
this.requestError = requestError;
|
|
144
147
|
}
|
|
145
148
|
};
|
|
146
|
-
setInterval(() => {
|
|
147
|
-
if (!process.connected) {
|
|
148
|
-
process.kill(process.pid, "SIGTERM");
|
|
149
|
-
}
|
|
150
|
-
}, 1e4);
|
|
151
|
-
process.on("disconnect", () => {
|
|
152
|
-
process.kill(process.pid, "SIGTERM");
|
|
153
|
-
});
|
|
154
149
|
|
|
155
150
|
// homebridge-ui/server.src.js
|
|
156
151
|
import { CcuClient } from "../dist/src/ccu/CcuClient.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-hap-homematic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Homebridge plugin for HomeMatic / HomematicIP devices on a CCU / OpenCCU, using XML-RPC only. Hard fork of matthiaseinig/Homebridge-Homematic-HAP (homebridge-homematic-hap); not affiliated with thkl/hap-homematic.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -45,19 +45,19 @@
|
|
|
45
45
|
"prepack": "npm run build"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@homebridge/plugin-ui-utils": "^2.
|
|
48
|
+
"@homebridge/plugin-ui-utils": "^2.2.7",
|
|
49
49
|
"homematic-xmlrpc": "^2.0.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@eslint/js": "^
|
|
52
|
+
"@eslint/js": "^10.0.1",
|
|
53
53
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
54
54
|
"@semantic-release/github": "^12.0.10",
|
|
55
55
|
"@semantic-release/release-notes-generator": "^14.1.1",
|
|
56
56
|
"@types/node": "^24.10.1",
|
|
57
|
-
"@vitest/coverage-v8": "^
|
|
57
|
+
"@vitest/coverage-v8": "^5.1.0",
|
|
58
58
|
"conventional-changelog-conventionalcommits": "^9.3.1",
|
|
59
|
-
"esbuild": "^0.
|
|
60
|
-
"eslint": "^
|
|
59
|
+
"esbuild": "^0.28.2",
|
|
60
|
+
"eslint": "^10.14.0",
|
|
61
61
|
"homebridge": "^2.5.0",
|
|
62
62
|
"nodemon": "^3.1.11",
|
|
63
63
|
"rimraf": "^6.1.0",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"ts-node": "^10.9.2",
|
|
66
66
|
"typescript": "^5.9.3",
|
|
67
67
|
"typescript-eslint": "^8.46.4",
|
|
68
|
-
"vitest": "^
|
|
68
|
+
"vitest": "^5.1.0"
|
|
69
69
|
},
|
|
70
70
|
"homepage": "https://github.com/california444/homebridge-hap-homematic#readme",
|
|
71
71
|
"repository": {
|