22 changed files with 846 additions and 43 deletions
@ -0,0 +1,226 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2023 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<mat-toolbar color="primary"> |
|||
<h2 translate>device.connectivity.check-connectivity</h2> |
|||
<span fxFlex></span> |
|||
<!-- <div [tb-help]="'check-connectivity'"></div>--> |
|||
<button mat-icon-button |
|||
(click)="close()" |
|||
type="button"> |
|||
<mat-icon class="material-icons">close</mat-icon> |
|||
</button> |
|||
</mat-toolbar> |
|||
<div mat-dialog-content> |
|||
<section *ngIf="loadedCommand; else loadingCommand" class="tb-form-panel no-padding no-border"> |
|||
<tb-toggle-header #transportProtocol |
|||
value="{{ selectTransportType }}" |
|||
name="TransportProtocol" |
|||
useSelectOnMdLg="false" |
|||
appearance="fill" |
|||
[fxHide]="allowTransportType.size < 2"> |
|||
<tb-toggle-option |
|||
*ngIf="allowTransportType.has(BasicTransportType.HTTP)" |
|||
[value]="BasicTransportType.HTTP"> |
|||
{{ deviceTransportTypeTranslationMap.get(BasicTransportType.HTTP) | translate }} |
|||
</tb-toggle-option> |
|||
<tb-toggle-option |
|||
*ngIf="allowTransportType.has(DeviceTransportType.MQTT)" |
|||
[value]="DeviceTransportType.MQTT"> |
|||
{{ deviceTransportTypeTranslationMap.get(DeviceTransportType.MQTT) | translate }} |
|||
</tb-toggle-option> |
|||
<tb-toggle-option |
|||
*ngIf="allowTransportType.has(DeviceTransportType.COAP)" |
|||
[value]="DeviceTransportType.COAP"> |
|||
{{ deviceTransportTypeTranslationMap.get(DeviceTransportType.COAP) | translate }} |
|||
</tb-toggle-option> |
|||
<tb-toggle-option |
|||
*ngIf="allowTransportType.has(DeviceTransportType.SNMP)" |
|||
[value]="DeviceTransportType.SNMP"> |
|||
{{ deviceTransportTypeTranslationMap.get(DeviceTransportType.SNMP) | translate }} |
|||
</tb-toggle-option> |
|||
<tb-toggle-option |
|||
*ngIf="allowTransportType.has(DeviceTransportType.LWM2M)" |
|||
[value]="DeviceTransportType.LWM2M"> |
|||
{{ deviceTransportTypeTranslationMap.get(DeviceTransportType.LWM2M) | translate }} |
|||
</tb-toggle-option> |
|||
</tb-toggle-header> |
|||
<div class="tb-form-panel stroked" [fxHide]="!allowTransportType.size"> |
|||
<ng-container [ngSwitch]="transportProtocol.value"> |
|||
<ng-template [ngSwitchCase]="BasicTransportType.HTTP"> |
|||
<div class="tb-form-panel-hint tb-font-14" translate>device.connectivity.use-following-instructions</div> |
|||
<div class="tb-hint-instruction"> |
|||
<span class="content tb-form-panel-hint" translate>device.connectivity.install-curl</span> |
|||
<span tb-help-popup="device/install_curl" |
|||
tb-help-popup-placement="bottom" |
|||
trigger-style="letter-spacing:0.25px; font-size: 12px" |
|||
[tb-help-popup-style]="{maxWidth: '800px'}" |
|||
trigger-text="{{ 'action.see-documentation' | translate }}"></span> |
|||
</div> |
|||
<div *ngIf="commands.http"> |
|||
<div class="tb-form-panel-hint tb-font-14" translate>device.connectivity.http-command</div> |
|||
<tb-markdown usePlainMarkdown containerClass="tb-command-code" |
|||
[data]=createMarkDownCommand(commands.http)></tb-markdown> |
|||
</div> |
|||
<div *ngIf="commands.https"> |
|||
<div class="tb-form-panel-hint tb-font-14" translate>device.connectivity.https-command</div> |
|||
<tb-markdown usePlainMarkdown containerClass="tb-command-code" |
|||
[data]=createMarkDownCommand(commands.https)></tb-markdown> |
|||
</div> |
|||
</ng-template> |
|||
<ng-template [ngSwitchCase]="DeviceTransportType.MQTT"> |
|||
<div *ngIf="!(commands.mqtts && commands.mqtts === 'Check documentation')" class="tb-form-panel no-padding no-border"> |
|||
<div class="tb-form-panel-hint tb-font-14" translate>device.connectivity.use-following-instructions</div> |
|||
<div class="tb-hint-instruction"> |
|||
<span class="content tb-form-panel-hint" translate>device.connectivity.install-mqtt-client</span> |
|||
<span tb-help-popup="device/install_mqtt_client" |
|||
tb-help-popup-placement="bottom" |
|||
trigger-style="letter-spacing:0.25px; font-size: 12px" |
|||
[tb-help-popup-style]="{maxWidth: '800px'}" |
|||
trigger-text="{{ 'action.see-documentation' | translate }}"></span> |
|||
</div> |
|||
</div> |
|||
<div *ngIf="commands.mqtt"> |
|||
<div class="tb-form-panel-hint tb-font-14" translate>device.connectivity.mqtt-command</div> |
|||
<tb-markdown usePlainMarkdown containerClass="tb-command-code" |
|||
[data]=createMarkDownCommand(commands.mqtt)></tb-markdown> |
|||
</div> |
|||
<div *ngIf="commands.mqtts"> |
|||
<div *ngIf="commands.mqtts !== 'Check documentation'; else mqttCertificate"> |
|||
<div class="tb-form-panel-hint tb-font-14" translate>device.connectivity.mqtts-command</div> |
|||
<tb-markdown usePlainMarkdown containerClass="tb-command-code" |
|||
[data]=createMarkDownCommand(commands.mqtts)></tb-markdown> |
|||
</div> |
|||
<ng-template #mqttCertificate> |
|||
<div class="tb-form-panel-hint tb-font-14" translate>device.connectivity.mqtts-x509-command</div> |
|||
<div> |
|||
<a mat-button color="primary" href="https://thingsboard.io/docs/user-guide/certificates/?ubuntuThingsboardX509=x509Chain" target="_blank"> |
|||
<span> |
|||
{{ 'action.see-documentation' | translate }} |
|||
<mat-icon class="tb-mat-18">open_in_new</mat-icon> |
|||
</span> |
|||
</a> |
|||
</div> |
|||
</ng-template> |
|||
</div> |
|||
</ng-template> |
|||
<ng-template [ngSwitchCase]="DeviceTransportType.COAP"> |
|||
<div *ngIf="!(commands.coaps && commands.coaps === 'Check documentation')" class="tb-form-panel no-padding no-border"> |
|||
<div class="tb-form-panel-hint tb-font-14" translate>device.connectivity.use-following-instructions</div> |
|||
<div class="tb-hint-instruction"> |
|||
<span class="content tb-form-panel-hint" translate>device.connectivity.install-coap-cli</span> |
|||
<span tb-help-popup="device/install_coap_client" |
|||
tb-help-popup-placement="bottom" |
|||
trigger-style="letter-spacing:0.25px; font-size: 12px" |
|||
[tb-help-popup-style]="{maxWidth: '800px'}" |
|||
trigger-text="{{ 'action.see-documentation' | translate }}"></span> |
|||
</div> |
|||
</div> |
|||
<div *ngIf="commands.coap"> |
|||
<div class="tb-form-panel-hint tb-font-14" translate>device.connectivity.coap-command</div> |
|||
<tb-markdown usePlainMarkdown containerClass="tb-command-code" |
|||
[data]=createMarkDownCommand(commands.coap)></tb-markdown> |
|||
</div> |
|||
<div *ngIf="commands.coaps"> |
|||
<div *ngIf="commands.coaps !== 'Check documentation'; else coapCertificate"> |
|||
<div class="tb-form-panel-hint tb-font-14" translate>device.connectivity.coaps-command</div> |
|||
<tb-markdown usePlainMarkdown containerClass="tb-command-code" |
|||
[data]=createMarkDownCommand(commands.coaps)></tb-markdown> |
|||
</div> |
|||
<ng-template #coapCertificate> |
|||
<div class="tb-form-panel-hint tb-font-14" translate>device.connectivity.coaps-x509-command</div> |
|||
<div> |
|||
<a mat-button color="primary" href="https://thingsboard.io/docs/user-guide/ssl/coap-x509-certificates/" target="_blank"> |
|||
<span> |
|||
{{ 'action.see-documentation' | translate }} |
|||
<mat-icon class="tb-mat-18">open_in_new</mat-icon> |
|||
</span> |
|||
</a> |
|||
</div> |
|||
</ng-template> |
|||
</div> |
|||
</ng-template> |
|||
<ng-template [ngSwitchCase]="DeviceTransportType.SNMP"> |
|||
<div class="tb-form-panel-hint tb-font-14" translate>device.connectivity.snmp-command</div> |
|||
<div> |
|||
<a mat-button color="primary" href="https://thingsboard.io/docs/reference/snmp-api/" target="_blank"> |
|||
<span> |
|||
{{ 'action.see-documentation' | translate }} |
|||
<mat-icon class="tb-mat-18">open_in_new</mat-icon> |
|||
</span> |
|||
</a> |
|||
</div> |
|||
</ng-template> |
|||
<ng-template [ngSwitchCase]="DeviceTransportType.LWM2M"> |
|||
<div class="tb-form-panel-hint tb-font-14" translate>device.connectivity.lwm2m-command</div> |
|||
<div> |
|||
<a mat-button color="primary" href="https://thingsboard.io/docs/reference/lwm2m-api/" target="_blank"> |
|||
<span> |
|||
{{ 'action.see-documentation' | translate }} |
|||
<mat-icon class="tb-mat-18">open_in_new</mat-icon> |
|||
</span> |
|||
</a> |
|||
</div> |
|||
</ng-template> |
|||
</ng-container> |
|||
</div> |
|||
<div class="tb-form-panel stroked"> |
|||
<div fxFlex fxLayout="row"> |
|||
<div class="tb-form-panel-title" translate>device.state</div> |
|||
<div class="status" [ngClass]="{'inactive': !status}"> |
|||
{{ (status ? 'device.active' : 'device.inactive') | translate }} |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-panel-hint tb-font-14" translate>attribute.latest-telemetry</div> |
|||
<div class="tb-form-table"> |
|||
<div class="tb-form-table-header"> |
|||
<div class="tb-form-table-header-cell" fxFlex translate>device.time</div> |
|||
<div class="tb-form-table-header-cell" fxFlex translate>attribute.key</div> |
|||
<div class="tb-form-table-header-cell" fxFlex translate>attribute.value</div> |
|||
</div> |
|||
<div *ngIf="latestTelemetry.length; else noData" class="tb-form-table-body"> |
|||
<div class="tb-form-table-row" *ngFor="let telemetry of latestTelemetry"> |
|||
<div class="tb-form-table-row-cell" fxFlex>{{ telemetry.lastUpdateTs | date: 'yyyy-MM-dd HH:mm:ss' }}</div> |
|||
<div class="tb-form-table-row-cell" fxFlex>{{ telemetry.key }}</div> |
|||
<div class="tb-form-table-row-cell" fxFlex>{{ telemetry.value }}</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
</div> |
|||
<div mat-dialog-actions class="tb-dialog-actions"> |
|||
<mat-slide-toggle fxShow="{{ showDontShowAgain }}" [(ngModel)]="notShowAgain">{{ 'action.dont-show-again' | translate}}</mat-slide-toggle> |
|||
<span fxFlex></span> |
|||
<button mat-button |
|||
[disabled]="(isLoading$ | async)" |
|||
(click)="close()">{{ 'action.cancel' | translate }}</button> |
|||
</div> |
|||
<ng-template #loadingCommand> |
|||
<div class="tb-loader"> |
|||
<mat-spinner color="accent" diameter="65" strokeWidth="4"></mat-spinner> |
|||
<span class="mat-subtitle-1 label"> |
|||
{{ 'device.connectivity.loading-check-connectivity-command' | translate }} |
|||
</span> |
|||
</div> |
|||
</ng-template> |
|||
<ng-template #noData> |
|||
<div class="tb-no-data-available"> |
|||
<div class="tb-no-data-bg"></div> |
|||
<div class="tb-no-data-text" translate>attribute.no-latest-telemetry</div> |
|||
</div> |
|||
</ng-template> |
|||
@ -0,0 +1,151 @@ |
|||
/** |
|||
* Copyright © 2016-2023 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
@import "../../../../../scss/constants"; |
|||
|
|||
:host { |
|||
height: 100%; |
|||
max-height: 100vh; |
|||
display: grid; |
|||
grid-template-rows: min-content minmax(auto, 1fr) min-content; |
|||
|
|||
.tb-loader { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
gap: 16px; |
|||
height: 300px; |
|||
max-height: 100%; |
|||
|
|||
.label { |
|||
margin-bottom: 0; |
|||
text-align: center; |
|||
} |
|||
} |
|||
|
|||
.status { |
|||
margin-left: 12px; |
|||
border-radius: 12px; |
|||
height: 24px; |
|||
line-height: 24px; |
|||
padding: 0 8px; |
|||
width: fit-content; |
|||
color: #198038; |
|||
background-color: rgba(25, 128, 56, 0.08); |
|||
font-size: 14px; |
|||
|
|||
&.inactive { |
|||
color: #d12730; |
|||
background-color: rgba(209, 39, 48, 0.08); |
|||
} |
|||
} |
|||
|
|||
.tb-hint-instruction { |
|||
border-radius: 6px; |
|||
background-color: rgba(48, 86, 128, 0.04); |
|||
padding: 6px 16px; |
|||
|
|||
.content { |
|||
vertical-align: middle; |
|||
} |
|||
} |
|||
|
|||
.tb-font-14 { |
|||
font-size: 14px; |
|||
} |
|||
|
|||
.tb-form-table-body { |
|||
max-height: 88px; |
|||
overflow-y: auto; |
|||
scrollbar-gutter: stable; |
|||
|
|||
.tb-form-table-row { |
|||
min-height: 38px; |
|||
} |
|||
} |
|||
|
|||
.tb-no-data-available { |
|||
.tb-no-data-bg { |
|||
min-height: 68px; |
|||
} |
|||
} |
|||
|
|||
@media #{$mat-sm} { |
|||
width: 470px; |
|||
} |
|||
|
|||
@media #{$mat-gt-sm} { |
|||
width: 720px; |
|||
} |
|||
} |
|||
|
|||
:host-context(.mat-mdc-dialog-container) { |
|||
.tb-dialog-actions { |
|||
display: flex; |
|||
gap: 8px; |
|||
padding: 8px 16px; |
|||
} |
|||
|
|||
.mat-mdc-dialog-content { |
|||
max-height: 80vh; |
|||
padding: 16px; |
|||
} |
|||
} |
|||
|
|||
:host ::ng-deep { |
|||
.tb-markdown-view { |
|||
.tb-command-code { |
|||
.code-wrapper { |
|||
padding: 0; |
|||
pre[class*=language-] { |
|||
background: #F3F6FA; |
|||
border-color: #305680; |
|||
} |
|||
} |
|||
button.clipboard-btn { |
|||
right: 0; |
|||
p { |
|||
color: #305680; |
|||
} |
|||
p, div { |
|||
background-color: #F3F6FA; |
|||
} |
|||
div { |
|||
img { |
|||
display: none; |
|||
} |
|||
&:after { |
|||
content: ""; |
|||
position: initial; |
|||
display: block; |
|||
width: 18px; |
|||
height: 18px; |
|||
background: #305680; |
|||
mask-image: url(/assets/copy-code-icon.svg); |
|||
mask-repeat: no-repeat; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.mdc-button__label > span { |
|||
.mat-icon { |
|||
vertical-align: text-bottom; |
|||
box-sizing: initial; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,170 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { Component, Inject, NgZone, OnDestroy, OnInit } from '@angular/core'; |
|||
import { DialogComponent } from '@shared/components/dialog.component'; |
|||
import { select, Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { Router } from '@angular/router'; |
|||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; |
|||
import { DeviceService } from '@core/http/device.service'; |
|||
import { FormBuilder } from '@angular/forms'; |
|||
import { |
|||
AttributeData, |
|||
AttributeScope, |
|||
AttributesSubscriptionCmd, |
|||
LatestTelemetry, |
|||
TelemetrySubscriber |
|||
} from '@shared/models/telemetry/telemetry.models'; |
|||
import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; |
|||
import { EntityId } from '@shared/models/id/entity-id'; |
|||
import { EntityType } from '@shared/models/entity-type.models'; |
|||
import { selectPersistDeviceStateToTelemetry } from '@core/auth/auth.selectors'; |
|||
import { take } from 'rxjs/operators'; |
|||
import { |
|||
BasicTransportType, |
|||
DeviceTransportType, |
|||
deviceTransportTypeTranslationMap, |
|||
NetworkTransportType |
|||
} from '@shared/models/device.models'; |
|||
import { UserSettingsService } from '@core/http/user-settings.service'; |
|||
import { ActionPreferencesUpdateUserSettings } from '@core/auth/auth.actions'; |
|||
|
|||
export interface DeviceCheckConnectivityDialogData { |
|||
deviceId: EntityId; |
|||
showDontShowAgain: boolean; |
|||
} |
|||
@Component({ |
|||
selector: 'tb-device-check-connectivity-dialog', |
|||
templateUrl: './device-check-connectivity-dialog.component.html', |
|||
styleUrls: ['./device-check-connectivity-dialog.component.scss'] |
|||
}) |
|||
export class DeviceCheckConnectivityDialogComponent extends |
|||
DialogComponent<DeviceCheckConnectivityDialogComponent> implements OnInit, OnDestroy { |
|||
|
|||
loadedCommand = false; |
|||
|
|||
status: boolean; |
|||
|
|||
latestTelemetry: Array<AttributeData> = []; |
|||
|
|||
commands: {[key: string]: string}; |
|||
|
|||
allowTransportType = new Set<NetworkTransportType>(); |
|||
selectTransportType: NetworkTransportType; |
|||
|
|||
BasicTransportType = BasicTransportType; |
|||
DeviceTransportType = DeviceTransportType; |
|||
deviceTransportTypeTranslationMap = deviceTransportTypeTranslationMap; |
|||
|
|||
showDontShowAgain = this.data.showDontShowAgain; |
|||
|
|||
notShowAgain = false; |
|||
|
|||
private telemetrySubscriber: TelemetrySubscriber; |
|||
|
|||
private currentTime = Date.now(); |
|||
|
|||
private transportTypes = [...Object.keys(BasicTransportType), ...Object.keys(DeviceTransportType)] as Array<NetworkTransportType>; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
protected router: Router, |
|||
@Inject(MAT_DIALOG_DATA) private data: DeviceCheckConnectivityDialogData, |
|||
public dialogRef: MatDialogRef<DeviceCheckConnectivityDialogComponent>, |
|||
private fb: FormBuilder, |
|||
private deviceService: DeviceService, |
|||
private telemetryWsService: TelemetryWebsocketService, |
|||
private userSettingsService: UserSettingsService, |
|||
private zone: NgZone) { |
|||
super(store, router, dialogRef); |
|||
} |
|||
|
|||
ngOnInit() { |
|||
this.loadCommands(); |
|||
this.subscribeToLatestTelemetry(); |
|||
} |
|||
|
|||
ngOnDestroy() { |
|||
super.ngOnDestroy(); |
|||
this.telemetrySubscriber?.complete(); |
|||
this.telemetrySubscriber?.unsubscribe(); |
|||
} |
|||
|
|||
close(): void { |
|||
if (this.notShowAgain && this.showDontShowAgain) { |
|||
this.store.dispatch(new ActionPreferencesUpdateUserSettings({ notDisplayConnectivityAfterAddDevice: true })); |
|||
this.dialogRef.close(null); |
|||
} else { |
|||
this.dialogRef.close(null); |
|||
} |
|||
} |
|||
|
|||
createMarkDownCommand(command: string): string { |
|||
return '```bash\n' + |
|||
command + |
|||
'{:copy-code}\n' + |
|||
'```'; |
|||
} |
|||
|
|||
private loadCommands() { |
|||
this.deviceService.getDevicePublishTelemetryCommands(this.data.deviceId.id).subscribe( |
|||
commands => { |
|||
this.commands = commands; |
|||
const commandsProtocols = Object.keys(commands); |
|||
this.transportTypes.forEach(transport => { |
|||
const findCommand = commandsProtocols.find(item => item.toUpperCase().startsWith(transport)); |
|||
if (findCommand) { |
|||
this.allowTransportType.add(transport); |
|||
} |
|||
}); |
|||
this.selectTransportType = this.allowTransportType.values().next().value; |
|||
this.loadedCommand = true; |
|||
} |
|||
); |
|||
} |
|||
|
|||
private subscribeToLatestTelemetry() { |
|||
this.store.pipe(select(selectPersistDeviceStateToTelemetry)).pipe( |
|||
take(1) |
|||
).subscribe(persistToTelemetry => { |
|||
this.telemetrySubscriber = TelemetrySubscriber.createEntityAttributesSubscription( |
|||
this.telemetryWsService, this.data.deviceId, LatestTelemetry.LATEST_TELEMETRY, this.zone); |
|||
if (!persistToTelemetry) { |
|||
const subscriptionCommand = new AttributesSubscriptionCmd(); |
|||
subscriptionCommand.entityType = this.data.deviceId.entityType as EntityType; |
|||
subscriptionCommand.entityId = this.data.deviceId.id; |
|||
subscriptionCommand.scope = AttributeScope.SERVER_SCOPE; |
|||
subscriptionCommand.keys = 'active'; |
|||
this.telemetrySubscriber.subscriptionCommands.push(subscriptionCommand); |
|||
} |
|||
|
|||
this.telemetrySubscriber.subscribe(); |
|||
this.telemetrySubscriber.attributeData$().subscribe( |
|||
(data) => { |
|||
this.latestTelemetry = data.reduce<Array<AttributeData>>((accumulator, item) => { |
|||
if (item.key === 'active') { |
|||
this.status = item.value; |
|||
} else if (item.lastUpdateTs > this.currentTime) { |
|||
accumulator.push(item); |
|||
} |
|||
return accumulator; |
|||
}, []); |
|||
} |
|||
); |
|||
}); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
#### CoAP installation instructions |
|||
--- |
|||
<br /> |
|||
|
|||
Install coap client tool on your **Linux/macOS**: |
|||
|
|||
```bash |
|||
git clone https://github.com/obgm/libcoap --recursive |
|||
{:copy-code} |
|||
``` |
|||
<br /> |
|||
|
|||
```bash |
|||
cd libcoap |
|||
{:copy-code} |
|||
``` |
|||
<br /> |
|||
|
|||
```bash |
|||
./autogen.sh |
|||
{:copy-code} |
|||
``` |
|||
<br /> |
|||
|
|||
```bash |
|||
./configure --with-openssl --disable-doxygen --disable-manpages --disable-shared |
|||
{:copy-code} |
|||
``` |
|||
<br /> |
|||
|
|||
```bash |
|||
make |
|||
{:copy-code} |
|||
``` |
|||
<br /> |
|||
|
|||
```bash |
|||
sudo make install |
|||
{:copy-code} |
|||
``` |
|||
@ -0,0 +1,34 @@ |
|||
#### cURL installation instructions |
|||
--- |
|||
<section style="margin: 18px 0"> |
|||
<div style="margin: 0 32px"> |
|||
<tb-toggle-header #publishCommandSteps value="ubuntu" name="publishCommandSteps" useSelectOnMdLg="false" appearance="fill"> |
|||
<tb-toggle-option value="ubuntu">Ubuntu</tb-toggle-option> |
|||
<tb-toggle-option value="macos">MacOS</tb-toggle-option> |
|||
<tb-toggle-option value="windows">Windows</tb-toggle-option> |
|||
</tb-toggle-header> |
|||
</div> |
|||
<ng-container [ngSwitch]="publishCommandSteps.value"> |
|||
<ng-template [ngSwitchCase]="'ubuntu'"> |
|||
<p style="margin-top: 18px; padding: 0 32px">Install cURL tool:</p> |
|||
<tb-markdown usePlainMarkdown containerClass="tb-getting-started-code" data=" |
|||
```bash |
|||
sudo apt-get install curl |
|||
{:copy-code} |
|||
``` |
|||
"></tb-markdown> |
|||
</ng-template> |
|||
<ng-template [ngSwitchCase]="'macos'"> |
|||
<p style="margin-top: 18px; padding: 0 32px">Install cURL tool:</p> |
|||
<tb-markdown usePlainMarkdown containerClass="tb-getting-started-code" data=" |
|||
```bash |
|||
brew install curl |
|||
{:copy-code} |
|||
``` |
|||
"></tb-markdown> |
|||
</ng-template> |
|||
<ng-template [ngSwitchCase]="'windows'"> |
|||
<div style="margin-top: 18px; padding: 0 32px; width: 404px;">Starting Windows 10 b17063, cURL is available by default.</div> |
|||
</ng-template> |
|||
</ng-container> |
|||
</section> |
|||
@ -0,0 +1,38 @@ |
|||
#### MQTT client tool installation instructions |
|||
--- |
|||
<section style="margin: 18px 0"> |
|||
<div style="margin: 0 32px"> |
|||
<tb-toggle-header #publishCommandSteps value="ubuntu" name="publishCommandSteps" useSelectOnMdLg="false" appearance="fill"> |
|||
<tb-toggle-option value="ubuntu">Ubuntu</tb-toggle-option> |
|||
<tb-toggle-option value="macos">MacOS</tb-toggle-option> |
|||
<tb-toggle-option value="windows">Windows</tb-toggle-option> |
|||
</tb-toggle-header> |
|||
</div> |
|||
<ng-container [ngSwitch]="publishCommandSteps.value"> |
|||
<ng-template [ngSwitchCase]="'ubuntu'"> |
|||
<p style="margin-top: 18px; padding: 0 32px">Install mqtt client tool:</p> |
|||
<tb-markdown usePlainMarkdown containerClass="tb-getting-started-code" data=" |
|||
```bash |
|||
sudo apt-get install curl mosquitto-clients |
|||
{:copy-code} |
|||
``` |
|||
"></tb-markdown> |
|||
</ng-template> |
|||
<ng-template [ngSwitchCase]="'macos'"> |
|||
<p style="margin-top: 18px; padding: 0 32px">Install mqtt client tool:</p> |
|||
<tb-markdown usePlainMarkdown containerClass="tb-getting-started-code" data=" |
|||
```bash |
|||
brew install mosquitto-clients |
|||
{:copy-code} |
|||
``` |
|||
"></tb-markdown> |
|||
</ng-template> |
|||
<ng-template [ngSwitchCase]="'windows'"> |
|||
<p style="margin-top: 18px; padding: 0 32px">Install mqtt client tool:</p> |
|||
<a mat-stroked-button color="primary" class="ignore-style-a" |
|||
style="margin: 8px 32px" |
|||
href="https://thingsboard.io/docs/getting-started-guides/helloworld/?connectdevice=mqtt-windows#step-2-connect-device" target="_blank"> |
|||
<mat-icon>description</mat-icon>How to install MQTT Box</a> |
|||
</ng-template> |
|||
</ng-container> |
|||
</section> |
|||
@ -0,0 +1,21 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
type NestedKeyOf<ObjectType extends object> = |
|||
{[Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object |
|||
? `${Key}` | `${Key}.${NestedKeyOf<ObjectType[Key]> extends infer U extends string ? U : never}` |
|||
: `${Key}` |
|||
}[keyof ObjectType & (string | number)]; |
|||
Loading…
Reference in new issue