Merge into Main #1

Merged
tom_trgr merged 15 commits from dev/devices into main 2024-08-02 08:16:31 +02:00
8 changed files with 204 additions and 52 deletions
Showing only changes of commit 06e2d77f08 - Show all commits

View File

@@ -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',

View File

@@ -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';

View File

@@ -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;
};
}

View File

@@ -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<MQTTEntity> = [];
public _selected_entity: MQTTEntity | null = null;
public created_enteties: Set<MQTTEntity> = new Set();
// private entities: Map<string, MQTTEntity> = new Map<string, MQTTEntity>();
@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<boolean> = new EventEmitter<boolean>();
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;
}

View File

@@ -1,13 +1,13 @@
<!DOCTYPE html5>
<main class="grid grid-cols-2 gap-4">
<form class="grid grid-cols-2 gap-4" (submit)="$event.preventDefault();generatorService.createEntity()" >
<div class="property" *ngIf="hasProperty('name')">
<p>Name</p>
<input required placeholder="entity name" type="text"[ngModel]="entity_name" (ngModelChange)="entity_name = $event"/>
<input required placeholder="entity name" type="text" name="entity_name" [ngModel]="entity_name" (ngModelChange)="entity_name = $event"/>
</div>
<div class="property" *ngIf="hasProperty('uniq_id')">
<p>Uniqe ID</p>
<div class="flex-row flex gap-2">
<input type="text" [ngModel]="entity_uniq_id" (ngModelChange)="entity_uniq_id = $event"/>
<input type="text" name="entity_uniq_id" [ngModel]="entity_uniq_id" (ngModelChange)="entity_uniq_id = $event"/>
<button class="randomButton" >
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-dice-3" viewBox="0 0 16 16">
<path d="M13 1a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2zM3 0a3 3 0 0 0-3 3v10a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V3a3 3 0 0 0-3-3z"/>
@@ -18,43 +18,44 @@
</div>
<div class="property" *ngIf="hasProperty('cmd_t')">
<p>Command Topic</p>
<input placeholder="command/topic" type="text" [ngModel]="entity_cmd_t" (ngModelChange)="entity_cmd_t = $event" /> <!-- (input)="lock_auto_topic(); update('entity_cmd_t', $event)"-->
<input placeholder="command/topic" type="text" name="entity_cmd_t" [ngModel]="entity_cmd_t" (ngModelChange)="entity_cmd_t = $event" /> <!-- (input)="lock_auto_topic(); update('entity_cmd_t', $event)"-->
</div>
<div class="property" *ngIf="hasProperty('bri_cmd_t')">
<p>Brightness Command Topic</p>
<input placeholder="brightness/command/topic" type="text" [ngModel]="entity_bri_cmd_t" (ngModelChange)="entity_bri_cmd_t = $event" /> <!--(ngModelChange)="lock_auto_topic(); update('entity_bri_cmd_t', $event)"-->
<input placeholder="brightness/command/topic" type="text" name="entity_bri_cmd_t" [ngModel]="entity_bri_cmd_t" (ngModelChange)="entity_bri_cmd_t = $event" /> <!--(ngModelChange)="lock_auto_topic(); update('entity_bri_cmd_t', $event)"-->
</div>
<div class="property !flex-row" *ngIf="hasProperty('pl_off') || hasProperty('pl_on')">
<div *ngIf="hasProperty('pl_on')">
<p>Payload on</p>
<input placeholder="1" type="text" [ngModel]="entity_pl_on" (ngModelChange)="entity_pl_on = $event"/>
<input placeholder="1" type="text" name="entity_pl_on" [ngModel]="entity_pl_on" (ngModelChange)="entity_pl_on = $event"/>
</div>
<div *ngIf="hasProperty('pl_off')">
<p>Payload off</p>
<input placeholder="0" type="text" [ngModel]="entity_pl_off" (ngModelChange)="entity_pl_off = $event"/>
<input placeholder="0" type="text" name="entity_pl_off" [ngModel]="entity_pl_off" (ngModelChange)="entity_pl_off = $event"/>
</div>
</div>
<div class="property" *ngIf="hasProperty('unit_of_meas')">
<p>Unit of meassurement</p>
<input placeholder="°C" type="text" [ngModel]="entity_unit_of_meas" (ngModelChange)="entity_unit_of_meas = $event"/>
<input placeholder="°C" type="text" name="entity_unit_of_meas" [ngModel]="entity_unit_of_meas" (ngModelChange)="entity_unit_of_meas = $event"/>
</div>
<div class="property" *ngIf="hasProperty('val_tpl')">
<p>Value Template</p>
<input type="text" [ngModel]="entity_val_tpl" (ngModelChange)="entity_val_tpl = $event"/>
<input type="text" name="entity_val_tpl" [ngModel]="entity_val_tpl" (ngModelChange)="entity_val_tpl = $event"/>
</div>
<div class="property bg-mySecondary" *ngIf="hasProperty('dev_cla')">
<div class="property " *ngIf="hasProperty('dev_cla')">
<p>Device Class</p>
<select [ngModel]="basemodel.getProperty('dev_cla').value" (ngModelChange)="entity_dev_cla =$event">
<select name="entity_dev_cla" [ngModel]="entity_dev_cla" (ngModelChange)="entity_dev_cla =$event">
<option *ngFor="let choice of basemodel.getProperty('dev_cla').choices" value="{{basemodel.getProperty('dev_cla').choices.indexOf(choice)}}">{{choice}}</option>
</select>
</div>
<div class="property" *ngIf="hasProperty('stat_t')">
<p>State Topic</p>
<div class="flex-row flex gap-2">
<input placeholder="state/topic" type="text" [ngModel]="basemodel.getProperty('stat_t')" (ngModelChange)="basemodel.stat_t= $event" /> <!--(ngModelChange)="lock_auto_topic(); update('stat_t', $event)"-->
<input placeholder="state/topic" type="text" name="stat_t" [ngModel]="basemodel.getProperty('stat_t')" (ngModelChange)="basemodel.stat_t= $event" /> <!--(ngModelChange)="lock_auto_topic(); update('stat_t', $event)"-->
</div>
</div>
<div class="property col-span-2" *ngIf="!created">
<button class="w-full" (click)="generatorService.createEntity()">Add Entity</button>
</div>
</main>
</form>

View File

@@ -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);
}

View File

@@ -30,6 +30,10 @@
</div>
<ng-container *ngIf="generatorService.selected_entity == null" >
<button (click)="select_type(entity_type)" class="genContainer" id="entityPlaceholder">Create new entity</button>
</ng-container>
<app-entity-data class="genContainer" [basemodel]="generatorService.selected_entity" *ngIf="generatorService.selected_entity != null" ></app-entity-data>
<!-- <app-entity class="genContainer" ></app-entity> -->
<app-entity-data class="genContainer" *ngFor="let entity of created_entities" [basemodel]="entity" [created]="true" ></app-entity-data>
</div>

View File

@@ -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) {