diff --git a/ui/src/app/core/accessories/accessories.module.ts b/ui/src/app/core/accessories/accessories.module.ts
index c6f564879..e8039d148 100644
--- a/ui/src/app/core/accessories/accessories.module.ts
+++ b/ui/src/app/core/accessories/accessories.module.ts
@@ -31,6 +31,10 @@ import { HumiditysensorComponent } from './types/humiditysensor/humiditysensor.c
import { AirqualitysensorComponent } from './types/airqualitysensor/airqualitysensor.component';
import { WindowcoveringComponent } from './types/windowcovering/windowcovering.component';
import { WindowcoveringManageComponent } from './types/windowcovering/windowcovering.manage.component';
+import { WindowComponent } from './types/window/window.component';
+import { WindowManageComponent } from './types/window/window.manage.component';
+import { DoorComponent } from './types/door/door.component';
+import { DoorManageComponent } from './types/door/door.manage.component';
import { TelevisionComponent } from './types/television/television.component';
import { ContactsensorComponent } from './types/contactsensor/contactsensor.component';
import { BatteryserviceComponent } from './types/batteryservice/batteryservice.component';
@@ -57,6 +61,8 @@ import { InfoModalComponent } from './info-modal/info-modal.component';
FanManageComponent,
Fanv2ManageComponent,
WindowcoveringManageComponent,
+ WindowManageComponent,
+ DoorManageComponent,
SpeakerManageComponent,
SecuritysystemManageComponent,
ValveManageComponent,
@@ -86,6 +92,10 @@ import { InfoModalComponent } from './info-modal/info-modal.component';
AirqualitysensorComponent,
WindowcoveringComponent,
WindowcoveringManageComponent,
+ WindowComponent,
+ WindowManageComponent,
+ DoorComponent,
+ DoorManageComponent,
TelevisionComponent,
ContactsensorComponent,
BatteryserviceComponent,
@@ -135,6 +145,10 @@ import { InfoModalComponent } from './info-modal/info-modal.component';
AirqualitysensorComponent,
WindowcoveringComponent,
WindowcoveringManageComponent,
+ WindowComponent,
+ WindowManageComponent,
+ DoorComponent,
+ DoorManageComponent,
TelevisionComponent,
ContactsensorComponent,
BatteryserviceComponent,
diff --git a/ui/src/app/core/accessories/types/door/door.component.html b/ui/src/app/core/accessories/types/door/door.component.html
new file mode 100644
index 000000000..d95223fe4
--- /dev/null
+++ b/ui/src/app/core/accessories/types/door/door.component.html
@@ -0,0 +1,24 @@
+
+
+
+
+
{{ service.customName || service.serviceName }}
+
+ {{ 'accessories.control.label_closed' | translate }}
+ 0 && service.values.CurrentPosition < 100">
+ {{ service.values.CurrentPosition }}% {{ 'accessories.control.label_open' | translate }}
+
+ {{ 'accessories.control.label_open' | translate }}
+
+
+ {{ 'accessories.control.label_opening' | translate }}...
+
+
+ {{ 'accessories.control.label_closing' | translate }}...
+
+
+
+
diff --git a/ui/src/app/core/accessories/types/door/door.component.scss b/ui/src/app/core/accessories/types/door/door.component.scss
new file mode 100644
index 000000000..558befd64
--- /dev/null
+++ b/ui/src/app/core/accessories/types/door/door.component.scss
@@ -0,0 +1,7 @@
+.door-mode-control {
+ .btn {
+ font-size: 1.4rem;
+ text-transform: initial;
+ }
+}
+
diff --git a/ui/src/app/core/accessories/types/door/door.component.ts b/ui/src/app/core/accessories/types/door/door.component.ts
new file mode 100644
index 000000000..eb6aea236
--- /dev/null
+++ b/ui/src/app/core/accessories/types/door/door.component.ts
@@ -0,0 +1,36 @@
+import { Component, OnInit, Input } from '@angular/core';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { ServiceTypeX } from '../../accessories.interfaces';
+
+import { DoorManageComponent } from './door.manage.component';
+
+@Component({
+ selector: 'app-door',
+ templateUrl: './door.component.html',
+ styleUrls: ['./door.component.scss'],
+})
+export class DoorComponent implements OnInit {
+ @Input() public service: ServiceTypeX;
+
+ constructor(
+ private modalService: NgbModal,
+ ) { }
+
+ ngOnInit() { }
+
+ onClick() {
+ if (this.service.values.TargetPosition) {
+ this.service.getCharacteristic('TargetPosition').setValue(0);
+ } else {
+ this.service.getCharacteristic('TargetPosition').setValue(100);
+ }
+ }
+
+ onLongClick() {
+ const ref = this.modalService.open(DoorManageComponent, {
+ size: 'sm',
+ });
+ ref.componentInstance.service = this.service;
+ }
+
+}
diff --git a/ui/src/app/core/accessories/types/door/door.manage.component.html b/ui/src/app/core/accessories/types/door/door.manage.component.html
new file mode 100644
index 000000000..b9297b831
--- /dev/null
+++ b/ui/src/app/core/accessories/types/door/door.manage.component.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+ {{ 'accessories.control.label_closed' | translate }}
+ 0 && service.values.CurrentPosition < 100">
+ {{ service.values.CurrentPosition }}% {{ 'accessories.control.label_open' | translate }}
+
+ Open
+
+
+ {{ 'accessories.control.label_opening' | translate }}...
+
+
+ {{ 'accessories.control.label_closing' | translate }}...
+
+
+
+
+
{{ 'accessories.control.label_target' | translate }}: {{ targetPosition.value }}%
+
+
+
+
+
+
diff --git a/ui/src/app/core/accessories/types/door/door.manage.component.ts b/ui/src/app/core/accessories/types/door/door.manage.component.ts
new file mode 100644
index 000000000..fcaf6ee8b
--- /dev/null
+++ b/ui/src/app/core/accessories/types/door/door.manage.component.ts
@@ -0,0 +1,59 @@
+import { Component, OnInit, Input } from '@angular/core';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { ServiceTypeX } from '../../accessories.interfaces';
+
+import { Subject } from 'rxjs';
+import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
+
+@Component({
+ selector: 'app-door-manage',
+ templateUrl: './door.manage.component.html',
+ styleUrls: ['./door.component.scss'],
+})
+export class DoorManageComponent implements OnInit {
+ @Input() public service: ServiceTypeX;
+ public targetMode: any;
+ public targetPosition: any;
+ public targetPositionChanged: Subject = new Subject();
+
+ constructor(
+ public activeModal: NgbActiveModal,
+ ) {
+ this.targetPositionChanged
+ .pipe(
+ debounceTime(300),
+ distinctUntilChanged(),
+ )
+ .subscribe((value) => {
+ if (this.service.getCharacteristic('CurrentPosition').value < this.targetPosition.value) {
+ this.service.values.PositionState = 1;
+ } else if (this.service.getCharacteristic('CurrentPosition').value > this.targetPosition.value) {
+ this.service.values.PositionState = 0;
+ }
+ this.service.getCharacteristic('TargetPosition').setValue(this.targetPosition.value);
+ });
+ }
+
+ ngOnInit() {
+ this.targetMode = this.service.values.On;
+ this.loadTargetPosition();
+ }
+
+ loadTargetPosition() {
+ const TargetPosition = this.service.getCharacteristic('TargetPosition');
+
+ if (TargetPosition) {
+ this.targetPosition = {
+ value: TargetPosition.value,
+ min: TargetPosition.minValue,
+ max: TargetPosition.maxValue,
+ step: TargetPosition.minStep,
+ };
+ }
+ }
+
+ onTargetPositionChange() {
+ this.targetPositionChanged.next(this.targetPosition.value);
+ }
+
+}
diff --git a/ui/src/app/core/accessories/types/window/window.component.html b/ui/src/app/core/accessories/types/window/window.component.html
new file mode 100644
index 000000000..53e2f3e8e
--- /dev/null
+++ b/ui/src/app/core/accessories/types/window/window.component.html
@@ -0,0 +1,24 @@
+
+
+
+
+
{{ service.customName || service.serviceName }}
+
+ {{ 'accessories.control.label_closed' | translate }}
+ 0 && service.values.CurrentPosition < 100">
+ {{ service.values.CurrentPosition }}% {{ 'accessories.control.label_open' | translate }}
+
+ {{ 'accessories.control.label_open' | translate }}
+
+
+ {{ 'accessories.control.label_opening' | translate }}...
+
+
+ {{ 'accessories.control.label_closing' | translate }}...
+
+
+
+
diff --git a/ui/src/app/core/accessories/types/window/window.component.scss b/ui/src/app/core/accessories/types/window/window.component.scss
new file mode 100644
index 000000000..d483bd167
--- /dev/null
+++ b/ui/src/app/core/accessories/types/window/window.component.scss
@@ -0,0 +1,7 @@
+.window-mode-control {
+ .btn {
+ font-size: 1.4rem;
+ text-transform: initial;
+ }
+}
+
diff --git a/ui/src/app/core/accessories/types/window/window.component.ts b/ui/src/app/core/accessories/types/window/window.component.ts
new file mode 100644
index 000000000..606bb1718
--- /dev/null
+++ b/ui/src/app/core/accessories/types/window/window.component.ts
@@ -0,0 +1,36 @@
+import { Component, OnInit, Input } from '@angular/core';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { ServiceTypeX } from '../../accessories.interfaces';
+
+import { WindowManageComponent } from './window.manage.component';
+
+@Component({
+ selector: 'app-window',
+ templateUrl: './window.component.html',
+ styleUrls: ['./window.component.scss'],
+})
+export class WindowComponent implements OnInit {
+ @Input() public service: ServiceTypeX;
+
+ constructor(
+ private modalService: NgbModal,
+ ) { }
+
+ ngOnInit() { }
+
+ onClick() {
+ if (this.service.values.TargetPosition) {
+ this.service.getCharacteristic('TargetPosition').setValue(0);
+ } else {
+ this.service.getCharacteristic('TargetPosition').setValue(100);
+ }
+ }
+
+ onLongClick() {
+ const ref = this.modalService.open(WindowManageComponent, {
+ size: 'sm',
+ });
+ ref.componentInstance.service = this.service;
+ }
+
+}
diff --git a/ui/src/app/core/accessories/types/window/window.manage.component.html b/ui/src/app/core/accessories/types/window/window.manage.component.html
new file mode 100644
index 000000000..b9297b831
--- /dev/null
+++ b/ui/src/app/core/accessories/types/window/window.manage.component.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+ {{ 'accessories.control.label_closed' | translate }}
+ 0 && service.values.CurrentPosition < 100">
+ {{ service.values.CurrentPosition }}% {{ 'accessories.control.label_open' | translate }}
+
+ Open
+
+
+ {{ 'accessories.control.label_opening' | translate }}...
+
+
+ {{ 'accessories.control.label_closing' | translate }}...
+
+
+
+
+
{{ 'accessories.control.label_target' | translate }}: {{ targetPosition.value }}%
+
+
+
+
+
+
diff --git a/ui/src/app/core/accessories/types/window/window.manage.component.ts b/ui/src/app/core/accessories/types/window/window.manage.component.ts
new file mode 100644
index 000000000..aad6b8faf
--- /dev/null
+++ b/ui/src/app/core/accessories/types/window/window.manage.component.ts
@@ -0,0 +1,59 @@
+import { Component, OnInit, Input } from '@angular/core';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { ServiceTypeX } from '../../accessories.interfaces';
+
+import { Subject } from 'rxjs';
+import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
+
+@Component({
+ selector: 'app-window-manage',
+ templateUrl: './window.manage.component.html',
+ styleUrls: ['./window.component.scss'],
+})
+export class WindowManageComponent implements OnInit {
+ @Input() public service: ServiceTypeX;
+ public targetMode: any;
+ public targetPosition: any;
+ public targetPositionChanged: Subject = new Subject();
+
+ constructor(
+ public activeModal: NgbActiveModal,
+ ) {
+ this.targetPositionChanged
+ .pipe(
+ debounceTime(300),
+ distinctUntilChanged(),
+ )
+ .subscribe((value) => {
+ if (this.service.getCharacteristic('CurrentPosition').value < this.targetPosition.value) {
+ this.service.values.PositionState = 1;
+ } else if (this.service.getCharacteristic('CurrentPosition').value > this.targetPosition.value) {
+ this.service.values.PositionState = 0;
+ }
+ this.service.getCharacteristic('TargetPosition').setValue(this.targetPosition.value);
+ });
+ }
+
+ ngOnInit() {
+ this.targetMode = this.service.values.On;
+ this.loadTargetPosition();
+ }
+
+ loadTargetPosition() {
+ const TargetPosition = this.service.getCharacteristic('TargetPosition');
+
+ if (TargetPosition) {
+ this.targetPosition = {
+ value: TargetPosition.value,
+ min: TargetPosition.minValue,
+ max: TargetPosition.maxValue,
+ step: TargetPosition.minStep,
+ };
+ }
+ }
+
+ onTargetPositionChange() {
+ this.targetPositionChanged.next(this.targetPosition.value);
+ }
+
+}
diff --git a/ui/src/app/modules/accessories/accessories.component.html b/ui/src/app/modules/accessories/accessories.component.html
index efaf9368c..872b45608 100644
--- a/ui/src/app/modules/accessories/accessories.component.html
+++ b/ui/src/app/modules/accessories/accessories.component.html
@@ -66,6 +66,10 @@
Window Covering
+ Window
+
+ Door
+
Television
BatteryService
diff --git a/ui/src/app/modules/status/widgets/accessories-widget/accessories-widget.component.html b/ui/src/app/modules/status/widgets/accessories-widget/accessories-widget.component.html
index 3a3812696..11612763e 100644
--- a/ui/src/app/modules/status/widgets/accessories-widget/accessories-widget.component.html
+++ b/ui/src/app/modules/status/widgets/accessories-widget/accessories-widget.component.html
@@ -34,6 +34,10 @@
Window Covering
+ Window
+
+ Door
+
Television
Security System
diff --git a/ui/src/assets/hap-icons/door-closed.svg b/ui/src/assets/hap-icons/door-closed.svg
new file mode 100644
index 000000000..73ab9079b
--- /dev/null
+++ b/ui/src/assets/hap-icons/door-closed.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/ui/src/assets/hap-icons/door-open.svg b/ui/src/assets/hap-icons/door-open.svg
new file mode 100644
index 000000000..7b32c2efb
--- /dev/null
+++ b/ui/src/assets/hap-icons/door-open.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/ui/src/assets/hap-icons/window-closed.svg b/ui/src/assets/hap-icons/window-closed.svg
new file mode 100644
index 000000000..9cd0713ae
--- /dev/null
+++ b/ui/src/assets/hap-icons/window-closed.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/ui/src/assets/hap-icons/window-open.svg b/ui/src/assets/hap-icons/window-open.svg
new file mode 100644
index 000000000..3ebbc4e8a
--- /dev/null
+++ b/ui/src/assets/hap-icons/window-open.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file