iobroker.mywebui 1.42.2 → 1.42.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.mywebui",
3
- "version": "1.42.2",
3
+ "version": "1.42.3",
4
4
  "description": "ioBroker mywebui - Custom edited mywebui by gokturk413 with 3D Editor",
5
5
  "type": "module",
6
6
  "main": "dist/backend/main.js",
@@ -6,7 +6,8 @@ export class IobrokerWebui3DScreenEditor extends BaseCustomWebComponentConstruct
6
6
  <div id="editor-container" style="width:100%;height:100%;display:flex;flex-direction:column;overflow:hidden;">
7
7
  <div id="toolbar" style="height:40px;background:#2a2a2a;border-bottom:1px solid #444;display:flex;align-items:center;padding:0 10px;gap:10px;">
8
8
  <button id="saveBtn" style="padding:5px 15px;background:#0078d4;color:white;border:none;cursor:pointer;border-radius:3px;">Save</button>
9
- <button id="addAssetBtn" style="padding:5px 15px;background:#28a745;color:white;border:none;cursor:pointer;border-radius:3px;">+ Add Asset</button>
9
+ <button id="addAssetBtn" style="padding:5px 15px;background:#28a745;color:white;border:none;cursor:pointer;border-radius:3px;">+ Asset</button>
10
+ <button id="addLightBtn" style="padding:5px 15px;background:#ffa500;color:white;border:none;cursor:pointer;border-radius:3px;">+ Light</button>
10
11
  <input id="fileInput" type="file" accept=".glb,.gltf,.fbx,.obj" style="display:none;">
11
12
  <button id="undoBtn" style="padding:5px 15px;background:#444;color:#aaa;border:none;cursor:pointer;border-radius:3px;">↶ Undo</button>
12
13
  <button id="redoBtn" style="padding:5px 15px;background:#444;color:#aaa;border:none;cursor:pointer;border-radius:3px;">↷ Redo</button>
@@ -374,8 +375,18 @@ export class IobrokerWebui3DScreenEditor extends BaseCustomWebComponentConstruct
374
375
 
375
376
  for (const light of this.sceneData.lights) {
376
377
  const itemDiv = document.createElement('div');
377
- itemDiv.textContent = light.name;
378
- itemDiv.style.cssText = 'padding:5px;cursor:pointer;color:#aaa;margin-left:10px;';
378
+ itemDiv.textContent = `${light.name} [${light.type}]`;
379
+ itemDiv.style.cssText = 'padding:5px;cursor:pointer;color:#ff0;margin-left:10px;background:#2a2a1a;border-radius:3px;margin-bottom:3px;';
380
+ itemDiv.addEventListener('click', () => this.showLightEditor(light));
381
+ itemDiv.addEventListener('dblclick', () => {
382
+ if (confirm(`Delete light "${light.name}"?`)) {
383
+ const idx = this.sceneData.lights.indexOf(light);
384
+ if (idx > -1) {
385
+ this.sceneData.lights.splice(idx, 1);
386
+ this.updateSceneTree();
387
+ }
388
+ }
389
+ });
379
390
  lightsDiv.appendChild(itemDiv);
380
391
  }
381
392
 
@@ -394,15 +405,24 @@ export class IobrokerWebui3DScreenEditor extends BaseCustomWebComponentConstruct
394
405
 
395
406
  const asset = this.selectedObject.userData.assetData;
396
407
 
408
+ // Asset name
409
+ const nameGroup = document.createElement('div');
410
+ nameGroup.className = 'property-group';
411
+ nameGroup.innerHTML = `
412
+ <div class="property-label">Asset Name</div>
413
+ <input type="text" id="assetName" value="${asset.name}" class="property-input" style="width:100%;">
414
+ `;
415
+ panelEl.appendChild(nameGroup);
416
+
397
417
  // Position
398
418
  const posGroup = document.createElement('div');
399
419
  posGroup.className = 'property-group';
400
420
  posGroup.innerHTML = `
401
- <div class="property-label">Position</div>
421
+ <div class="property-label">📍 Position</div>
402
422
  <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:5px;">
403
- <input type="number" id="posX" value="${asset.position.x}" step="0.1" class="property-input" placeholder="X">
404
- <input type="number" id="posY" value="${asset.position.y}" step="0.1" class="property-input" placeholder="Y">
405
- <input type="number" id="posZ" value="${asset.position.z}" step="0.1" class="property-input" placeholder="Z">
423
+ <input type="number" id="posX" value="${asset.position.x.toFixed(2)}" step="0.1" class="property-input" placeholder="X" data-prop="position.x">
424
+ <input type="number" id="posY" value="${asset.position.y.toFixed(2)}" step="0.1" class="property-input" placeholder="Y" data-prop="position.y">
425
+ <input type="number" id="posZ" value="${asset.position.z.toFixed(2)}" step="0.1" class="property-input" placeholder="Z" data-prop="position.z">
406
426
  </div>
407
427
  `;
408
428
  panelEl.appendChild(posGroup);
@@ -411,11 +431,11 @@ export class IobrokerWebui3DScreenEditor extends BaseCustomWebComponentConstruct
411
431
  const rotGroup = document.createElement('div');
412
432
  rotGroup.className = 'property-group';
413
433
  rotGroup.innerHTML = `
414
- <div class="property-label">Rotation</div>
434
+ <div class="property-label">🔄 Rotation (rad)</div>
415
435
  <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:5px;">
416
- <input type="number" id="rotX" value="${asset.rotation.x}" step="0.1" class="property-input" placeholder="X">
417
- <input type="number" id="rotY" value="${asset.rotation.y}" step="0.1" class="property-input" placeholder="Y">
418
- <input type="number" id="rotZ" value="${asset.rotation.z}" step="0.1" class="property-input" placeholder="Z">
436
+ <input type="number" id="rotX" value="${asset.rotation.x.toFixed(2)}" step="0.01" class="property-input" placeholder="X" data-prop="rotation.x">
437
+ <input type="number" id="rotY" value="${asset.rotation.y.toFixed(2)}" step="0.01" class="property-input" placeholder="Y" data-prop="rotation.y">
438
+ <input type="number" id="rotZ" value="${asset.rotation.z.toFixed(2)}" step="0.01" class="property-input" placeholder="Z" data-prop="rotation.z">
419
439
  </div>
420
440
  `;
421
441
  panelEl.appendChild(rotGroup);
@@ -424,19 +444,237 @@ export class IobrokerWebui3DScreenEditor extends BaseCustomWebComponentConstruct
424
444
  const scaleGroup = document.createElement('div');
425
445
  scaleGroup.className = 'property-group';
426
446
  scaleGroup.innerHTML = `
427
- <div class="property-label">Scale</div>
447
+ <div class="property-label">📏 Scale</div>
428
448
  <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:5px;">
429
- <input type="number" id="scaleX" value="${asset.scale.x}" step="0.1" class="property-input" placeholder="X">
430
- <input type="number" id="scaleY" value="${asset.scale.y}" step="0.1" class="property-input" placeholder="Y">
431
- <input type="number" id="scaleZ" value="${asset.scale.z}" step="0.1" class="property-input" placeholder="Z">
449
+ <input type="number" id="scaleX" value="${asset.scale.x.toFixed(2)}" step="0.1" class="property-input" placeholder="X" data-prop="scale.x">
450
+ <input type="number" id="scaleY" value="${asset.scale.y.toFixed(2)}" step="0.1" class="property-input" placeholder="Y" data-prop="scale.y">
451
+ <input type="number" id="scaleZ" value="${asset.scale.z.toFixed(2)}" step="0.1" class="property-input" placeholder="Z" data-prop="scale.z">
432
452
  </div>
433
453
  `;
434
454
  panelEl.appendChild(scaleGroup);
455
+
456
+ // Bindings section
457
+ const bindingsGroup = document.createElement('div');
458
+ bindingsGroup.className = 'property-group';
459
+ bindingsGroup.style.borderTop = '2px solid #0f0';
460
+ bindingsGroup.innerHTML = `
461
+ <div class="property-label" style="color:#0f0;margin-bottom:10px;">🔗 ioBroker Bindings</div>
462
+ <div id="bindingsList" style="font-size:11px;"></div>
463
+ <button id="addBindingBtn" style="width:100%;padding:8px;margin-top:10px;background:#1a5a1a;color:#0f0;border:1px solid #0f0;cursor:pointer;border-radius:3px;">+ Add Binding</button>
464
+ `;
465
+ panelEl.appendChild(bindingsGroup);
466
+
467
+ // Add property change listeners
468
+ panelEl.querySelectorAll('input[data-prop]').forEach(input => {
469
+ input.addEventListener('change', (e) => {
470
+ const prop = e.target.dataset.prop;
471
+ const value = parseFloat(e.target.value);
472
+ this.updateAssetProperty(asset, prop, value);
473
+ });
474
+ });
475
+
476
+ // Add binding button
477
+ panelEl.querySelector('#addBindingBtn').addEventListener('click', () => {
478
+ this.showBindingEditor(asset);
479
+ });
480
+
481
+ // Asset name listener
482
+ panelEl.querySelector('#assetName').addEventListener('change', (e) => {
483
+ asset.name = e.target.value;
484
+ this.selectedObject.userData.assetData.name = e.target.value;
485
+ });
486
+
487
+ // Update bindings list
488
+ this.updateBindingsList(asset, panelEl);
489
+ }
490
+
491
+ updateAssetProperty(asset, propPath, value) {
492
+ const parts = propPath.split('.');
493
+ let obj = asset;
494
+ for (let i = 0; i < parts.length - 1; i++) {
495
+ obj = obj[parts[i]];
496
+ }
497
+ obj[parts[parts.length - 1]] = value;
498
+
499
+ // Update Three.js object
500
+ if (this.selectedObject) {
501
+ if (propPath.startsWith('position')) {
502
+ this.selectedObject.position.set(asset.position.x, asset.position.y, asset.position.z);
503
+ } else if (propPath.startsWith('rotation')) {
504
+ this.selectedObject.rotation.set(asset.rotation.x, asset.rotation.y, asset.rotation.z);
505
+ } else if (propPath.startsWith('scale')) {
506
+ this.selectedObject.scale.set(asset.scale.x, asset.scale.y, asset.scale.z);
507
+ }
508
+ }
509
+ }
510
+
511
+ updateBindingsList(asset, panelEl) {
512
+ const bindingsList = panelEl.querySelector('#bindingsList');
513
+ if (!asset.bindings || Object.keys(asset.bindings).length === 0) {
514
+ bindingsList.innerHTML = '<div style="color:#888;padding:5px;">No bindings configured</div>';
515
+ return;
516
+ }
517
+
518
+ let html = '';
519
+ for (const [prop, binding] of Object.entries(asset.bindings)) {
520
+ html += `
521
+ <div style="padding:5px;background:#1a1a1a;margin-bottom:3px;border-left:2px solid #0f0;font-family:monospace;">
522
+ <div style="color:#0f0;">${prop}</div>
523
+ <div style="color:#888;font-size:10px;">→ ${binding.signal || 'unset'}</div>
524
+ </div>
525
+ `;
526
+ }
527
+ bindingsList.innerHTML = html;
528
+ }
529
+
530
+ showBindingEditor(asset) {
531
+ const propInput = prompt('Property path (e.g., position.x, rotation.y, scale.z):', 'position.x');
532
+ if (!propInput) return;
533
+
534
+ const signalInput = prompt('ioBroker signal (e.g., system.adapter.0.alive):', '');
535
+ if (!signalInput) return;
536
+
537
+ if (!asset.bindings) {
538
+ asset.bindings = {};
539
+ }
540
+
541
+ asset.bindings[propInput] = {
542
+ signal: signalInput,
543
+ type: 'number',
544
+ min: 0,
545
+ max: 100
546
+ };
547
+
548
+ this.updatePropertyPanel();
549
+ console.log('✅ Binding added:', propInput, '→', signalInput);
550
+ }
551
+
552
+ showLightCreationDialog() {
553
+ const lightType = prompt('Light type:\n1 = Ambient\n2 = Directional\n3 = Point\n4 = Spot\n\nEnter number (default: 2):', '2');
554
+ if (!lightType) return;
555
+
556
+ const typeMap = { '1': 'ambient', '2': 'directional', '3': 'point', '4': 'spot' };
557
+ const type = typeMap[lightType] || 'directional';
558
+
559
+ const name = prompt(`Create new ${type} light\n\nLight name:`, `${type}_${Date.now().toString(36).substring(5)}`);
560
+ if (!name) return;
561
+
562
+ const light = {
563
+ id: 'light_' + Date.now().toString(36),
564
+ name: name,
565
+ type: type,
566
+ color: '#ffffff',
567
+ intensity: type === 'ambient' ? 0.6 : 0.8,
568
+ position: type === 'directional' ? { x: 10, y: 10, z: 10 } : { x: 0, y: 5, z: 0 },
569
+ castShadow: type !== 'ambient',
570
+ visible: true
571
+ };
572
+
573
+ if (!this.sceneData.lights) {
574
+ this.sceneData.lights = [];
575
+ }
576
+ this.sceneData.lights.push(light);
577
+
578
+ // Add to Three.js scene
579
+ this.addLightToScene(light);
580
+
581
+ // Update scene tree
582
+ this.updateSceneTree();
583
+
584
+ console.log('✅ Light created:', name);
585
+ }
586
+
587
+ showLightEditor(light) {
588
+ const panelEl = this._getDomElement('propertyPanel');
589
+ panelEl.innerHTML = '';
590
+
591
+ // Light name
592
+ const nameGroup = document.createElement('div');
593
+ nameGroup.className = 'property-group';
594
+ nameGroup.innerHTML = `
595
+ <div class="property-label">💡 Light: ${light.type}</div>
596
+ <input type="text" id="lightName" value="${light.name}" class="property-input" style="width:100%;">
597
+ `;
598
+ panelEl.appendChild(nameGroup);
599
+
600
+ // Color
601
+ const colorGroup = document.createElement('div');
602
+ colorGroup.className = 'property-group';
603
+ colorGroup.innerHTML = `
604
+ <div class="property-label">Color</div>
605
+ <input type="color" id="lightColor" value="${light.color}" class="property-input" style="width:100%;height:35px;">
606
+ `;
607
+ panelEl.appendChild(colorGroup);
608
+
609
+ // Intensity
610
+ const intensityGroup = document.createElement('div');
611
+ intensityGroup.className = 'property-group';
612
+ intensityGroup.innerHTML = `
613
+ <div class="property-label">Intensity: <span id="intensityValue">${light.intensity.toFixed(2)}</span></div>
614
+ <input type="range" id="lightIntensity" min="0" max="2" step="0.1" value="${light.intensity}" style="width:100%;">
615
+ `;
616
+ panelEl.appendChild(intensityGroup);
617
+
618
+ // Position (if applicable)
619
+ if (light.position) {
620
+ const posGroup = document.createElement('div');
621
+ posGroup.className = 'property-group';
622
+ posGroup.innerHTML = `
623
+ <div class="property-label">📍 Position</div>
624
+ <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:5px;">
625
+ <input type="number" id="lightPosX" value="${light.position.x.toFixed(2)}" step="0.5" class="property-input" placeholder="X">
626
+ <input type="number" id="lightPosY" value="${light.position.y.toFixed(2)}" step="0.5" class="property-input" placeholder="Y">
627
+ <input type="number" id="lightPosZ" value="${light.position.z.toFixed(2)}" step="0.5" class="property-input" placeholder="Z">
628
+ </div>
629
+ `;
630
+ panelEl.appendChild(posGroup);
631
+
632
+ panelEl.querySelector('#lightPosX').addEventListener('change', (e) => {
633
+ light.position.x = parseFloat(e.target.value);
634
+ });
635
+ panelEl.querySelector('#lightPosY').addEventListener('change', (e) => {
636
+ light.position.y = parseFloat(e.target.value);
637
+ });
638
+ panelEl.querySelector('#lightPosZ').addEventListener('change', (e) => {
639
+ light.position.z = parseFloat(e.target.value);
640
+ });
641
+ }
642
+
643
+ // Cast Shadow (if applicable)
644
+ if (light.castShadow !== undefined) {
645
+ const shadowGroup = document.createElement('div');
646
+ shadowGroup.className = 'property-group';
647
+ shadowGroup.innerHTML = `
648
+ <div class="property-label">Cast Shadow</div>
649
+ <input type="checkbox" id="lightShadow" ${light.castShadow ? 'checked' : ''} style="width:20px;height:20px;cursor:pointer;">
650
+ `;
651
+ panelEl.appendChild(shadowGroup);
652
+
653
+ panelEl.querySelector('#lightShadow').addEventListener('change', (e) => {
654
+ light.castShadow = e.target.checked;
655
+ });
656
+ }
657
+
658
+ // Event listeners
659
+ panelEl.querySelector('#lightName').addEventListener('change', (e) => {
660
+ light.name = e.target.value;
661
+ this.updateSceneTree();
662
+ });
663
+
664
+ panelEl.querySelector('#lightColor').addEventListener('change', (e) => {
665
+ light.color = e.target.value;
666
+ });
667
+
668
+ panelEl.querySelector('#lightIntensity').addEventListener('input', (e) => {
669
+ light.intensity = parseFloat(e.target.value);
670
+ panelEl.querySelector('#intensityValue').textContent = light.intensity.toFixed(2);
671
+ });
435
672
  }
436
673
 
437
674
  setupEventListeners() {
438
675
  this._getDomElement('saveBtn').addEventListener('click', () => this.saveScene());
439
676
  this._getDomElement('addAssetBtn').addEventListener('click', () => this.onAddAssetClick());
677
+ this._getDomElement('addLightBtn').addEventListener('click', () => this.showLightCreationDialog());
440
678
  this._getDomElement('fileInput').addEventListener('change', (e) => this.onFileSelected(e));
441
679
  this._getDomElement('undoBtn').addEventListener('click', () => console.log('Undo'));
442
680
  this._getDomElement('redoBtn').addEventListener('click', () => console.log('Redo'));