Compare commits

...

7 Commits

8 changed files with 2345 additions and 2205 deletions

4302
package-lock.json generated

File diff suppressed because it is too large Load Diff

100
server/package-lock.json generated
View File

@ -15,7 +15,7 @@
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
"integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
"requires": {
"mime-types": "~2.1.18",
"mime-types": "2.1.18",
"negotiator": "0.6.1"
}
},
@ -38,15 +38,15 @@
"integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
"requires": {
"bytes": "3.0.0",
"content-type": "~1.0.4",
"content-type": "1.0.4",
"debug": "2.6.9",
"depd": "~1.1.1",
"http-errors": "~1.6.2",
"depd": "1.1.2",
"http-errors": "1.6.3",
"iconv-lite": "0.4.19",
"on-finished": "~2.3.0",
"on-finished": "2.3.0",
"qs": "6.5.1",
"raw-body": "2.3.2",
"type-is": "~1.6.15"
"type-is": "1.6.16"
}
},
"bytes": {
@ -117,36 +117,36 @@
"resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz",
"integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=",
"requires": {
"accepts": "~1.3.5",
"accepts": "1.3.5",
"array-flatten": "1.1.1",
"body-parser": "1.18.2",
"content-disposition": "0.5.2",
"content-type": "~1.0.4",
"content-type": "1.0.4",
"cookie": "0.3.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"depd": "1.1.2",
"encodeurl": "1.0.2",
"escape-html": "1.0.3",
"etag": "1.8.1",
"finalhandler": "1.1.1",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"methods": "1.1.2",
"on-finished": "2.3.0",
"parseurl": "1.3.2",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.3",
"proxy-addr": "2.0.3",
"qs": "6.5.1",
"range-parser": "~1.2.0",
"range-parser": "1.2.0",
"safe-buffer": "5.1.1",
"send": "0.16.2",
"serve-static": "1.13.2",
"setprototypeof": "1.1.0",
"statuses": "~1.4.0",
"type-is": "~1.6.16",
"statuses": "1.4.0",
"type-is": "1.6.16",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
"vary": "1.1.2"
}
},
"finalhandler": {
@ -155,12 +155,12 @@
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"statuses": "~1.4.0",
"unpipe": "~1.0.0"
"encodeurl": "1.0.2",
"escape-html": "1.0.3",
"on-finished": "2.3.0",
"parseurl": "1.3.2",
"statuses": "1.4.0",
"unpipe": "1.0.0"
}
},
"forwarded": {
@ -178,10 +178,10 @@
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"requires": {
"depd": "~1.1.2",
"depd": "1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.0",
"statuses": ">= 1.4.0 < 2"
"statuses": "1.4.0"
}
},
"iconv-lite": {
@ -229,7 +229,7 @@
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
"integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
"requires": {
"mime-db": "~1.33.0"
"mime-db": "1.33.0"
}
},
"morgan": {
@ -237,11 +237,11 @@
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz",
"integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=",
"requires": {
"basic-auth": "~2.0.0",
"basic-auth": "2.0.0",
"debug": "2.6.9",
"depd": "~1.1.1",
"on-finished": "~2.3.0",
"on-headers": "~1.0.1"
"depd": "1.1.2",
"on-finished": "2.3.0",
"on-headers": "1.0.1"
}
},
"ms": {
@ -277,8 +277,8 @@
"resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
"integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=",
"requires": {
"process": "^0.11.1",
"util": "^0.10.3"
"process": "0.11.10",
"util": "0.10.4"
}
},
"path-to-regexp": {
@ -296,7 +296,7 @@
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz",
"integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==",
"requires": {
"forwarded": "~0.1.2",
"forwarded": "0.1.2",
"ipaddr.js": "1.6.0"
}
},
@ -334,7 +334,7 @@
"depd": "1.1.1",
"inherits": "2.0.3",
"setprototypeof": "1.0.3",
"statuses": ">= 1.3.1 < 2"
"statuses": "1.4.0"
}
},
"setprototypeof": {
@ -355,18 +355,18 @@
"integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"depd": "1.1.2",
"destroy": "1.0.4",
"encodeurl": "1.0.2",
"escape-html": "1.0.3",
"etag": "1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.6.2",
"http-errors": "1.6.3",
"mime": "1.4.1",
"ms": "2.0.0",
"on-finished": "~2.3.0",
"range-parser": "~1.2.0",
"statuses": "~1.4.0"
"on-finished": "2.3.0",
"range-parser": "1.2.0",
"statuses": "1.4.0"
}
},
"serve-static": {
@ -374,9 +374,9 @@
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
"integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.2",
"encodeurl": "1.0.2",
"escape-html": "1.0.3",
"parseurl": "1.3.2",
"send": "0.16.2"
}
},
@ -396,7 +396,7 @@
"integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.18"
"mime-types": "2.1.18"
}
},
"typescript": {

18
smartclient.service Normal file
View File

@ -0,0 +1,18 @@
[Unit]
Description=smartclient
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
GuessMainPID=yes
ExecStart=/usr/bin/npm run server
ExecStop=kill -SIGINT $mainpid
Restart=on-failure
WorkingDirectory=/opt/services/smartclient
[Install]
Alias=smartclient
WantedBy=multi-user.target

View File

@ -27,7 +27,7 @@
<p class="clear"></p>
</mat-tab>
<mat-tab label="Heizung">
<heatingcontroller topicPre="testTopic" label="testLabel"></heatingcontroller>
</mat-tab>
</mat-tab-group>

View File

@ -6,8 +6,9 @@ import { MatTabsModule } from '@angular/material/tabs';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider';
import { MatInputModule } from '@angular/material/input'
import { MqttclientService } from './mqttclient.service'
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { LedindicatorComponent } from './ledindicator/ledindicator.component';
@ -16,6 +17,7 @@ import { LedButtonGroupComponent } from './led-button-group/led-button-group.com
import { NumberFieldComponent } from './number-field/number-field.component';
import { LedButtonGroup2Component } from './led-button-group2/led-button-group2.component';
import { LedBoxComponent } from './led-box/led-box.component';
import { HeatingControllerComponent } from './heating-controller/heating-controller.component';
@NgModule({
@ -26,7 +28,8 @@ import { LedBoxComponent } from './led-box/led-box.component';
LedButtonGroupComponent,
NumberFieldComponent,
LedButtonGroup2Component,
LedBoxComponent
LedBoxComponent,
HeatingControllerComponent
],
imports: [
BrowserModule,
@ -34,7 +37,9 @@ import { LedBoxComponent } from './led-box/led-box.component';
MatTabsModule,
MatExpansionModule,
MatButtonModule,
MatDividerModule
MatDividerModule,
MatInputModule,
FormsModule
],
providers: [
MqttclientService

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HeatingControllerComponent } from './heating-controller.component';
describe('HeatingControllerComponent', () => {
let component: HeatingControllerComponent;
let fixture: ComponentFixture<HeatingControllerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HeatingControllerComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HeatingControllerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,91 @@
import { Component, Input, OnInit } from '@angular/core'
import { MqttclientService } from '../mqttclient.service'
import {coerceNumberProperty} from '@angular/cdk/coercion'
@Component({
selector: 'heatingcontroller',
template: `
<div [ngStyle]="{ 'text-align': 'center', 'background-color':'lightgrey', 'border-radius':'10px',
'width': '300px', 'padding':'5px', 'margin': '5px 5px 5px 0',
'font-family': 'sans-serif' }">
{{label}}
<p>
<button mat-raised-button color="primary" (click)="clickOff()" [ngStyle]="{'font-size':'100%'}">off</button>
<button mat-raised-button color="accent" (click)="clickOn()" [ngStyle]="{'font-size':'100%'}">on</button>
<button mat-raised-button color="warn" (click)="clickForceOn()" [ngStyle]="{'font-size':'100%'}">forceOn</button>
</p>
<p>
<mat-form-field [ngStyle]="{'margin':'10px', 'width':'40%'}">
<input type="checkbox" [(ngModel)]="enableCurrentTemperature">
<input matInput type="number" placeholder="Current" [(ngModel)]="currentTemperature"
(click)="setCurrentTemperature()" (keyup)="setCurrentTemperature()"
[disabled]="! enableCurrentTemperature">
</mat-form-field>
<mat-form-field [ngStyle]="{'margin':'10px', 'width':'40%'}">
<input type="checkbox" [(ngModel)]="enablePresetTemperature">
<input matInput type="number" placeholder="Preset" [(ngModel)]="presetTemperature"
(click)="setPresetTemperature()" (keyup)="setPresetTemperature()"
[disabled]="! enablePresetTemperature">
</mat-form-field>
</p>
</div>
`
})
export class HeatingControllerComponent implements OnInit {
@Input() topicPre : string = 'invalid'
commandTopic : string = 'invalid'
presetTopic : string = 'invalid'
feedbackPresetTopic : string = 'invalid'
temperatureTopic : string = 'invalid'
feedbackTemperatureTopic : string = 'invalid'
@Input() label : string = 'invalid'
enableCurrentTemperature : boolean = false
currentTemperature : number = 20
enablePresetTemperature : boolean = false
presetTemperature : number = 20
constructor(private mqttclientService : MqttclientService) { }
ngOnInit() {
this.commandTopic = this.topicPre + '/command'
this.presetTopic = this.topicPre + '/presetTemperature'
this.feedbackPresetTopic = this.topicPre + '/presetTemperature/feedback'
this.temperatureTopic = this.topicPre + '/temperature'
this.feedbackTemperatureTopic = this.topicPre + '/temperature/feedback'
this.mqttclientService.register(this.feedbackTemperatureTopic, (message: string) => {
this.currentTemperature = parseInt(message)
})
this.mqttclientService.register(this.feedbackPresetTopic, (message: string) => {
this.presetTemperature = parseInt(message)
})
}
clickOn() {
console.log(`click on for ${this.commandTopic}`)
this.mqttclientService.publish(this.commandTopic, "ON")
}
clickForceOn() {
console.log(`click absent for ${this.commandTopic}`)
this.mqttclientService.publish(this.commandTopic, "FORCE_ON")
}
clickOff() {
console.log(`click off for ${this.commandTopic}`)
this.mqttclientService.publish(this.commandTopic, "OFF")
}
setCurrentTemperature() {
console.log(`click on currentTemperature ${this.currentTemperature}`)
this.mqttclientService.publish(this.temperatureTopic, this.currentTemperature.toString())
}
setPresetTemperature() {
console.log(`click on presetTemperature ${this.presetTemperature}`)
this.mqttclientService.publish(this.presetTopic, this.presetTemperature.toString())
}
}

View File

@ -9,6 +9,7 @@ export class MqttclientService {
private callbacks : Map<string, callbackFunc> = new Map()
constructor() {
// this.mqttClient = Mqtt.connect('ws://172.16.2.16:9001')
this.mqttClient = Mqtt.connect('ws://127.0.0.1:9001')
this.mqttClient.on('connect', () => {
console.log('MQTT connected')