From 56365214b8baa56c2c2158d9c8da91f033c33d18 Mon Sep 17 00:00:00 2001 From: tom Date: Mon, 29 Jul 2024 15:33:39 +0200 Subject: [PATCH 01/15] ADD: components to display entity data --- src/app/entity-data/entity-data.component.css | 0 .../entity-data/entity-data.component.html | 60 +++++++ .../entity-data/entity-data.component.spec.ts | 23 +++ src/app/entity-data/entity-data.component.ts | 152 ++++++++++++++++++ .../entity-output/entity-output.component.css | 0 .../entity-output.component.html | 1 + .../entity-output.component.spec.ts | 23 +++ .../entity-output/entity-output.component.ts | 10 ++ 8 files changed, 269 insertions(+) create mode 100644 src/app/entity-data/entity-data.component.css create mode 100644 src/app/entity-data/entity-data.component.html create mode 100644 src/app/entity-data/entity-data.component.spec.ts create mode 100644 src/app/entity-data/entity-data.component.ts create mode 100644 src/app/entity-output/entity-output.component.css create mode 100644 src/app/entity-output/entity-output.component.html create mode 100644 src/app/entity-output/entity-output.component.spec.ts create mode 100644 src/app/entity-output/entity-output.component.ts diff --git a/src/app/entity-data/entity-data.component.css b/src/app/entity-data/entity-data.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/entity-data/entity-data.component.html b/src/app/entity-data/entity-data.component.html new file mode 100644 index 0000000..ef15b2c --- /dev/null +++ b/src/app/entity-data/entity-data.component.html @@ -0,0 +1,60 @@ + +
+
+

Name

+ +
+
+

Uniqe ID

+
+ + +
+
+
+

Command Topic

+ +
+
+

Brightness Command Topic

+ +
+
+
+

Payload on

+ +
+
+

Payload off

+ +
+
+
+

Unit of meassurement

+ +
+
+

Value Template

+ +
+
+

Device Class

+ +
+ + +
+

State Topic

+
+ +
+
+ +
\ No newline at end of file diff --git a/src/app/entity-data/entity-data.component.spec.ts b/src/app/entity-data/entity-data.component.spec.ts new file mode 100644 index 0000000..cfc7ab7 --- /dev/null +++ b/src/app/entity-data/entity-data.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EntityDataComponent } from './entity-data.component'; + +describe('EntityDataComponent', () => { + let component: EntityDataComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [EntityDataComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(EntityDataComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/entity-data/entity-data.component.ts b/src/app/entity-data/entity-data.component.ts new file mode 100644 index 0000000..19d0276 --- /dev/null +++ b/src/app/entity-data/entity-data.component.ts @@ -0,0 +1,152 @@ +import { Component, Input } from '@angular/core'; +import { MQTTEntity } from '../_models/mqtt_base'; +import { EntityService } from '../_services/entity.service'; +import { GeneratorService } from '../_services/generator.service'; + +@Component({ + selector: 'app-entity-data', + templateUrl: './entity-data.component.html', + styleUrl: './entity-data.component.css', +}) +export class EntityDataComponent { + @Input() entity_type: number = 0; + @Input() basemodel!: MQTTEntity; + constructor( + private entityService: EntityService, + private generatorService: GeneratorService + ) { + if (generatorService.selected_entity) { + this.basemodel = generatorService.selected_entity; + } + } + + _entity_name: string = ''; + _entity_uniq_id: string = ''; + _entity_stat_t: string = ''; + _entity_cmd_t: string = ''; + _entity_bri_cmd_t: string = ''; + _entity_pl_off: string = ''; + _entity_pl_on: string = ''; + _entity_unit_of_meas: string = ''; + _entity_val_tpl: string = ''; + _entity_dev_cla: string = ''; + + get entity_name() { + let base = this.basemodel.getProperty('name'); + if (base == '') { + this.basemodel.setProperty('name', this._entity_name); + return this._entity_name; + } + return base; + } + + set entity_name(data: string) { + this._entity_name = data; + this.basemodel.setProperty('name', data); + } + + get entity_uniq_id() { + let base = this.basemodel.getProperty('uniq_id'); + if (base == '') { + this.basemodel.setProperty('uniq_id', this._entity_uniq_id); + return this._entity_uniq_id; + } + return base; + } + + set entity_uniq_id(data: string) { + this._entity_uniq_id = data; + this.basemodel.setProperty('uniq_id', data); + } + + get entity_stat_t() { + let base = this.basemodel.getProperty('stat_t'); + if (base == 'state/topic') { + this.basemodel.setProperty('stat_t', this._entity_stat_t); + return this._entity_stat_t; + } + return base; + } + + set entity_stat_t(data: string) { + this._entity_stat_t = data; + this.basemodel.setProperty('stat_t', data); + } + + get entity_cmd_t() { + let base = this.basemodel.getProperty('cmd_t'); + if (base == 'command/topic') { + this.basemodel.setProperty('cmd_t', this._entity_cmd_t); + return this._entity_cmd_t; + } + return base; + } + + set entity_cmd_t(data: string) { + this._entity_cmd_t = data; + this.basemodel.setProperty('cmd_t', data); + } + + get entity_bri_cmd_t() { + let base = this.basemodel.getProperty('bri_cmd_t'); + if (base == 'brightness/command/topic') { + this.basemodel.setProperty('bri_cmd_t', this._entity_bri_cmd_t); + return this._entity_bri_cmd_t; + } + return base; + } + + set entity_bri_cmd_t(data: string) { + this._entity_bri_cmd_t = data; + this.basemodel.setProperty('bri_cmd_t', data); + } + + get entity_pl_off() { + return this._entity_pl_off; + } + + set entity_pl_off(data: string) { + this._entity_pl_off = data; + this.basemodel.setProperty('pl_off', data); + } + + get entity_pl_on() { + return this._entity_pl_on; + } + + set entity_pl_on(data: string) { + this._entity_pl_on = data; + this.basemodel.setProperty('pl_on', data); + } + + get entity_unit_of_meas() { + return this._entity_unit_of_meas; + } + + set entity_unit_of_meas(data: string) { + this._entity_unit_of_meas = data; + this.basemodel.setProperty('unit_of_meas', data); + } + + get entity_val_tpl() { + return this._entity_val_tpl; + } + + set entity_val_tpl(data: string) { + this._entity_val_tpl = data; + this.basemodel.setProperty('val_tpl', data); + } + + get entity_dev_cla() { + return this._entity_dev_cla; + } + + set entity_dev_cla(data: string) { + this._entity_dev_cla = data; + this.basemodel.setProperty('dev_cla', data); + } + + hasProperty(property: string) { + return this.basemodel.attrs.has(property); + } +} diff --git a/src/app/entity-output/entity-output.component.css b/src/app/entity-output/entity-output.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/entity-output/entity-output.component.html b/src/app/entity-output/entity-output.component.html new file mode 100644 index 0000000..01cc950 --- /dev/null +++ b/src/app/entity-output/entity-output.component.html @@ -0,0 +1 @@ +

entity-output works!

diff --git a/src/app/entity-output/entity-output.component.spec.ts b/src/app/entity-output/entity-output.component.spec.ts new file mode 100644 index 0000000..fea0300 --- /dev/null +++ b/src/app/entity-output/entity-output.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EntityOutputComponent } from './entity-output.component'; + +describe('EntityOutputComponent', () => { + let component: EntityOutputComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [EntityOutputComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(EntityOutputComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/entity-output/entity-output.component.ts b/src/app/entity-output/entity-output.component.ts new file mode 100644 index 0000000..df60952 --- /dev/null +++ b/src/app/entity-output/entity-output.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-entity-output', + templateUrl: './entity-output.component.html', + styleUrl: './entity-output.component.css' +}) +export class EntityOutputComponent { + +} -- 2.49.1 From bbc4dd6e53277dc7d4e29cf08a8cb2e030cc5fbc Mon Sep 17 00:00:00 2001 From: tom Date: Mon, 29 Jul 2024 15:33:58 +0200 Subject: [PATCH 02/15] CHANGE: automatic topic generation in class --- src/app/_models/mqtt-binary.ts | 12 ++++- src/app/_models/mqtt-light.ts | 45 ++++++++++++++++-- src/app/_models/mqtt-sensor.ts | 15 +++++- src/app/_models/mqtt-switch.ts | 37 ++++++++++++++- src/app/_models/mqtt_base.ts | 83 ++++++++++++++++++++++++++-------- 5 files changed, 166 insertions(+), 26 deletions(-) diff --git a/src/app/_models/mqtt-binary.ts b/src/app/_models/mqtt-binary.ts index f378ffe..4707f0c 100644 --- a/src/app/_models/mqtt-binary.ts +++ b/src/app/_models/mqtt-binary.ts @@ -1,6 +1,16 @@ -import { DeviceClass, MQTTEntity } from './mqtt_base'; +import { DeviceClass, iMQTTEntityBase, MQTTEntity } from './mqtt_base'; export class MqttBinary extends MQTTEntity { + override ent_type: string = 'binary'; + override attrs: Set = new Set([ + 'name', + 'uniq_id', + 'stat_t', + 'pl_on', + 'pl_off', + 'dev_cla', + ]); + dev_cla: DeviceClass = new DeviceClass([ 'battery', 'battery_charging', diff --git a/src/app/_models/mqtt-light.ts b/src/app/_models/mqtt-light.ts index 0313407..27108f6 100644 --- a/src/app/_models/mqtt-light.ts +++ b/src/app/_models/mqtt-light.ts @@ -1,9 +1,48 @@ -import { MQTTEntity } from './mqtt_base'; +import { iMQTTEntityBase, MQTTEntity } from './mqtt_base'; export class MqttLight extends MQTTEntity { - cmd_t: string = 'command/topic'; - bri_cmd_t: string = 'brightness/command/topic'; pl_on: string = '1'; pl_off: string = '0'; val_tpl: string = ''; + bri_cmd_t: string = 'brightness/command/topic'; + _cmd_t: string = 'command/topic'; + + override readonly ent_type: string = 'light'; + override attrs: Set = new Set([ + 'name', + 'uniq_id', + 'stat_t', + 'pl_on', + 'pl_off', + 'val_tpl', + 'bri_cmd_t', + 'cmd_t', + ]); + + get cmd_t() { + return this._cmd_t; + } + set cmd_t(data: string) { + this._cmd_t = data; + } + + override set name(data: string) { + super.name = data; + this.topic_updates.next('cmd_t'); + this.topic_updates.next('bri_cmd_t'); + } + + override get name() { + return this._name; + } + + override set uniq_id(data: string) { + super.uniq_id = data; + this.topic_updates.next('cmd_t'); + this.topic_updates.next('bri_cmd_t'); + } + + override get uniq_id() { + return this._uniq_id; + } } diff --git a/src/app/_models/mqtt-sensor.ts b/src/app/_models/mqtt-sensor.ts index 369ac49..bdc5080 100644 --- a/src/app/_models/mqtt-sensor.ts +++ b/src/app/_models/mqtt-sensor.ts @@ -1,5 +1,16 @@ -import { MQTTEntity } from './mqtt_base'; +import { iMQTTEntityBase, MQTTEntity } from './mqtt_base'; -export class MqttSensor extends MQTTEntity { +export class MqttSensor extends MQTTEntity implements iMqttSensor { unit_of_meas: string = 'meassure'; + override ent_type: string = 'sensor'; + override attrs: Set = new Set([ + 'name', + 'uniq_id', + 'stat_t', + 'unit_of_meas', + ]); +} + +export interface iMqttSensor extends iMQTTEntityBase { + unit_of_meas: string; } diff --git a/src/app/_models/mqtt-switch.ts b/src/app/_models/mqtt-switch.ts index 57c681a..4915ae3 100644 --- a/src/app/_models/mqtt-switch.ts +++ b/src/app/_models/mqtt-switch.ts @@ -1,8 +1,43 @@ -import { DeviceClass, MQTTEntity } from './mqtt_base'; +import { EntityService } from '../_services/entity.service'; +import { DeviceClass, iMQTTEntityBase, MQTTEntity } from './mqtt_base'; export class MqttSwitch extends MQTTEntity { + override ent_type: string = 'switch'; + override attrs: Set = new Set([ + 'name', + 'uniq_id', + 'stat_t', + 'dev_cla', + 'cmd_t', + 'pl_on', + 'pl_off', + ]); dev_cla: DeviceClass = new DeviceClass(['switch', 'outlet']); cmd_t: string = 'command/topic'; pl_on: string = '1'; pl_off: string = '0'; + + override set name(name: string) { + super.name = name; + this.topic_updates.next('cmd_t'); + } + + override set uniq_id(uniq_id: string) { + super.uniq_id = uniq_id; + this.topic_updates.next('cmd_t'); + } + + override get name() { + return this._name; + } + override get uniq_id() { + return this._uniq_id; + } +} + +export interface iMqttSwitch extends iMQTTEntityBase { + cmd_t: string; + pl_on: string; + pl_off: string; + dev_cla: DeviceClass; } diff --git a/src/app/_models/mqtt_base.ts b/src/app/_models/mqtt_base.ts index 46992a5..9a03d00 100644 --- a/src/app/_models/mqtt_base.ts +++ b/src/app/_models/mqtt_base.ts @@ -1,18 +1,49 @@ -export class MQTTEntity { - name: string = ''; - stat_t: string = ''; - uniq_id: string = ''; - // dev: MQTTDevice | null = null; - // entity_type: ENTITY_TYPE = 0; +import { EventEmitter, Injectable } from '@angular/core'; +import { EntityService } from '../_services/entity.service'; - setProperty(name: unknown, value: any): void { - if (!this.hasOwnProperty(String(name))) return; - this[name as keyof this] = value; +@Injectable() +export class MQTTEntity implements iMQTTEntityBase { + protected _name: string = ''; + protected _stat_t: string = ''; + protected _uniq_id: string = ''; + + attrs = new Set(['name', 'stat_t', 'uniq_id']); + topic_updates = new EventEmitter(); + readonly ent_type: string = 'base'; + + get name() { + return this._name; } - getProperty(name: string): any { - if (!this.hasOwnProperty(name)) return; - return this[name as keyof this]; + set name(name: string) { + this._name = name; + this._uniq_id = btoa(this._name).slice(-7).slice(0, 5); + this.topic_updates.next('stat_t'); + } + + get stat_t() { + return this._stat_t; + } + + set stat_t(stat: string) { + this._stat_t = stat; + } + + get uniq_id() { + return this._uniq_id; + } + + set uniq_id(uniq_id: string) { + this._uniq_id = uniq_id; + this.topic_updates.next('stat_t'); + } + + get display_name() { + if (this._name != '' && this._uniq_id != '') { + return this._name + '_' + this._uniq_id; + } else { + return this._name; + } } toJSON(): any { @@ -24,14 +55,15 @@ export class MQTTEntity { } return jsonObject; } + getProperty(name: string): any { + if (!this.attrs.has(name)) return; + return this[name as keyof this]; + } - // toJSON(): any { - // return { - // name: this.name, - // stat_t: this.stat_t, - // uniq_id: this.uniq_id, - // }; - // } + setProperty(name: string, value: any) { + if (!this.attrs.has(name)) return; + this[name as keyof this] = value; + } } export class MQTTDevice { @@ -53,3 +85,16 @@ export class DeviceClass { return this.choices[this.value]; } } + +export interface iMQTTEntityBase { + // name: string; + // stat_t: string; + // uniq_id: string; + // display_name: string; + + attrs: Set; + readonly ent_type: string; + topic_updates: EventEmitter; + + toJSON: () => string; +} -- 2.49.1 From fe60416e8cb4cd8c508a242d455599bc7e101923 Mon Sep 17 00:00:00 2001 From: tom Date: Mon, 29 Jul 2024 15:35:44 +0200 Subject: [PATCH 03/15] CHANGE: entity_type in select --- src/app/generator/generator.component.html | 11 ++++- src/app/generator/generator.component.ts | 47 ++++++++++++---------- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/app/generator/generator.component.html b/src/app/generator/generator.component.html index 2032dbb..f893625 100644 --- a/src/app/generator/generator.component.html +++ b/src/app/generator/generator.component.html @@ -7,7 +7,7 @@

Bereich

+ (ngModelChange)="generatorService.upperTopic = $event" />

Automatische Topics

@@ -20,9 +20,16 @@
+
+

EntityTyp:

+ +
- + + \ No newline at end of file diff --git a/src/app/generator/generator.component.ts b/src/app/generator/generator.component.ts index 7e9f1f4..9ed328a 100644 --- a/src/app/generator/generator.component.ts +++ b/src/app/generator/generator.component.ts @@ -1,21 +1,17 @@ -import { Component, Input } from '@angular/core'; -import { MqttLight } from '../_models/mqtt-light'; -import { MqttSwitch } from '../_models/mqtt-switch'; -import { MqttSensor } from '../_models/mqtt-sensor'; -import { MqttBinary } from '../_models/mqtt-binary'; -import { GeneratorService, entity_types, randomString } from '../_services/generator.service'; -import { MQTTEntity } from '../_models/mqtt_base'; +import { Component } from '@angular/core'; +import { EntityService } from '../_services/entity.service'; +import { GeneratorService, randomString } from '../_services/generator.service'; @Component({ selector: 'app-home', templateUrl: './generator.component.html', - styleUrl: './generator.component.css' + styleUrl: './generator.component.css', }) - export class GeneratorComponent { - constructor(public generatorService: GeneratorService) { - } - + constructor( + public generatorService: GeneratorService, + private entityService: EntityService + ) {} readonly useObject = Object; readonly useRandomString = randomString; @@ -29,16 +25,23 @@ export class GeneratorComponent { return String(this.generatorService.auto_topic); } - log(event:any){ - console.log(Number(event)); + entity_type: number = 0; + + select_type(etype: number) { + let ent_type = + this.generatorService.entity_types[ + etype as keyof typeof this.generatorService.entity_types + ]; + let ent_class = ent_type[1]; + if (typeof ent_class === 'function' && etype != 0) { + let entity_model = new ent_class(); + this.generatorService.selected_entity = entity_model; + } else this.generatorService.selected_entity = null; + this.entity_type = etype; + console.log(this.generatorService.selected_entity); } - + log(event: any) { + console.log(Number(event)); + } } - - - - - - - -- 2.49.1 From 06e2d77f08402b71d0bd50d7b7a5e506c6fd3b7b Mon Sep 17 00:00:00 2001 From: tom Date: Tue, 30 Jul 2024 15:30:35 +0200 Subject: [PATCH 04/15] CHANGE: multiple entites --- src/app/_models/mqtt-binary.ts | 10 +- src/app/_models/mqtt-switch.ts | 10 +- src/app/_models/mqtt_base.ts | 46 +++++- src/app/_services/generator.service.ts | 131 ++++++++++++++---- .../entity-data/entity-data.component.html | 31 +++-- src/app/entity-data/entity-data.component.ts | 17 ++- src/app/generator/generator.component.html | 6 +- src/app/generator/generator.component.ts | 5 + 8 files changed, 204 insertions(+), 52 deletions(-) diff --git a/src/app/_models/mqtt-binary.ts b/src/app/_models/mqtt-binary.ts index 4707f0c..14a0226 100644 --- a/src/app/_models/mqtt-binary.ts +++ b/src/app/_models/mqtt-binary.ts @@ -11,7 +11,15 @@ export class MqttBinary extends MQTTEntity { 'dev_cla', ]); - dev_cla: DeviceClass = new DeviceClass([ + get dev_cla(): DeviceClass { + return this._dev_cla; + } + + set dev_cla(data: number) { + this._dev_cla.value = data; + } + + _dev_cla: DeviceClass = new DeviceClass([ 'battery', 'battery_charging', 'carbon_monoxide', diff --git a/src/app/_models/mqtt-switch.ts b/src/app/_models/mqtt-switch.ts index 4915ae3..08d288a 100644 --- a/src/app/_models/mqtt-switch.ts +++ b/src/app/_models/mqtt-switch.ts @@ -12,7 +12,15 @@ export class MqttSwitch extends MQTTEntity { 'pl_on', 'pl_off', ]); - dev_cla: DeviceClass = new DeviceClass(['switch', 'outlet']); + get dev_cla(): DeviceClass { + return this._dev_cla; + } + + set dev_cla(data: number) { + this._dev_cla.value = data; + } + + _dev_cla: DeviceClass = new DeviceClass(['switch', 'outlet']); cmd_t: string = 'command/topic'; pl_on: string = '1'; pl_off: string = '0'; diff --git a/src/app/_models/mqtt_base.ts b/src/app/_models/mqtt_base.ts index 9a03d00..42028eb 100644 --- a/src/app/_models/mqtt_base.ts +++ b/src/app/_models/mqtt_base.ts @@ -17,7 +17,8 @@ export class MQTTEntity implements iMQTTEntityBase { set name(name: string) { this._name = name; - this._uniq_id = btoa(this._name).slice(-7).slice(0, 5); + if (name == '') return; + this._uniq_id = hash(name); this.topic_updates.next('stat_t'); } @@ -98,3 +99,46 @@ export interface iMQTTEntityBase { toJSON: () => string; } + +function hash(str: string): string { + let seed = cyrb128(str); + let rand = sfc32(seed[0], seed[1], seed[2], seed[3]); + let rand_num = Math.floor(rand() * 100000).toString(); + return rand_num; +} + +function cyrb128(str: string) { + let h1 = 1779033703, + h2 = 3144134277, + h3 = 1013904242, + h4 = 2773480762; + for (let i = 0, k; i < str.length; i++) { + k = str.charCodeAt(i); + h1 = h2 ^ Math.imul(h1 ^ k, 597399067); + h2 = h3 ^ Math.imul(h2 ^ k, 2869860233); + h3 = h4 ^ Math.imul(h3 ^ k, 951274213); + h4 = h1 ^ Math.imul(h4 ^ k, 2716044179); + } + h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067); + h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233); + h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213); + h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179); + (h1 ^= h2 ^ h3 ^ h4), (h2 ^= h1), (h3 ^= h1), (h4 ^= h1); + return [h1 >>> 0, h2 >>> 0, h3 >>> 0, h4 >>> 0]; +} + +function sfc32(a: number, b: number, c: number, d: number) { + return function () { + a |= 0; + b |= 0; + c |= 0; + d |= 0; + let t = (((a + b) | 0) + d) | 0; + d = (d + 1) | 0; + a = b ^ (b >>> 9); + b = (c + (c << 3)) | 0; + c = (c << 21) | (c >>> 11); + c = (c + t) | 0; + return (t >>> 0) / 4294967296; + }; +} diff --git a/src/app/_services/generator.service.ts b/src/app/_services/generator.service.ts index 6077039..2b3efe1 100644 --- a/src/app/_services/generator.service.ts +++ b/src/app/_services/generator.service.ts @@ -1,51 +1,130 @@ import { EventEmitter, Injectable, Input } from '@angular/core'; -import { MQTTEntity } from '../_models/mqtt_base'; -import { MqttLight } from '../_models/mqtt-light'; -import { MqttSwitch } from '../_models/mqtt-switch'; -import { MqttSensor } from '../_models/mqtt-sensor'; import { MqttBinary } from '../_models/mqtt-binary'; +import { MqttLight } from '../_models/mqtt-light'; +import { MqttSensor } from '../_models/mqtt-sensor'; +import { MqttSwitch } from '../_models/mqtt-switch'; +import { MQTTEntity } from '../_models/mqtt_base'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class GeneratorService { - public selected_entity: MQTTEntity | null = null; - public created_enteties: Array = []; + public _selected_entity: MQTTEntity | null = null; + public created_enteties: Set = new Set(); + // private entities: Map = new Map(); - @Input() device_name: string = ""; - @Input() device_id: string = ""; + @Input() device_name: string = ''; + @Input() device_id: string = ''; @Input() device_standalone: boolean = false; - @Input() upperTopic: string = ""; @Input() auto_topic: number = 2; + + _upperTopic: string = ''; + + get upperTopic(): string { + return this._upperTopic; + } + + set upperTopic(value: string) { + this._upperTopic = value; + for (let entity of [...this.created_enteties, this.selected_entity]) { + entity?.setProperty('stat_t', this.updateTopic(entity, 'stat_t')); + entity?.setProperty('cmd_t', this.updateTopic(entity, 'cmd_t')); + entity?.setProperty('bri_cmd_t', this.updateTopic(entity, 'bri_cmd_tt')); + } + } + updateObserver: EventEmitter = new EventEmitter(); - - constructor() { } - - get_properties() { - return Object.getOwnPropertyNames(this.selected_entity); + set selected_entity(entity: MQTTEntity | null) { + this._selected_entity = entity; + console.log('added'); + entity?.topic_updates.subscribe((topic: string) => { + entity.setProperty(topic, this.updateTopic(entity, topic)); + }); } - update(){ - this.updateObserver.emit(); + get selected_entity(): MQTTEntity | null { + return this._selected_entity; } + updateTopic(entity: MQTTEntity, topic: string) { + return join( + '/', + this.upperTopic, + entity.ent_type, + this.device_name, + entity.display_name, + topic + ).toLowerCase(); + } + + createEntity() { + console.log('Creating entity...'); + if (this._selected_entity) { + this.created_enteties.add(this._selected_entity); + this._selected_entity = null; + } + } + + // get_properties() { + // return Object.getOwnPropertyNames(this.selected_entity); + // } + + // update() { + // this.updateObserver.emit(); + // } + has_property(property: string): boolean { - if (this.selected_entity == null) return false; - if (this.selected_entity.hasOwnProperty(property)) return true; - return false + return false; } + // console.log(this.selected_entity); + // if (this.selected_entity == null) return false; + // if (this.selected_entity.attrs.has(property)) return true; + // if (this.selected_entity.hasOwnProperty(property)) return true; + // return false; + // } + // get_entity(key: string): MQTTEntity | undefined { + // return this.entities.get(key); + // if (res === undefined) return new MQTTEntity(); + // return res; + // } + + // add_entity(key: string, data: MQTTEntity) { + // this.entities.set(key, data); + // } + + entity_types: { [id: string]: [string, typeof MQTTEntity] } = { + '0': ['select type', MQTTEntity], + '1': ['light', MqttLight], + '2': ['switch', MqttSwitch], + '3': ['sensor', MqttSensor], + '4': ['binary_sensor', MqttBinary], + }; } export function randomString(length: number): string { - return Math.round((Math.pow(36, length + 1) - Math.random() * Math.pow(36, length))).toString(36).slice(1); + return Math.round( + Math.pow(36, length + 1) - Math.random() * Math.pow(36, length) + ) + .toString(36) + .slice(1); } export const entity_types: { [id: string]: [string, typeof MQTTEntity] } = { - '0': ["select type", MQTTEntity], - '1': ["light", MqttLight], - '2': ["switch", MqttSwitch], - '3': ["sensor", MqttSensor], - '4': ["binary_sensor", MqttBinary] + '0': ['select type', MQTTEntity], + '1': ['light', MqttLight], + '2': ['switch', MqttSwitch], + '3': ['sensor', MqttSensor], + '4': ['binary_sensor', MqttBinary], +}; + +function join(seperator = '/', first: string, ...args: string[]): string { + let result = ''; + for (let str of args) { + if (str == '') continue; + result += seperator + str; + } + if (first == '') return result.slice(1); + return first + result; } diff --git a/src/app/entity-data/entity-data.component.html b/src/app/entity-data/entity-data.component.html index ef15b2c..a38d060 100644 --- a/src/app/entity-data/entity-data.component.html +++ b/src/app/entity-data/entity-data.component.html @@ -1,13 +1,13 @@ -
+

Name

- +

Uniqe ID

- +

Command Topic

- +

Brightness Command Topic

- +

Payload on

- +

Payload off

- +

Unit of meassurement

- +

Value Template

- +
-
+

Device Class

-
- -

State Topic

- +
+
+ +
-
\ No newline at end of file + \ No newline at end of file diff --git a/src/app/entity-data/entity-data.component.ts b/src/app/entity-data/entity-data.component.ts index 19d0276..da3042b 100644 --- a/src/app/entity-data/entity-data.component.ts +++ b/src/app/entity-data/entity-data.component.ts @@ -1,5 +1,5 @@ import { Component, Input } from '@angular/core'; -import { MQTTEntity } from '../_models/mqtt_base'; +import { DeviceClass, MQTTEntity } from '../_models/mqtt_base'; import { EntityService } from '../_services/entity.service'; import { GeneratorService } from '../_services/generator.service'; @@ -11,9 +11,10 @@ import { GeneratorService } from '../_services/generator.service'; export class EntityDataComponent { @Input() entity_type: number = 0; @Input() basemodel!: MQTTEntity; + @Input() created: boolean = false; constructor( private entityService: EntityService, - private generatorService: GeneratorService + public generatorService: GeneratorService ) { if (generatorService.selected_entity) { this.basemodel = generatorService.selected_entity; @@ -29,7 +30,7 @@ export class EntityDataComponent { _entity_pl_on: string = ''; _entity_unit_of_meas: string = ''; _entity_val_tpl: string = ''; - _entity_dev_cla: string = ''; + _entity_dev_cla: DeviceClass = new DeviceClass([]); get entity_name() { let base = this.basemodel.getProperty('name'); @@ -137,12 +138,14 @@ export class EntityDataComponent { this.basemodel.setProperty('val_tpl', data); } - get entity_dev_cla() { - return this._entity_dev_cla; + get entity_dev_cla(): number { + this._entity_dev_cla = this.basemodel.getProperty('dev_cla'); + return this._entity_dev_cla.value; } - set entity_dev_cla(data: string) { - this._entity_dev_cla = data; + set entity_dev_cla(data: number) { + data = Number(data); + this._entity_dev_cla.value = data; this.basemodel.setProperty('dev_cla', data); } diff --git a/src/app/generator/generator.component.html b/src/app/generator/generator.component.html index f893625..a413641 100644 --- a/src/app/generator/generator.component.html +++ b/src/app/generator/generator.component.html @@ -30,6 +30,10 @@ + + + + - + \ No newline at end of file diff --git a/src/app/generator/generator.component.ts b/src/app/generator/generator.component.ts index 9ed328a..d8f5353 100644 --- a/src/app/generator/generator.component.ts +++ b/src/app/generator/generator.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { EntityService } from '../_services/entity.service'; import { GeneratorService, randomString } from '../_services/generator.service'; +import { MQTTEntity } from '../_models/mqtt_base'; @Component({ selector: 'app-home', @@ -25,6 +26,10 @@ export class GeneratorComponent { return String(this.generatorService.auto_topic); } + get created_entities(): MQTTEntity[] { + return Array.from(this.generatorService.created_enteties).reverse(); + } + entity_type: number = 0; select_type(etype: number) { -- 2.49.1 From 897120028cb776edc7bc61fe476a966e12076724 Mon Sep 17 00:00:00 2001 From: tom Date: Wed, 31 Jul 2024 13:06:38 +0200 Subject: [PATCH 05/15] CHANGE: autochange topics with custom ending in mind --- src/app/_models/mqtt_base.ts | 2 +- src/app/_services/generator.service.ts | 12 +++++--- src/app/generator/generator.component.html | 8 ++--- src/app/generator/generator.component.ts | 36 +++++++++------------- 4 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/app/_models/mqtt_base.ts b/src/app/_models/mqtt_base.ts index 42028eb..4c79368 100644 --- a/src/app/_models/mqtt_base.ts +++ b/src/app/_models/mqtt_base.ts @@ -4,7 +4,7 @@ import { EntityService } from '../_services/entity.service'; @Injectable() export class MQTTEntity implements iMQTTEntityBase { protected _name: string = ''; - protected _stat_t: string = ''; + protected _stat_t: string = 'state/topic'; protected _uniq_id: string = ''; attrs = new Set(['name', 'stat_t', 'uniq_id']); diff --git a/src/app/_services/generator.service.ts b/src/app/_services/generator.service.ts index 2b3efe1..735f828 100644 --- a/src/app/_services/generator.service.ts +++ b/src/app/_services/generator.service.ts @@ -16,7 +16,7 @@ export class GeneratorService { @Input() device_name: string = ''; @Input() device_id: string = ''; @Input() device_standalone: boolean = false; - @Input() auto_topic: number = 2; + @Input() auto_topic: boolean = true; _upperTopic: string = ''; @@ -37,9 +37,9 @@ export class GeneratorService { set selected_entity(entity: MQTTEntity | null) { this._selected_entity = entity; - console.log('added'); entity?.topic_updates.subscribe((topic: string) => { - entity.setProperty(topic, this.updateTopic(entity, topic)); + if (this.auto_topic) + entity.setProperty(topic, this.updateTopic(entity, topic)); }); } @@ -47,14 +47,18 @@ export class GeneratorService { return this._selected_entity; } + //TODO: add dynamic topic gen -> use already set topic if there are changes updateTopic(entity: MQTTEntity, topic: string) { + let topicStr: string = entity.getProperty(topic).split('/').pop(); + let customTopic = + topicStr == 'topic' || topicStr == topic ? topic : topicStr; return join( '/', this.upperTopic, entity.ent_type, this.device_name, entity.display_name, - topic + customTopic ).toLowerCase(); } diff --git a/src/app/generator/generator.component.html b/src/app/generator/generator.component.html index a413641..5d665d2 100644 --- a/src/app/generator/generator.component.html +++ b/src/app/generator/generator.component.html @@ -12,17 +12,15 @@

Automatische Topics

- + - - - +

EntityTyp:

-
diff --git a/src/app/generator/generator.component.ts b/src/app/generator/generator.component.ts index d8f5353..725083c 100644 --- a/src/app/generator/generator.component.ts +++ b/src/app/generator/generator.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, Input, ViewChild } from '@angular/core'; import { EntityService } from '../_services/entity.service'; import { GeneratorService, randomString } from '../_services/generator.service'; import { MQTTEntity } from '../_models/mqtt_base'; @@ -17,33 +17,27 @@ export class GeneratorComponent { readonly useObject = Object; readonly useRandomString = randomString; - set auto_topic(value: number) { - this.generatorService.auto_topic = value; - console.log(this.generatorService.auto_topic); - } - - get auto_topic(): string { - return String(this.generatorService.auto_topic); - } + @ViewChild('typeinput') typeinput: any; + @Input() entity_type: number = 0; get created_entities(): MQTTEntity[] { return Array.from(this.generatorService.created_enteties).reverse(); } - entity_type: number = 0; - - select_type(etype: number) { - let ent_type = + select_type(entity_num: number) { + this.entity_type = entity_num; + let entity_type = this.generatorService.entity_types[ - etype as keyof typeof this.generatorService.entity_types + entity_num as keyof typeof this.generatorService.entity_types ]; - let ent_class = ent_type[1]; - if (typeof ent_class === 'function' && etype != 0) { - let entity_model = new ent_class(); - this.generatorService.selected_entity = entity_model; - } else this.generatorService.selected_entity = null; - this.entity_type = etype; - console.log(this.generatorService.selected_entity); + let entity_class = entity_type[1]; + + if (typeof entity_class === 'function' && entity_num != 0) { + this.generatorService.selected_entity = new entity_class(); + } else { + this.generatorService.selected_entity = null; + this.typeinput.nativeElement.focus(); + } } log(event: any) { -- 2.49.1 From f98a3035e145c218342f7165498e759cf736e28e Mon Sep 17 00:00:00 2001 From: tom Date: Wed, 31 Jul 2024 13:06:52 +0200 Subject: [PATCH 06/15] CHANGE: focus on name input --- .../entity-data/entity-data.component.html | 6 +- src/app/entity-data/entity-data.component.ts | 14 +-- src/app/entity/entity.component.ts | 92 +++++++++---------- 3 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/app/entity-data/entity-data.component.html b/src/app/entity-data/entity-data.component.html index a38d060..6966270 100644 --- a/src/app/entity-data/entity-data.component.html +++ b/src/app/entity-data/entity-data.component.html @@ -2,18 +2,18 @@

Name

- +

Uniqe ID

-
diff --git a/src/app/entity-data/entity-data.component.ts b/src/app/entity-data/entity-data.component.ts index da3042b..9118c6a 100644 --- a/src/app/entity-data/entity-data.component.ts +++ b/src/app/entity-data/entity-data.component.ts @@ -1,6 +1,5 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, ViewChild } from '@angular/core'; import { DeviceClass, MQTTEntity } from '../_models/mqtt_base'; -import { EntityService } from '../_services/entity.service'; import { GeneratorService } from '../_services/generator.service'; @Component({ @@ -9,17 +8,18 @@ import { GeneratorService } from '../_services/generator.service'; styleUrl: './entity-data.component.css', }) export class EntityDataComponent { - @Input() entity_type: number = 0; + @Input() entity_type: number = 1; @Input() basemodel!: MQTTEntity; @Input() created: boolean = false; - constructor( - private entityService: EntityService, - public generatorService: GeneratorService - ) { + @ViewChild('nameinput') nameinput!: any; + constructor(public generatorService: GeneratorService) { if (generatorService.selected_entity) { this.basemodel = generatorService.selected_entity; } } + ngAfterViewInit() { + if (!this.created) this.nameinput.nativeElement.focus(); + } _entity_name: string = ''; _entity_uniq_id: string = ''; diff --git a/src/app/entity/entity.component.ts b/src/app/entity/entity.component.ts index cd6ecec..395be4f 100644 --- a/src/app/entity/entity.component.ts +++ b/src/app/entity/entity.component.ts @@ -49,23 +49,23 @@ export class EntityComponent { } update(model: keyof this = 'stat_t', event: Event | boolean = false): void { - if (this.generatorService.auto_topic == 2) { - this.stat_t = this.updateTopic('stat'); - this.entity_cmd_t = this.updateTopic('cmd'); - this.entity_bri_cmd_t = this.updateTopic('bri_cmd'); - return; - } - if (event instanceof Event) - var value = (event.target as HTMLInputElement).value; - else return; - if (this.generatorService.auto_topic == 1) { - this.sync_topcis(model, value); - } else { - if (!this.hasOwnProperty(model)) return; - if (typeof this[model] === 'string') { - this[model] = value as (typeof this)[keyof this]; - } - } + // if (this.generatorService.auto_topic == 2) { + // this.stat_t = this.updateTopic('stat'); + // this.entity_cmd_t = this.updateTopic('cmd'); + // this.entity_bri_cmd_t = this.updateTopic('bri_cmd'); + // return; + // } + // if (event instanceof Event) + // var value = (event.target as HTMLInputElement).value; + // else return; + // if (this.generatorService.auto_topic == 1) { + // this.sync_topcis(model, value); + // } else { + // if (!this.hasOwnProperty(model)) return; + // if (typeof this[model] === 'string') { + // this[model] = value as (typeof this)[keyof this]; + // } + // } } sync_topcis(model: keyof this, value: string) { @@ -74,7 +74,7 @@ export class EntityComponent { let missing = new_list.filter((item) => old_list.indexOf(item) < 0); if (old_list.filter((item) => new_list.indexOf(item) < 0).length > 1) - return (this.generatorService.auto_topic = 0); + return (this.generatorService.auto_topic = false); if (missing.length == 0) return; //(this.generatorService.auto_topic = 2); if (new_list.indexOf(missing[0]) == new_list.length - 1) { this[model] = value as (typeof this)[keyof this]; @@ -114,38 +114,38 @@ export class EntityComponent { return topic_str; } - select_type(event: unknown) { - let ent_type = entity_types[event as keyof typeof entity_types]; - let ent_class = ent_type[1]; - if (typeof ent_class === 'function' && event != 0) { - this.basemodel = new ent_class(); - this.generatorService.selected_entity = this.basemodel; - if (this.generatorService.has_property('dev_cla')) { - this.entity_dev_cla = this.basemodel.getProperty('dev_cla'); - } - } else this.generatorService.selected_entity = null; - this.entity_type = event as number; - this.update(); - } + select_type(event: unknown) {} + // let ent_type = entity_types[event as keyof typeof entity_types]; + // let ent_class = ent_type[1]; + // if (typeof ent_class === 'function' && event != 0) { + // this.basemodel = new ent_class(); + // this.generatorService.selected_entity = this.basemodel; + // // if (this.generatorService.has_property('dev_cla')) { + // // this.entity_dev_cla = this.basemodel.; + // // } + // } else this.generatorService.selected_entity = null; + // this.entity_type = event as number; + // this.update(); + // } lock_auto_topic() { - this.generatorService.auto_topic = 1; + // this.generatorService.auto_topic = 1; } - create_entity() { - this.basemodel?.setProperty('stat_t', this.stat_t); - this.basemodel?.setProperty('name', this.entity_name); - this.basemodel?.setProperty('cmd_t', this.entity_cmd_t); - this.basemodel?.setProperty('bri_cmd_t', this.entity_bri_cmd_t); - this.basemodel?.setProperty('pl_off', this.entity_pl_off); - this.basemodel?.setProperty('pl_on', this.entity_pl_on); - this.basemodel?.setProperty('unit_of_meas', this.entity_unit_of_meas); - this.basemodel?.setProperty('val_tpl', this.entity_val_tpl); - this.basemodel?.setProperty('dev_cla', this.entity_dev_cla); - this._discoveryTopic = this.discoveryTopic; - this.showDiscovery = true; - return; - } + create_entity() {} + // this.basemodel?.setProperty('stat_t', this.stat_t); + // this.basemodel?.setProperty('name', this.entity_name); + // this.basemodel?.setProperty('cmd_t', this.entity_cmd_t); + // this.basemodel?.setProperty('bri_cmd_t', this.entity_bri_cmd_t); + // this.basemodel?.setProperty('pl_off', this.entity_pl_off); + // this.basemodel?.setProperty('pl_on', this.entity_pl_on); + // this.basemodel?.setProperty('unit_of_meas', this.entity_unit_of_meas); + // this.basemodel?.setProperty('val_tpl', this.entity_val_tpl); + // this.basemodel?.setProperty('dev_cla', this.entity_dev_cla); + // this._discoveryTopic = this.discoveryTopic; + // this.showDiscovery = true; + // return; + // } get discoveryString() { return this.basemodel?.toJSON(); -- 2.49.1 From 15365cfbfeb887a647f6f25e5a491c995bfa4ced Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 2 Aug 2024 07:07:44 +0200 Subject: [PATCH 07/15] CHNAGE: added size comparism --- src/app/_models/mqtt_base.ts | 6 ++++++ src/app/_services/output.service.ts | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/app/_services/output.service.ts diff --git a/src/app/_models/mqtt_base.ts b/src/app/_models/mqtt_base.ts index 4c79368..9bc4aad 100644 --- a/src/app/_models/mqtt_base.ts +++ b/src/app/_models/mqtt_base.ts @@ -56,6 +56,12 @@ export class MQTTEntity implements iMQTTEntityBase { } return jsonObject; } + + get size(): number { + this._size = JSON.stringify(this.toJSON()).length; + return this._size; + } + getProperty(name: string): any { if (!this.attrs.has(name)) return; return this[name as keyof this]; diff --git a/src/app/_services/output.service.ts b/src/app/_services/output.service.ts new file mode 100644 index 0000000..761ee31 --- /dev/null +++ b/src/app/_services/output.service.ts @@ -0,0 +1,19 @@ + get device_entity(): MQTTEntity { + if ( + this._device_entity_cached.listlen === + this.generatorService.created_entity_num + ) + return this._device_entity_cached.entity; + let entites = Array.from(this.generatorService.created_enteties).sort( + (a, b) => { + return a.size - b.size; + } + ); + this._device_entity_cached = { + entity: entites[0], + listlen: this.generatorService.created_entity_num, + }; + console.log('sizing'); + console.log(this._device_entity_cached); + return entites[0]; + } -- 2.49.1 From f6fb4f6b617995f6baa0c383aff7f3529c8e4c32 Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 2 Aug 2024 07:08:50 +0200 Subject: [PATCH 08/15] ADD: Discovery Output with code integration --- src/app/_services/output.service.spec.ts | 16 ++++ src/app/_services/output.service.ts | 100 +++++++++++++++++++++++ src/app/output/output.component.css | 5 ++ src/app/output/output.component.html | 5 ++ src/app/output/output.component.spec.ts | 23 ++++++ src/app/output/output.component.ts | 11 +++ 6 files changed, 160 insertions(+) create mode 100644 src/app/_services/output.service.spec.ts create mode 100644 src/app/output/output.component.css create mode 100644 src/app/output/output.component.html create mode 100644 src/app/output/output.component.spec.ts create mode 100644 src/app/output/output.component.ts diff --git a/src/app/_services/output.service.spec.ts b/src/app/_services/output.service.spec.ts new file mode 100644 index 0000000..4e894f1 --- /dev/null +++ b/src/app/_services/output.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { OutputService } from './output.service'; + +describe('OutputService', () => { + let service: OutputService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(OutputService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/_services/output.service.ts b/src/app/_services/output.service.ts index 761ee31..a6d782a 100644 --- a/src/app/_services/output.service.ts +++ b/src/app/_services/output.service.ts @@ -1,3 +1,20 @@ +import { Injectable } from '@angular/core'; +import { MQTTEntity } from '../_models/mqtt_base'; +import { GeneratorService } from './generator.service'; + +@Injectable({ + providedIn: 'root', +}) +export class OutputService { + constructor(private generatorService: GeneratorService) {} + basecode: {} = { 'mqtt-init': '{testestsdsdf}' }; + output: boolean = false; + _integrated_output: boolean = false; + _device_entity_cached: { entity: MQTTEntity; listlen: number } = { + entity: new MQTTEntity(), + listlen: 0, + }; + get device_entity(): MQTTEntity { if ( this._device_entity_cached.listlen === @@ -17,3 +34,86 @@ console.log(this._device_entity_cached); return entites[0]; } + + get seperate_outputs(): boolean { + return this.output && !this._integrated_output; + } + + get integrated_output(): boolean { + return this.output && this._integrated_output; + } + + set integrated_output(value: boolean) { + this._integrated_output = value; + } + + getDiscoveryString(entity: MQTTEntity, escaped = false): string | {} { + let str = entity.toJSON(); + if (this.generatorService.use_device) { + str['dev'] = this.generatorService.device.toJSON( + entity.uniq_id == this.device_entity.uniq_id + ); + } + + if (escaped) { + return JSON.stringify(str).replaceAll('"', '\\"'); + } + return str; + } + + getDiscoveryTopic(entity: MQTTEntity): string { + return join( + '/', + 'homeassistant', + entity.ent_type, + entity.display_name, + 'config' + ).toLowerCase(); + } + + integrateCode(): string { + let mqtt_init = + '#include \n\nEspMQTTClient mqtt(\n\t"wifi_name",\n\t"wifi_pw",\n\t"broker_ip",\n\t"mqtt_acc",\n\t"mqtt_pw",\n\t"device_name",\n\t"mqtt_port"\n);\n'; + let setup = 'void setup(){\nclient.setMaxPacketSize(512);\n}\n'; + let onConnectStart = 'void onConnectionEstablished() {\n\tdelay=10\n'; + let onConnectEnd = '\tmqtt.loop()\n}\n'; + let loop = 'void loop() {\n\tmqtt.loop();\n\tdelay(100);\n}'; + + let discoveryMsg: string[] = []; + let publishTopics: string[] = []; + for (let [index, entity] of Array.from( + this.generatorService.created_enteties + ).entries()) { + discoveryMsg.push('\t// Sending discovery for Entity ' + index); + discoveryMsg.push( + `\tmqtt.publish(${this.getDiscoveryTopic( + entity + )}, ${this.getDiscoveryString(entity, true)}, true);\n` + ); + + publishTopics.push(...entity.publish_topics(index)); + publishTopics.push(); + } + publishTopics.push('\n'); + return ( + mqtt_init + + '\n' + + publishTopics.join('\n') + + setup + + onConnectStart + + discoveryMsg.join('\n') + + onConnectEnd + + loop + ); + } +} + +function join(seperator = '/', first: string, ...args: string[]): string { + let result = ''; + for (let str of args) { + if (str == '') continue; + result += seperator + str; + } + if (first == '') return result.slice(1); + return first + result; +} diff --git a/src/app/output/output.component.css b/src/app/output/output.component.css new file mode 100644 index 0000000..3124112 --- /dev/null +++ b/src/app/output/output.component.css @@ -0,0 +1,5 @@ +pre { + max-width: calc(50vw - 1rem); + overflow-x: scroll; + text-wrap: nowrap; +} diff --git a/src/app/output/output.component.html b/src/app/output/output.component.html new file mode 100644 index 0000000..2cafb6a --- /dev/null +++ b/src/app/output/output.component.html @@ -0,0 +1,5 @@ +
+

Code Output

+
{{outputService.integrateCode()}}
+

+
diff --git a/src/app/output/output.component.spec.ts b/src/app/output/output.component.spec.ts new file mode 100644 index 0000000..6ac7b51 --- /dev/null +++ b/src/app/output/output.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { OutputComponent } from './output.component'; + +describe('OutputComponent', () => { + let component: OutputComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [OutputComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(OutputComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/output/output.component.ts b/src/app/output/output.component.ts new file mode 100644 index 0000000..dc5a3f8 --- /dev/null +++ b/src/app/output/output.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; +import { OutputService } from '../_services/output.service'; + +@Component({ + selector: 'app-output', + templateUrl: './output.component.html', + styleUrl: './output.component.css', +}) +export class OutputComponent { + constructor(public outputService: OutputService) {} +} -- 2.49.1 From e6a49d55e2d3b0452d015c144bf981868a4763c3 Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 2 Aug 2024 07:09:46 +0200 Subject: [PATCH 09/15] CHANGE: seperated mqtt device --- src/app/_models/mqtt-device.spec.ts | 7 ++++ src/app/_models/mqtt-device.ts | 52 +++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 src/app/_models/mqtt-device.spec.ts create mode 100644 src/app/_models/mqtt-device.ts diff --git a/src/app/_models/mqtt-device.spec.ts b/src/app/_models/mqtt-device.spec.ts new file mode 100644 index 0000000..3e4378c --- /dev/null +++ b/src/app/_models/mqtt-device.spec.ts @@ -0,0 +1,7 @@ +import { MqttDevice } from './mqtt-device'; + +describe('MqttDevice', () => { + it('should create an instance', () => { + expect(new MqttDevice()).toBeTruthy(); + }); +}); diff --git a/src/app/_models/mqtt-device.ts b/src/app/_models/mqtt-device.ts new file mode 100644 index 0000000..90066a6 --- /dev/null +++ b/src/app/_models/mqtt-device.ts @@ -0,0 +1,52 @@ +import { EventEmitter } from "@angular/core"; +import { hash } from "./mqtt_base"; + +export class MQTTDevice { + private _name: string = ''; + private _identifier: string = ''; + private _serial_number: string = ''; + // private configuration_url: string = ''; + + topic_updates = new EventEmitter(); + + get name() { + return this._name; + } + + set name(name: string) { + this._name = name; + if (name == '') this._serial_number = ''; + else this._serial_number = hash(name); + this.topic_updates.next('stat_t'); + this.topic_updates.next('cmd_t'); + this.topic_updates.next('bri_cmd_t'); + } + + get serial_number() { + return this._serial_number; + } + + set serial_number(serial_number: string) { + this._serial_number = serial_number; + } + + get indetifier() { + this._identifier = + this._name == '' && this._serial_number == '' + ? '' + : [this._name, this._serial_number].join('_'); + return this._identifier; + } + + toJSON(full: boolean = false): { [key: string]: string } { + if (full) + return { + ...this.toJSON(), + name: this._name, + serial_number: this._serial_number, + }; + return { + ids: this._identifier, + }; + } + } \ No newline at end of file -- 2.49.1 From 942142f517d21d77115190877dff19b74215182b Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 2 Aug 2024 07:10:09 +0200 Subject: [PATCH 10/15] CHANGE: added publish topic functions --- src/app/_models/mqtt-light.ts | 10 +++++++- src/app/_models/mqtt-switch.ts | 8 +++++- src/app/_models/mqtt_base.ts | 45 ++++++++++++++++------------------ 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/src/app/_models/mqtt-light.ts b/src/app/_models/mqtt-light.ts index 27108f6..dea55b3 100644 --- a/src/app/_models/mqtt-light.ts +++ b/src/app/_models/mqtt-light.ts @@ -1,4 +1,4 @@ -import { iMQTTEntityBase, MQTTEntity } from './mqtt_base'; +import { MQTTEntity } from './mqtt_base'; export class MqttLight extends MQTTEntity { pl_on: string = '1'; @@ -45,4 +45,12 @@ export class MqttLight extends MQTTEntity { override get uniq_id() { return this._uniq_id; } + + override publish_topics(index: number = 1): string[] { + return [ + `String stat_topic_${index} = "${this.stat_t}";`, + `String cmd_topic_${index} = "${this.cmd_t}";`, + `String bri_cmd_topic_${index} = "${this.bri_cmd_t}";`, + ]; + } } diff --git a/src/app/_models/mqtt-switch.ts b/src/app/_models/mqtt-switch.ts index 08d288a..60eee29 100644 --- a/src/app/_models/mqtt-switch.ts +++ b/src/app/_models/mqtt-switch.ts @@ -1,4 +1,3 @@ -import { EntityService } from '../_services/entity.service'; import { DeviceClass, iMQTTEntityBase, MQTTEntity } from './mqtt_base'; export class MqttSwitch extends MQTTEntity { @@ -41,6 +40,13 @@ export class MqttSwitch extends MQTTEntity { override get uniq_id() { return this._uniq_id; } + + override publish_topics(index: number = 1): string[] { + return [ + `String stat_topic_${index} = "${this.stat_t}";`, + `String cmd_topic_${index} = "${this.cmd_t}";`, + ]; + } } export interface iMqttSwitch extends iMQTTEntityBase { diff --git a/src/app/_models/mqtt_base.ts b/src/app/_models/mqtt_base.ts index 9bc4aad..f86a402 100644 --- a/src/app/_models/mqtt_base.ts +++ b/src/app/_models/mqtt_base.ts @@ -1,11 +1,11 @@ import { EventEmitter, Injectable } from '@angular/core'; -import { EntityService } from '../_services/entity.service'; @Injectable() export class MQTTEntity implements iMQTTEntityBase { protected _name: string = ''; protected _stat_t: string = 'state/topic'; protected _uniq_id: string = ''; + private _size: number = 0; attrs = new Set(['name', 'stat_t', 'uniq_id']); topic_updates = new EventEmitter(); @@ -18,7 +18,7 @@ export class MQTTEntity implements iMQTTEntityBase { set name(name: string) { this._name = name; if (name == '') return; - this._uniq_id = hash(name); + this._uniq_id = name + '_' + hash(name); this.topic_updates.next('stat_t'); } @@ -40,16 +40,19 @@ export class MQTTEntity implements iMQTTEntityBase { } get display_name() { - if (this._name != '' && this._uniq_id != '') { - return this._name + '_' + this._uniq_id; - } else { - return this._name; + if (this._uniq_id != '') { + return this._uniq_id; } + return this._name; } - toJSON(): any { - let jsonObject: { [key: string]: string } = {}; - for (let prop_name of Object.getOwnPropertyNames(this)) { + publish_topics(index: number = 1): string[] { + return [`String stat_topic_${index} = "${this.stat_t}";`]; + } + + toJSON(): { [key: string]: string | {} } { + let jsonObject: { [key: string]: string | {} } = {}; + for (let prop_name of this.attrs.values()) { let property = this[prop_name as keyof this]; if (property == '') continue; jsonObject[prop_name] = String(property); @@ -63,7 +66,7 @@ export class MQTTEntity implements iMQTTEntityBase { } getProperty(name: string): any { - if (!this.attrs.has(name)) return; + if (!this.attrs.has(name)) return ''; return this[name as keyof this]; } @@ -73,13 +76,6 @@ export class MQTTEntity implements iMQTTEntityBase { } } -export class MQTTDevice { - name: string = ''; - identifiers: string[] = ['MQTT']; - serial_number: string = ''; - configuration_url: string = ''; -} - export class DeviceClass { value: number = 0; choices: string[] = ['None']; @@ -94,22 +90,23 @@ export class DeviceClass { } export interface iMQTTEntityBase { - // name: string; - // stat_t: string; - // uniq_id: string; - // display_name: string; + name: string; + stat_t: string; + uniq_id: string; + display_name: string; attrs: Set; readonly ent_type: string; topic_updates: EventEmitter; - toJSON: () => string; + publish_topics: (index: number) => string[]; + toJSON: () => { [key: string]: string | {} }; } -function hash(str: string): string { +export function hash(str: string, digits: number = 5): string { let seed = cyrb128(str); let rand = sfc32(seed[0], seed[1], seed[2], seed[3]); - let rand_num = Math.floor(rand() * 100000).toString(); + let rand_num = Math.floor(rand() * 10 ** digits).toString(); return rand_num; } -- 2.49.1 From f4eea9bebefcf4946cf9971c294a58bab0c37c1f Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 2 Aug 2024 07:34:09 +0200 Subject: [PATCH 11/15] CHANGE: Created seperate output for discovery --- .../entity-output/entity-output.component.css | 5 + .../entity-output.component.html | 13 +- .../entity-output/entity-output.component.ts | 9 +- src/app/entity/entity.component.css | 3 - src/app/entity/entity.component.html | 80 -------- src/app/entity/entity.component.spec.ts | 23 --- src/app/entity/entity.component.ts | 177 ------------------ 7 files changed, 23 insertions(+), 287 deletions(-) delete mode 100644 src/app/entity/entity.component.css delete mode 100644 src/app/entity/entity.component.html delete mode 100644 src/app/entity/entity.component.spec.ts delete mode 100644 src/app/entity/entity.component.ts diff --git a/src/app/entity-output/entity-output.component.css b/src/app/entity-output/entity-output.component.css index e69de29..bff0f60 100644 --- a/src/app/entity-output/entity-output.component.css +++ b/src/app/entity-output/entity-output.component.css @@ -0,0 +1,5 @@ +.jsonPrev { + max-width: 50%; + text-wrap: wrap; + flex-shrink: 0; +} diff --git a/src/app/entity-output/entity-output.component.html b/src/app/entity-output/entity-output.component.html index 01cc950..d372171 100644 --- a/src/app/entity-output/entity-output.component.html +++ b/src/app/entity-output/entity-output.component.html @@ -1 +1,12 @@ -

entity-output works!

+ +
+

Discovery String

+
+
{{outputService.getDiscoveryString(basemodel) | json}}
+ {{outputService.getDiscoveryString(basemodel, true)}} +
+
+
+

Discovery Topic

+ +
\ No newline at end of file diff --git a/src/app/entity-output/entity-output.component.ts b/src/app/entity-output/entity-output.component.ts index df60952..d6e962c 100644 --- a/src/app/entity-output/entity-output.component.ts +++ b/src/app/entity-output/entity-output.component.ts @@ -1,10 +1,13 @@ -import { Component } from '@angular/core'; +import { Component, Input } from '@angular/core'; +import { MQTTEntity } from '../_models/mqtt_base'; +import { OutputService } from '../_services/output.service'; @Component({ selector: 'app-entity-output', templateUrl: './entity-output.component.html', - styleUrl: './entity-output.component.css' + styleUrl: './entity-output.component.css', }) export class EntityOutputComponent { - + constructor(public outputService: OutputService) {} + @Input() basemodel!: MQTTEntity; } diff --git a/src/app/entity/entity.component.css b/src/app/entity/entity.component.css deleted file mode 100644 index 5a303fc..0000000 --- a/src/app/entity/entity.component.css +++ /dev/null @@ -1,3 +0,0 @@ -pre{ - -} \ No newline at end of file diff --git a/src/app/entity/entity.component.html b/src/app/entity/entity.component.html deleted file mode 100644 index cf2f9e2..0000000 --- a/src/app/entity/entity.component.html +++ /dev/null @@ -1,80 +0,0 @@ -
- -
-

EntityTyp:

- -
- -
-

Name

- -
-
-

Uniqe ID

-
- - -
-
-
-

Command Topic

- -
-
-

Brightness Command Topic

- -
-
-
-

Payload off

- -
-
-

Payload on

- -
-
-
-

Unit of meassurement

- -
-
-

Value Template

- -
-
-

Device Class

- -
- - -
-

State Topic

-
- -
-
- - -
-

Discovery String

-
-
{{discoveryString | json}}
- {{JSONdiscoveryString}} -
-
-
-

Discovery Topic

- -
-
-
\ No newline at end of file diff --git a/src/app/entity/entity.component.spec.ts b/src/app/entity/entity.component.spec.ts deleted file mode 100644 index d9e8dfb..0000000 --- a/src/app/entity/entity.component.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { EntityComponent } from './entity.component'; - -describe('EntityComponent', () => { - let component: EntityComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [EntityComponent] - }) - .compileComponents(); - - fixture = TestBed.createComponent(EntityComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/entity/entity.component.ts b/src/app/entity/entity.component.ts deleted file mode 100644 index 395be4f..0000000 --- a/src/app/entity/entity.component.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { - GeneratorService, - entity_types, - randomString, -} from '../_services/generator.service'; -import { MqttBinary } from '../_models/mqtt-binary'; -import { DeviceClass, MQTTEntity } from '../_models/mqtt_base'; - -@Component({ - selector: 'app-entity', - templateUrl: './entity.component.html', - styleUrl: './entity.component.css', -}) -export class EntityComponent { - constructor(public generatorService: GeneratorService) { - generatorService.updateObserver.subscribe((date) => { - this.update(); - }); - } - - readonly useObject = Object; - readonly entities = entity_types; - readonly useRandomString = randomString; - - showDiscovery: boolean = false; - _discoveryTopic: string = ''; - - @Input() entity_type: number = 0; - @Input() stat_t: string = ''; - @Input() basemodel: MQTTEntity | null = null; - - @Input() entity_name: string = ''; - @Input() entity_uniq_id: string = ''; - @Input() entity_cmd_t: string = ''; - @Input() entity_bri_cmd_t: string = ''; - @Input() entity_pl_off: string = '0'; - @Input() entity_pl_on: string = '1'; - @Input() entity_unit_of_meas: string = ''; - @Input() entity_val_tpl: string = ''; - @Input() entity_dev_cla: DeviceClass = new DeviceClass([]); - - get display_name() { - if (this.entity_name != '' && this.entity_uniq_id != '') { - return this.entity_name + '_' + this.entity_uniq_id; - } else { - return this.entity_name; - } - } - - update(model: keyof this = 'stat_t', event: Event | boolean = false): void { - // if (this.generatorService.auto_topic == 2) { - // this.stat_t = this.updateTopic('stat'); - // this.entity_cmd_t = this.updateTopic('cmd'); - // this.entity_bri_cmd_t = this.updateTopic('bri_cmd'); - // return; - // } - // if (event instanceof Event) - // var value = (event.target as HTMLInputElement).value; - // else return; - // if (this.generatorService.auto_topic == 1) { - // this.sync_topcis(model, value); - // } else { - // if (!this.hasOwnProperty(model)) return; - // if (typeof this[model] === 'string') { - // this[model] = value as (typeof this)[keyof this]; - // } - // } - } - - sync_topcis(model: keyof this, value: string) { - let new_list = value.split('/'); - let old_list = String(this[model]).split('/'); - let missing = new_list.filter((item) => old_list.indexOf(item) < 0); - - if (old_list.filter((item) => new_list.indexOf(item) < 0).length > 1) - return (this.generatorService.auto_topic = false); - if (missing.length == 0) return; //(this.generatorService.auto_topic = 2); - if (new_list.indexOf(missing[0]) == new_list.length - 1) { - this[model] = value as (typeof this)[keyof this]; - return; - } - let missing_index = new_list.indexOf(missing[0]); - this.stat_t = this.replaceComponent(this.stat_t, missing[0], missing_index); - this.entity_cmd_t = this.replaceComponent( - this.entity_cmd_t, - missing[0], - missing_index - ); - this.entity_bri_cmd_t = this.replaceComponent( - this.entity_bri_cmd_t, - missing[0], - missing_index - ); - - return; - } - - replaceComponent(object: string, value: string, index: number) { - let param_list = object.split('/'); - param_list[index] = value; - return param_list.join('/'); - } - - updateTopic(topic: string) { - let topic_str = join( - '/', - this.generatorService.upperTopic, - entity_types[this.entity_type][0], - this.generatorService.device_name, - this.display_name, - topic - ).toLowerCase(); - return topic_str; - } - - select_type(event: unknown) {} - // let ent_type = entity_types[event as keyof typeof entity_types]; - // let ent_class = ent_type[1]; - // if (typeof ent_class === 'function' && event != 0) { - // this.basemodel = new ent_class(); - // this.generatorService.selected_entity = this.basemodel; - // // if (this.generatorService.has_property('dev_cla')) { - // // this.entity_dev_cla = this.basemodel.; - // // } - // } else this.generatorService.selected_entity = null; - // this.entity_type = event as number; - // this.update(); - // } - - lock_auto_topic() { - // this.generatorService.auto_topic = 1; - } - - create_entity() {} - // this.basemodel?.setProperty('stat_t', this.stat_t); - // this.basemodel?.setProperty('name', this.entity_name); - // this.basemodel?.setProperty('cmd_t', this.entity_cmd_t); - // this.basemodel?.setProperty('bri_cmd_t', this.entity_bri_cmd_t); - // this.basemodel?.setProperty('pl_off', this.entity_pl_off); - // this.basemodel?.setProperty('pl_on', this.entity_pl_on); - // this.basemodel?.setProperty('unit_of_meas', this.entity_unit_of_meas); - // this.basemodel?.setProperty('val_tpl', this.entity_val_tpl); - // this.basemodel?.setProperty('dev_cla', this.entity_dev_cla); - // this._discoveryTopic = this.discoveryTopic; - // this.showDiscovery = true; - // return; - // } - - get discoveryString() { - return this.basemodel?.toJSON(); - } - - get JSONdiscoveryString() { - return JSON.stringify(this.basemodel?.toJSON()).replaceAll('"', '\\"'); - } - - get discoveryTopic() { - if (this.entity_type == 0) return ''; - return join( - '/', - 'homeassistant', - entity_types[this.entity_type][0], - this.display_name - ).toLowerCase(); - } -} - -function join(seperator = '/', first: string, ...args: string[]): string { - let result = ''; - for (let str of args) { - if (str == '') continue; - result += seperator + str; - } - if (first == '') return result.slice(1); - return first + result; -} -- 2.49.1 From 0403eb90d3751e3d4d86267a30614a6bb4ac7e09 Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 2 Aug 2024 07:34:57 +0200 Subject: [PATCH 12/15] ADD: Device and Output control --- src/app/_services/generator.service.ts | 79 +++++++--------------- src/app/generator/generator.component.css | 66 +++++++++++------- src/app/generator/generator.component.html | 67 ++++++++++++++---- src/app/generator/generator.component.ts | 19 +++--- 4 files changed, 132 insertions(+), 99 deletions(-) diff --git a/src/app/_services/generator.service.ts b/src/app/_services/generator.service.ts index 735f828..5abdd63 100644 --- a/src/app/_services/generator.service.ts +++ b/src/app/_services/generator.service.ts @@ -4,6 +4,7 @@ import { MqttLight } from '../_models/mqtt-light'; import { MqttSensor } from '../_models/mqtt-sensor'; import { MqttSwitch } from '../_models/mqtt-switch'; import { MQTTEntity } from '../_models/mqtt_base'; +import { MQTTDevice } from '../_models/mqtt-device'; @Injectable({ providedIn: 'root', @@ -11,25 +12,31 @@ import { MQTTEntity } from '../_models/mqtt_base'; export class GeneratorService { public _selected_entity: MQTTEntity | null = null; public created_enteties: Set = new Set(); - // private entities: Map = new Map(); - @Input() device_name: string = ''; - @Input() device_id: string = ''; - @Input() device_standalone: boolean = false; @Input() auto_topic: boolean = true; + @Input() use_device: boolean = false; - _upperTopic: string = ''; + device: MQTTDevice = new MQTTDevice(); + _upper_topic: string = ''; - get upperTopic(): string { - return this._upperTopic; + constructor() { + this.device.topic_updates.subscribe((topic) => { + for (let entity of [...this.created_enteties, this.selected_entity]) { + entity?.setProperty(topic, this.updateTopic(entity, topic)); + } + }); } - set upperTopic(value: string) { - this._upperTopic = value; + get upper_topic(): string { + return this._upper_topic; + } + + set upper_topic(value: string) { + this._upper_topic = value; for (let entity of [...this.created_enteties, this.selected_entity]) { entity?.setProperty('stat_t', this.updateTopic(entity, 'stat_t')); entity?.setProperty('cmd_t', this.updateTopic(entity, 'cmd_t')); - entity?.setProperty('bri_cmd_t', this.updateTopic(entity, 'bri_cmd_tt')); + entity?.setProperty('bri_cmd_t', this.updateTopic(entity, 'bri_cmd_t')); } } @@ -47,16 +54,18 @@ export class GeneratorService { return this._selected_entity; } - //TODO: add dynamic topic gen -> use already set topic if there are changes + get created_entity_num(): number { + return Array.from(this.created_enteties).length; + } + updateTopic(entity: MQTTEntity, topic: string) { let topicStr: string = entity.getProperty(topic).split('/').pop(); let customTopic = topicStr == 'topic' || topicStr == topic ? topic : topicStr; return join( '/', - this.upperTopic, - entity.ent_type, - this.device_name, + this.upper_topic, + this.use_device ? this.device.indetifier : entity.ent_type, entity.display_name, customTopic ).toLowerCase(); @@ -70,33 +79,13 @@ export class GeneratorService { } } - // get_properties() { - // return Object.getOwnPropertyNames(this.selected_entity); - // } - - // update() { - // this.updateObserver.emit(); - // } + deleteEntity(entity: MQTTEntity) { + this.created_enteties.delete(entity); + } has_property(property: string): boolean { return false; } - // console.log(this.selected_entity); - // if (this.selected_entity == null) return false; - // if (this.selected_entity.attrs.has(property)) return true; - // if (this.selected_entity.hasOwnProperty(property)) return true; - // return false; - // } - - // get_entity(key: string): MQTTEntity | undefined { - // return this.entities.get(key); - // if (res === undefined) return new MQTTEntity(); - // return res; - // } - - // add_entity(key: string, data: MQTTEntity) { - // this.entities.set(key, data); - // } entity_types: { [id: string]: [string, typeof MQTTEntity] } = { '0': ['select type', MQTTEntity], @@ -107,22 +96,6 @@ export class GeneratorService { }; } -export function randomString(length: number): string { - return Math.round( - Math.pow(36, length + 1) - Math.random() * Math.pow(36, length) - ) - .toString(36) - .slice(1); -} - -export const entity_types: { [id: string]: [string, typeof MQTTEntity] } = { - '0': ['select type', MQTTEntity], - '1': ['light', MqttLight], - '2': ['switch', MqttSwitch], - '3': ['sensor', MqttSensor], - '4': ['binary_sensor', MqttBinary], -}; - function join(seperator = '/', first: string, ...args: string[]): string { let result = ''; for (let str of args) { diff --git a/src/app/generator/generator.component.css b/src/app/generator/generator.component.css index cd945ae..1b35e87 100644 --- a/src/app/generator/generator.component.css +++ b/src/app/generator/generator.component.css @@ -1,32 +1,52 @@ +#mainContainer { + display: grid; + grid-template-columns: 3fr 1fr; + gap: 0.5rem; + transition: all 0.25s; +} + +.genContainer, +.outContainer { + background: #9d9d9d; + border-radius: 1rem; + padding: 1rem 0.8rem; + gap: 1rem; +} + .genContainer { - background: #9D9D9D; - border-radius: 1rem; - padding: 1rem .8rem ; - display: flex; - flex-direction: column; - gap: 1rem; + grid-column: 1; } -.customCheckboxContainer{ - display: flex; - flex-direction: row; - gap: .5rem; +.outContainer { + display: flex; + flex-direction: column; + grid-column: 2; } -.customCheckboxContainer input[type="radio"]{ - display: none; +.customCheckboxContainer { + display: flex; + flex-direction: row; + gap: 0.5rem; } -.customCheckbox{ - width: 100%; - padding: .25rem 0; - text-align: center; - background: #B3B3B3; - transition: background .25s ease-in-out, color .25s ease-in-out; - user-select: none; +.customCheckboxContainer input { + display: none; } -.customCheckboxContainer input:checked + label{ - background: var(--accent); - color: var(--secondary); -} \ No newline at end of file +.customCheckbox { + width: 100%; + padding: 0.25rem 0; + text-align: center; + background: #b3b3b3; + transition: background 0.25s ease-in-out, color 0.25s ease-in-out; + user-select: none; +} + +.customCheckboxContainer input:checked + label { + background: var(--accent); + color: var(--secondary); +} + +.outEnable { + grid-template-columns: 1fr 1fr !important; +} diff --git a/src/app/generator/generator.component.html b/src/app/generator/generator.component.html index 5d665d2..3dced6c 100644 --- a/src/app/generator/generator.component.html +++ b/src/app/generator/generator.component.html @@ -1,23 +1,44 @@ -
-
+
+
-
+

Bereich

- +
-
-

Automatische Topics

-
- - - - +
+

Device

+
+ +
+
+

Automatic Topics

+
+ + +
+
+ +
+

Device Name

+ +
+
+

Device Identifier

+ +
+ +

EntityTyp:

+ +
+

Seperate Output

+
+ + +
+
+ + - + + + + +
\ No newline at end of file diff --git a/src/app/generator/generator.component.ts b/src/app/generator/generator.component.ts index 725083c..4bb234d 100644 --- a/src/app/generator/generator.component.ts +++ b/src/app/generator/generator.component.ts @@ -1,7 +1,8 @@ import { Component, Input, ViewChild } from '@angular/core'; -import { EntityService } from '../_services/entity.service'; -import { GeneratorService, randomString } from '../_services/generator.service'; +import { GeneratorService } from '../_services/generator.service'; import { MQTTEntity } from '../_models/mqtt_base'; +import { OutputService } from '../_services/output.service'; +import { MQTTDevice } from '../_models/mqtt-device'; @Component({ selector: 'app-home', @@ -11,19 +12,25 @@ import { MQTTEntity } from '../_models/mqtt_base'; export class GeneratorComponent { constructor( public generatorService: GeneratorService, - private entityService: EntityService + public outputService: OutputService ) {} readonly useObject = Object; - readonly useRandomString = randomString; @ViewChild('typeinput') typeinput: any; @Input() entity_type: number = 0; + device: MQTTDevice = this.generatorService.device; + get created_entities(): MQTTEntity[] { return Array.from(this.generatorService.created_enteties).reverse(); } + get codeSpan(): string { + let start = this.generatorService.selected_entity == null ? 3 : 2; + return `${start}/${3 + this.generatorService.created_entity_num}`; + } + select_type(entity_num: number) { this.entity_type = entity_num; let entity_type = @@ -39,8 +46,4 @@ export class GeneratorComponent { this.typeinput.nativeElement.focus(); } } - - log(event: any) { - console.log(Number(event)); - } } -- 2.49.1 From ddadb1f14b29573b59723ec03682823cc5eb5c76 Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 2 Aug 2024 07:35:42 +0200 Subject: [PATCH 13/15] ADD: general changes + changelog --- Changelog.md | 10 +++ src/app/app.component.html | 2 +- src/app/app.module.ts | 27 ++++---- src/styles.css | 128 ++++++++++++++++++++++--------------- 4 files changed, 102 insertions(+), 65 deletions(-) create mode 100644 Changelog.md diff --git a/Changelog.md b/Changelog.md new file mode 100644 index 0000000..b6b1830 --- /dev/null +++ b/Changelog.md @@ -0,0 +1,10 @@ +# Changelog + +## [1.1.0] - Devices +- code integration for faster development +- device and multi entity support +- dual window view (config + discovery) +- fixed config bug + +## [1.0.0] - Initial +- Single Entity creation and discovery creation \ No newline at end of file diff --git a/src/app/app.component.html b/src/app/app.component.html index 7560e08..744741f 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -3,7 +3,7 @@
app icon

MQTT Discovery Creator

-

v.1.0

+

v.1.1

My Homepage Code
diff --git a/src/app/app.module.ts b/src/app/app.module.ts index aa212a9..b8c480c 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,26 +1,27 @@ import { NgModule } from '@angular/core'; -import { BrowserModule, provideClientHydration } from '@angular/platform-browser'; +import { + BrowserModule, + provideClientHydration, +} from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { GeneratorComponent } from './generator/generator.component'; import { FormsModule } from '@angular/forms'; -import { EntityComponent } from './entity/entity.component'; +import { EntityDataComponent } from './entity-data/entity-data.component'; +import { EntityOutputComponent } from './entity-output/entity-output.component'; +import { OutputComponent } from './output/output.component'; @NgModule({ declarations: [ AppComponent, GeneratorComponent, - EntityComponent + EntityDataComponent, + EntityOutputComponent, + OutputComponent, ], - imports: [ - BrowserModule, - AppRoutingModule, - FormsModule, - ], - providers: [ - provideClientHydration() - ], - bootstrap: [AppComponent] + imports: [BrowserModule, AppRoutingModule, FormsModule], + providers: [provideClientHydration()], + bootstrap: [AppComponent], }) -export class AppModule { } +export class AppModule {} diff --git a/src/styles.css b/src/styles.css index 1900866..93abd2c 100644 --- a/src/styles.css +++ b/src/styles.css @@ -3,81 +3,107 @@ @tailwind components; @tailwind utilities; -:root{ - --accent: #4cb926; - --text: #535353; - --primary: #b3b3b3; - --secondary: #f8f8f8; - +:root { + --accent: #4cb926; + --text: #535353; + --primary: #b3b3b3; + --secondary: #f8f8f8; } -input, select, label, pre { - background: var(--primary); - padding: .25rem .75rem; - border-radius: 1rem; +input, +select, +label, +pre, +button { + background: var(--primary); + padding: 0.25rem 0.75rem; + border-radius: 1rem; } -input, select{ - width: 100%; - @apply focus:outline focus:outline-myAccent focus:outline-2; +pre { + text-wrap: wrap; } -input::placeholder{ - color: var(--text); - opacity: 1; +input, +select { + width: 100%; + @apply focus:outline focus:outline-myAccent focus:outline-2; } -input::-ms-input-placeholder { /* Edge 12 -18 */ - color: var(--text); - } - -svg{ - height: 90%; - margin: auto 0; - width: auto; +span { + @apply focus-visible:outline focus-visible:outline-myAccent focus-visible:outline-2; } -*{ - color: var(--text); +input::placeholder { + color: var(--text); + opacity: 1; } -button{ - background-color: var(--accent); - border-radius: 1rem; - color: var(--secondary); - font-weight: bold; - min-width: 4rem; +input::-ms-input-placeholder { + /* Edge 12 -18 */ + color: var(--text); } -.randomButton{ - @apply flex justify-center content-center p-1; +svg { + height: 90%; + margin: auto 0; + width: auto; +} + +* { + color: var(--text); +} + +button { + background-color: var(--accent); + border-radius: 1rem; + color: var(--secondary); + font-weight: bold; + min-width: 4rem; +} + +.randomButton { + @apply flex justify-center content-center p-1; } button path { - color: var(--secondary); + color: var(--secondary); } .property { - background-color: #9D9D9D; - border-radius: .5rem; - /* padding: .5rem; */ - display: inline-flex; - flex-direction: column; - gap: .25rem; + /* background-color: #9d9d9d; */ + border-radius: 0.5rem; + /* padding: .5rem; */ + display: inline-flex; + flex-direction: column; + gap: 0.25rem; } -.property p, .property h3{ - align-self: left; - padding-left: .5rem; +.property p, +.property h3 { + align-self: left; + padding-left: 0.5rem; } -.property > div { - width: 100%; +div.property { + width: 100%; } .property span { - background: #B3B3B3; - padding: .25rem .75rem; - border-radius: 1rem; - /* width: 100%; */ -} \ No newline at end of file + background: #b3b3b3; + padding: 0.25rem 0.75rem; + border-radius: 1rem; + /* width: 100%; */ +} + +#entityPlaceholder { + background: transparent; + border: 2px dotted #b3b3b3; + color: #b3b3b3; + font-weight: normal; +} + +#entityPlaceholder:hover { + border-color: #f8f8f8; + color: #f8f8f8; +} -- 2.49.1 From c2c82d73c1f0e8875b88dbc1a5ede882bea46f20 Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 2 Aug 2024 08:11:08 +0200 Subject: [PATCH 14/15] CHANGE: removed some defaults in basemodel, to create less in toJSON --- src/app/_models/mqtt-light.ts | 4 ++-- src/app/_models/mqtt-sensor.ts | 2 +- src/app/_models/mqtt-switch.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/_models/mqtt-light.ts b/src/app/_models/mqtt-light.ts index dea55b3..1911d19 100644 --- a/src/app/_models/mqtt-light.ts +++ b/src/app/_models/mqtt-light.ts @@ -1,8 +1,8 @@ import { MQTTEntity } from './mqtt_base'; export class MqttLight extends MQTTEntity { - pl_on: string = '1'; - pl_off: string = '0'; + pl_on: string = ''; + pl_off: string = ''; val_tpl: string = ''; bri_cmd_t: string = 'brightness/command/topic'; _cmd_t: string = 'command/topic'; diff --git a/src/app/_models/mqtt-sensor.ts b/src/app/_models/mqtt-sensor.ts index bdc5080..da995ab 100644 --- a/src/app/_models/mqtt-sensor.ts +++ b/src/app/_models/mqtt-sensor.ts @@ -1,7 +1,7 @@ import { iMQTTEntityBase, MQTTEntity } from './mqtt_base'; export class MqttSensor extends MQTTEntity implements iMqttSensor { - unit_of_meas: string = 'meassure'; + unit_of_meas: string = ''; override ent_type: string = 'sensor'; override attrs: Set = new Set([ 'name', diff --git a/src/app/_models/mqtt-switch.ts b/src/app/_models/mqtt-switch.ts index 60eee29..fa90578 100644 --- a/src/app/_models/mqtt-switch.ts +++ b/src/app/_models/mqtt-switch.ts @@ -21,8 +21,8 @@ export class MqttSwitch extends MQTTEntity { _dev_cla: DeviceClass = new DeviceClass(['switch', 'outlet']); cmd_t: string = 'command/topic'; - pl_on: string = '1'; - pl_off: string = '0'; + pl_on: string = ''; + pl_off: string = ''; override set name(name: string) { super.name = name; -- 2.49.1 From 184bf373ad6ae1087656e0d49e71045dec2d4db7 Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 2 Aug 2024 08:11:39 +0200 Subject: [PATCH 15/15] ADD: heading and close button --- src/app/entity-data/entity-data.component.css | 18 +++++++++++++++ .../entity-data/entity-data.component.html | 15 +++++++------ src/app/entity-data/entity-data.component.ts | 2 ++ src/app/generator/generator.component.css | 3 ++- src/app/generator/generator.component.html | 4 ++-- src/app/generator/generator.component.ts | 2 +- src/app/output/output.component.css | 2 +- src/styles.css | 22 +++++++++---------- 8 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/app/entity-data/entity-data.component.css b/src/app/entity-data/entity-data.component.css index e69de29..5a7288b 100644 --- a/src/app/entity-data/entity-data.component.css +++ b/src/app/entity-data/entity-data.component.css @@ -0,0 +1,18 @@ +.heading { + display: flex; + position: inherit; + margin-bottom: 0.25rem; +} + +.heading button { + padding: 0.25rem; + min-width: 0; + position: absolute; + right: 0; + top: 0; +} + +.heading h2 { + width: 100%; + text-align: center; +} diff --git a/src/app/entity-data/entity-data.component.html b/src/app/entity-data/entity-data.component.html index 6966270..1bea529 100644 --- a/src/app/entity-data/entity-data.component.html +++ b/src/app/entity-data/entity-data.component.html @@ -1,4 +1,11 @@ +
+

entity {{ent_index}} - {{basemodel.ent_type}}

+ +

Name

@@ -8,12 +15,6 @@

Uniqe ID

-
@@ -58,4 +59,4 @@
- \ No newline at end of file + diff --git a/src/app/entity-data/entity-data.component.ts b/src/app/entity-data/entity-data.component.ts index 9118c6a..7bca260 100644 --- a/src/app/entity-data/entity-data.component.ts +++ b/src/app/entity-data/entity-data.component.ts @@ -11,6 +11,8 @@ export class EntityDataComponent { @Input() entity_type: number = 1; @Input() basemodel!: MQTTEntity; @Input() created: boolean = false; + @Input() ent_index: number = 0; + @ViewChild('nameinput') nameinput!: any; constructor(public generatorService: GeneratorService) { if (generatorService.selected_entity) { diff --git a/src/app/generator/generator.component.css b/src/app/generator/generator.component.css index 1b35e87..2336ade 100644 --- a/src/app/generator/generator.component.css +++ b/src/app/generator/generator.component.css @@ -9,12 +9,13 @@ .outContainer { background: #9d9d9d; border-radius: 1rem; - padding: 1rem 0.8rem; + padding: 0.75rem; gap: 1rem; } .genContainer { grid-column: 1; + position: relative; } .outContainer { diff --git a/src/app/generator/generator.component.html b/src/app/generator/generator.component.html index 3dced6c..46ae0ab 100644 --- a/src/app/generator/generator.component.html +++ b/src/app/generator/generator.component.html @@ -67,8 +67,8 @@ - - + +
\ No newline at end of file diff --git a/src/app/generator/generator.component.ts b/src/app/generator/generator.component.ts index 4bb234d..31d613e 100644 --- a/src/app/generator/generator.component.ts +++ b/src/app/generator/generator.component.ts @@ -23,7 +23,7 @@ export class GeneratorComponent { device: MQTTDevice = this.generatorService.device; get created_entities(): MQTTEntity[] { - return Array.from(this.generatorService.created_enteties).reverse(); + return Array.from(this.generatorService.created_enteties); } get codeSpan(): string { diff --git a/src/app/output/output.component.css b/src/app/output/output.component.css index 3124112..a10880b 100644 --- a/src/app/output/output.component.css +++ b/src/app/output/output.component.css @@ -1,5 +1,5 @@ pre { - max-width: calc(50vw - 1rem); + max-width: calc(50vw - 3rem); overflow-x: scroll; text-wrap: nowrap; } diff --git a/src/styles.css b/src/styles.css index 93abd2c..5b778e2 100644 --- a/src/styles.css +++ b/src/styles.css @@ -5,9 +5,11 @@ :root { --accent: #4cb926; - --text: #535353; + --text: #3d3d3d; --primary: #b3b3b3; --secondary: #f8f8f8; + --placeholder: #777; + --input_bg: #b3b3b3; } input, @@ -35,13 +37,13 @@ span { } input::placeholder { - color: var(--text); + color: var(--placeholder); opacity: 1; } input::-ms-input-placeholder { /* Edge 12 -18 */ - color: var(--text); + color: var(--placeholder); } svg { @@ -62,10 +64,6 @@ button { min-width: 4rem; } -.randomButton { - @apply flex justify-center content-center p-1; -} - button path { color: var(--secondary); } @@ -90,7 +88,7 @@ div.property { } .property span { - background: #b3b3b3; + background: var(--input_bg); padding: 0.25rem 0.75rem; border-radius: 1rem; /* width: 100%; */ @@ -98,12 +96,12 @@ div.property { #entityPlaceholder { background: transparent; - border: 2px dotted #b3b3b3; - color: #b3b3b3; + border: 2px dotted var(--input_bg); + color: var(--input_bg); font-weight: normal; } #entityPlaceholder:hover { - border-color: #f8f8f8; - color: #f8f8f8; + border-color: var(--secondary); + color: var(--secondary); } -- 2.49.1