UNPKG

@iotize/ionic

Version:

Iotize specific building blocks on top of @ionic/angular.

1 lines 457 kB
{"version":3,"file":"iotize-ionic.mjs","sources":["../../../../projects/iotize-ionic/src/injection-types.ts","../../../../projects/iotize-ionic/src/lib/config-version.ts","../../../../projects/iotize-ionic/src/lib/extensions/device-client-extensions.ts","../../../../projects/iotize-ionic/src/lib/logger.ts","../../../../projects/iotize-ionic/src/lib/extensions/data-manager/impl.ts","../../../../projects/iotize-ionic/src/lib/nfc/utility.ts","../../../../projects/iotize-ionic/src/lib/protocol-factory.service.ts","../../../../projects/iotize-ionic/src/lib/rx-utility/run-in-zone.ts","../../../../projects/iotize-ionic/src/lib/errors.ts","../../../../projects/iotize-ionic/src/lib/utility.ts","../../../../projects/iotize-ionic/src/lib/current-device.service.ts","../../../../projects/iotize-ionic/src/lib/connected-tap-resolver.ts","../../../../projects/iotize-ionic/src/lib/csv-helper.ts","../../../../projects/iotize-ionic/src/lib/definitions/cloud.ts","../../../../projects/iotize-ionic/src/lib/disconnect-current-tap.guard.ts","../../../../projects/iotize-ionic/src/lib/error-utility.ts","../../../../projects/iotize-ionic/src/lib/execute-tap-action/execute-tap-action.provider.ts","../../../../projects/iotize-ionic/src/lib/extensions/datalogger/data-logger.ts","../../../../projects/iotize-ionic/src/lib/has-current-tap.guard.ts","../../../../projects/iotize-ionic/src/lib/inline-editor/value-or-default.pipe.ts","../../../../projects/iotize-ionic/src/lib/inline-editor/inline-editor.component.ts","../../../../projects/iotize-ionic/src/lib/inline-editor/inline-editor.component.html","../../../../projects/iotize-ionic/src/lib/lib-common/auto-focus.directive.ts","../../../../projects/iotize-ionic/src/lib/lib-common/disable-control.directive.ts","../../../../projects/iotize-ionic/src/lib/lib-common/enum.pipe.ts","../../../../projects/iotize-ionic/src/lib/lib-common/extract-bits.pipe.ts","../../../../projects/iotize-ionic/src/lib/lib-common/remember-value-control.ts","../../../../projects/iotize-ionic/src/lib/lib-common/tap-request.pipe.ts","../../../../projects/iotize-ionic/src/lib/lib-common/trim.pipe.ts","../../../../projects/iotize-ionic/src/lib/lib-common/lib-common.module.ts","../../../../projects/iotize-ionic/src/lib/pending-call-manager.ts","../../../../projects/iotize-ionic/src/lib/tap-value-editor-modal/tap-value-editor-modal.component.ts","../../../../projects/iotize-ionic/src/lib/tap-value-editor-modal/tap-value-editor-modal.component.html","../../../../projects/iotize-ionic/src/lib/tap-value-editor-container/tap-value-editor-container.component.ts","../../../../projects/iotize-ionic/src/lib/tap-value-editor-container/tap-value-editor-container.component.html","../../../../projects/iotize-ionic/src/lib/tap-value-editor-container/tap-value-editor-container.module.ts","../../../../projects/iotize-ionic/src/lib/inline-editor/inline-editor.module.ts","../../../../projects/iotize-ionic/src/lib/lib-common/global-events-service.ts","../../../../projects/iotize-ionic/src/lib/mock-variable.ts","../../../../projects/iotize-ionic/src/lib/multi-requests/multi-requests-utility.ts","../../../../projects/iotize-ionic/src/lib/nfc/index.ts","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-nfc/nfc-service.service.ts","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-nfc/tap-scanner-nfc.service.ts","../../../../projects/iotize-ionic/src/lib/nfc-re-tap.service.ts","../../../../projects/iotize-ionic/src/lib/tap-info/keys.ts","../../../../projects/iotize-ionic/src/lib/tap-info/constants.ts","../../../../projects/iotize-ionic/src/lib/tap-info/tap-info-config.service.ts","../../../../projects/iotize-ionic/src/lib/tap-info/utility.ts","../../../../projects/iotize-ionic/src/lib/tap-info/tap-info-cache.service.ts","../../../../projects/iotize-ionic/src/lib/tap-administration.service.ts","../../../../projects/iotize-ionic/src/lib/tap-info/uniq-array.ts","../../../../projects/iotize-ionic/src/lib/tap-info/uniq-key-queue.ts","../../../../projects/iotize-ionic/src/lib/tap-info/tap-info-request.service.ts","../../../../projects/iotize-ionic/src/lib/tap-connect/tap-connection-store/tap-connection-store.service.ts","../../../../projects/iotize-ionic/src/lib/tap-connect/tap-connection-store/tap-connection-store-item/tap-connection-store-item.component.ts","../../../../projects/iotize-ionic/src/lib/tap-connect/tap-connection-store/tap-connection-store-item/tap-connection-store-item.component.html","../../../../projects/iotize-ionic/src/lib/tap-connect/tap-connection-store/tap-connection-store-list/tap-connection-store-list.component.ts","../../../../projects/iotize-ionic/src/lib/tap-connect/tap-connection-store/tap-connection-store-list/tap-connection-store-list.component.html","../../../../projects/iotize-ionic/src/lib/tap-connect/tap-connection-store/tap-connection-store.module.ts","../../../../projects/iotize-ionic/src/lib/tap-connection.service.ts","../../../../projects/iotize-ionic/src/lib/tap-api-explorer.service.ts","../../../../projects/iotize-ionic/src/lib/tap-info/tap-info-dao.service.ts","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-ble/manufacturer-data-filter.ts","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-ble/ble-service.service.ts","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-common/ble-rssi-signal/ble-rssi-signal.component.ts","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-common/ble-rssi-signal/ble-rssi-signal.component.html","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-common/rssi-to-signal-strenght.pipe.ts","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-ble/tap-scan-results/tap-scan-results.component.ts","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-ble/tap-scan-results/tap-scan-results.component.html","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-ble/tap-scanner-ble.component.ts","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-ble/tap-scanner-ble.component.html","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-common/tap-scanner-common.module.ts","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-ble/tap-scanner-ble.module.ts","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-web-bluetooth/tap-scanner-web-bluetooth.component.ts","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-web-bluetooth/tap-scanner-web-bluetooth.component.html","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-web-bluetooth/tap-scanner-web-bluetooth.module.ts","../../../../projects/iotize-ionic/src/lib/tap-nfc-initialization.resolver.ts","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-wifi/tap-scanner-wifi-item/tap-scanner-wifi-item.component.ts","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-wifi/tap-scanner-wifi-item/tap-scanner-wifi-item.component.html","../../../../projects/iotize-ionic/src/lib/tap-scanner/tap-scanner-wifi/tap-scanner-wifi.module.ts","../../../../projects/iotize-ionic/src/lib/utility/protocol-meta-param-converter.ts","../../../../projects/iotize-ionic/src/lib/tap-selected.guard.ts","../../../../projects/iotize-ionic/src/lib/tap-target-connect.service.ts","../../../../projects/iotize-ionic/src/lib/task-manager/provider.ts","../../../../projects/iotize-ionic/src/lib/task-manager/task-manager.service.ts","../../../../projects/iotize-ionic/src/lib/task-manager/task-manager.module.ts","../../../../projects/iotize-ionic/src/lib/task-manager-ui/task-manager.component.ts","../../../../projects/iotize-ionic/src/lib/task-manager-ui/task-manager.component.html","../../../../projects/iotize-ionic/src/lib/task-manager-ui/task-manager-ui.module.ts","../../../../projects/iotize-ionic/src/lib/config.ts","../../../../projects/iotize-ionic/src/public-api.ts","../../../../projects/iotize-ionic/src/iotize-ionic.ts"],"sourcesContent":["export const TAP_BLE_SCANNER = 'TAP_BLE_SCANNER';\nexport const TAP_WIFI_SCANNER = 'TAP_WIFI_SCANNER';\nexport const TAP_NETWORK_SCANNER = 'TAP_NETWORK_SCANNER';\n","export const CONFIGURATION_MODE_VERSION = '255.255.65535';\nexport const FACTORY_RESET_MODE_VERSION = '0.0.0';\n","import { AppPathType, TapResponse, TapVersion } from '@iotize/tap';\nimport { InterfaceService } from '@iotize/tap/service/impl/interface';\nimport { TapnpassService } from '@iotize/tap/service/impl/tapnpass';\nimport { TargetService } from '@iotize/tap/service/impl/target';\n\ndeclare module '@iotize/tap/service/impl/tapnpass/lib/generated/service' {\n export interface TapnpassService {\n getAdpVersion(): Promise<TapVersion>;\n }\n}\nexport async function adpServiceGetAdpVersion(\n this: TapnpassService\n): Promise<TapVersion> {\n const response: TapResponse<any> = await this.getStatus();\n response.successful();\n const data = await response.rawBody();\n const version = data.slice(2, 5);\n return {\n major: version[0],\n minor: version[1],\n patch: version[2],\n };\n}\n\nTapnpassService.prototype.getAdpVersion = adpServiceGetAdpVersion;\n\ndeclare module '@iotize/tap/service/impl/interface/lib/generated/service' {\n export interface InterfaceService {\n getAppPathResolved(): Promise<TapResponse<string>>;\n\n getConfigPath(): Promise<TapResponse<string>>;\n }\n}\n\n// export async function interfaceServiceGetAppType(this: InterfaceService): Promise<TapVersion>{\n// let response: TapResponse<any> = await this.\n// }\n\n// InterfaceService.prototype.getAppType = interfaceServiceGetAppType;\n\nexport async function interfaceServiceGetAppPathResolved(\n this: InterfaceService\n): Promise<TapResponse<string>> {\n const response: TapResponse<string> = (await this.getAppPath()) as any;\n if (response.isSuccessful()) {\n let value = response.body();\n if (value.length >= 3) {\n const start = value.substring(0, 3);\n const valueWithoutPrefix = value.substring(3);\n switch (start) {\n case AppPathType.PRIMER_CLOUD_WEB_APP_URL:\n value = `https://user.cloud.iotize.com/users/${valueWithoutPrefix}`;\n break;\n case AppPathType.URL:\n value = `${valueWithoutPrefix}`;\n break;\n case AppPathType.PRIMER_CLOUD_CONFIG_URL:\n value = `https://user.cloud.iotize.com/users/${valueWithoutPrefix}`;\n break;\n }\n }\n response.setBody(value);\n }\n return response;\n}\n\nInterfaceService.prototype.getAppPathResolved =\n interfaceServiceGetAppPathResolved;\n\nexport async function interfaceServiceGetConfigPath(\n this: InterfaceService\n): Promise<TapResponse<string>> {\n const response: TapResponse<string> =\n (await this.getAppPath()) as TapResponse<string>;\n if (response.isSuccessful()) {\n let value = response.body();\n const start = value.substr(0, 2);\n switch (start) {\n case '$5':\n value = `http://user.cloud.iotize.com/users${value.substr(2)}`;\n break;\n default:\n if (\n !value.endsWith('.json') &&\n !value.endsWith('.xml') &&\n !value.endsWith('.cloud')\n ) {\n value = '';\n }\n }\n response.setBody(value);\n }\n return response;\n}\n\nInterfaceService.prototype.getConfigPath = interfaceServiceGetConfigPath;\n\ndeclare module '@iotize/tap/service/impl/target/lib/generated/service' {\n export interface TargetService {\n getAppType(): Promise<TapResponse<string>>;\n }\n}\n\n// export async function targetServiceGetAppType(this: TargetService): Promise<TapResponse<string>> {\n// let response = await this.getProtocol();\n// let protocol = response.body();\n// let newResponse: TapResponse<string> = TapResponse.SUCCESS();\n// switch (protocol) {\n// case TargetProtocol.SERIAL_VIA_TAPNPASS:\n// newResponse.setBody(\"tapnpass\");\n// default:\n// newResponse.setBody(\"tapnlink\");\n// }\n// return newResponse;\n// }\n\nexport async function targetServiceGetAppType(\n this: TargetService\n): Promise<TapResponse<string>> {\n const response = await this.getSubProtocol();\n const newResponse: TapResponse<string> = TapResponse.SUCCESS();\n if (response.isSuccessful()) {\n newResponse.setBody('tapnpass');\n } else {\n newResponse.setBody('tapnlink');\n }\n return newResponse;\n}\n\nTargetService.prototype.getAppType = targetServiceGetAppType;\n\n// export async function isConnected(this: TargetService): Promise<TapResponse<boolean>> {\n// return this._call({\n// path: \"/target/connect\",\n// methodType: \"get\",\n// responseBodyDecoder: \"boolean\"\n// })\n// }\n\n// TargetService.prototype.isConnected = isConnected;\n","import { createDebugger } from '@iotize/common/debug';\r\n\r\nexport const debug = createDebugger('@iotize/ionic/core');\r\n","import { Tap } from '@iotize/tap';\nimport { TlvBundleConverter } from '@iotize/tap/client/impl';\nimport {\n BundleConfig,\n TapConfiguratorConfig,\n} from '@iotize/tap/config/schema/v1';\nimport { MonitorEngine } from '@iotize/tap/data';\nimport { DataLogger } from '@iotize/tap/data-log';\nimport '@iotize/tap/ext/data';\nimport { Observable } from 'rxjs';\nimport { debug } from '../../logger';\nimport { SwitchableStreamMonitor as SwitchableStreamController } from '../switchable-stream-monitor';\nimport {\n DataSourceController,\n DataSourceType,\n ItemHolder,\n} from './definitions';\n\nconst TAG = 'DataManager';\n\nexport class DataHolder {\n constructor(public items: ItemHolder[] = []) {}\n\n item(id: string): ItemHolder {\n const item = this.items.find((v) => v.id == id);\n if (!item) {\n throw new Error(`Item with id \"${id}\" does not exist`);\n }\n return item;\n }\n\n add(item: ItemHolder, replace: boolean) {\n if (replace || this.items.find((v) => v.id == item.id) == undefined) {\n debug(TAG, 'Added item: ', item);\n this.items.push(item);\n }\n return this.item(item.id);\n }\n}\n\n// export function newBundleConfigToOldBundleConfig(bundlesConfig: BundleConfig[] | undefined): BundleConfigPredefined[] {\n// if (!bundlesConfig) {\n// return [];\n// }\n// return bundlesConfig.map((bundleConfig: BundleSchemaConfig) => {\n// const predefinedConfig: BundleConfigPredefined = {\n// id: bundleConfig.id,\n// converter: undefined,\n// name: bundleConfig.name || `Bundle ${bundleConfig.id}`,\n// variables: (bundleConfig.variables || []).map((variableConfig: MemoryVariableConfig) => {\n// const variableType = VariableType[variableConfig.type];\n// if (variableType === undefined) {\n// throw new Error(`Invalid variable type: ${variableConfig.type}`);\n// }\n// let converter: BodyConverter<any> | undefined;\n// if (variableConfig.converter) {\n// converter = variableConfig.converter;\n// } else {\n// converter = getConverterFromFormat(variableType, variableConfig.length || 1);\n// }\n// const predifinedVariableConfig: TapVariable.Config = {\n// id: variableConfig.id,\n// converter: converter,\n// length: variableConfig.length,\n// name: variableConfig.name || `Variable ${variableConfig.id}`,\n// type: variableType,\n// unit: variableConfig.unit\n// };\n// return predifinedVariableConfig;\n// })\n// };\n// return predefinedConfig;\n// });\n// }\n\nexport class DataManagerIonic<\n VariableByType extends Record<string, any> = Record<string, any>,\n VariableKey extends string = string,\n BundleByType extends Record<string, any> = Record<string, any>,\n BundleKey extends string = string\n> {\n constructor(public tap: Tap) {\n // const allVariables: AbstractVariable<VariableByType[VariableKey]>[] = Object.values(data.variables);\n // this.tlvConverter = new TlvBundleConverter(allVariables.map((variable: AbstractVariable<any>) => {\n // if (!(variable instanceof VariableConfig)) {\n // throw new Error(`Variable ${variable.identifier} has no config`);\n // }\n // return {\n // id: variable.config.id,\n // name: variable.config.name,\n // converter: variable.converter\n // };\n // }));\n // this.dataStream = this.setupDataStream();\n // this.dataLogger = DataLogger.fromTap(\n // this.tap,\n // {\n // period: 2000 // TODO change\n // }\n // );\n // this.tap.variables._variables = data.variables as any;\n // this.tap.bundles._bundles = data.bundles as any;\n // Change variable stream;\n // Object.values(this.data.variables).map((variable: any) => {\n // const variableName = variable.config.name;\n // // console.log('Replacing monitor for ', variable);\n // variable._monitor = ObservableMonitor.fromObservable(\n // this.dataStream\n // .pipe(\n // filter(data => variableName in data),\n // map(data => data[variableName]),\n // rxTap(value => debug(TAG, `Extracted key ${variableName} from monitoring data`, value))\n // )\n // ,\n // MonitorEngine.State.START\n // );\n // });\n // // Change bundle stream;\n // Object.values(this.data.bundles).map((bundle: any) => {\n // bundle._monitor = ObservableMonitor.fromObservable(\n // this.dataStream\n // .pipe(\n // rxTap(value => debug(TAG, `New bundle value `, value))\n // )\n // ,\n // MonitorEngine.State.START\n // );\n // // TODO factorize and clean\n // Object.values(bundle.variables).map((variable: any) => {\n // const variableName = variable.config.name;\n // // console.log('Replacing monitor for variable in bundle', variable);\n // variable._monitor = ObservableMonitor.fromObservable(\n // this.dataStream\n // .pipe(\n // filter(data => variableName in data),\n // map(data => data[variableName]),\n // rxTap(value => debug(TAG, `Extracted key ${variableName} from monitoring data`, value))\n // )\n // ,\n // MonitorEngine.State.START\n // );\n // });\n // });\n // debug(TAG, 'New data manager instance: ', this);\n }\n\n // get variables(): Record<VariableKey, AbstractVariable<VariableByType[VariableKey]>> {\n // return this.data.variables;\n // }\n\n // get bundles(): Record<BundleKey, Bundle<BundleByType[BundleKey]>> {\n // return this.data.bundles;\n // }\n\n public get sourceController(): DataSourceController {\n const currentSourceId =\n this.switchableStreamController.getCurrentSourceId();\n return this.sourceControllers[currentSourceId];\n }\n\n get isMonitoringRunning() {\n for (const value of Object.values(this.sourceControllers)) {\n if (value.state == MonitorEngine.State.START) {\n return true;\n }\n }\n return false;\n }\n\n switchableStreamController!: SwitchableStreamController<any>;\n // dataStream: Observable<Record<string, any>>;\n // tlvConverter: TlvBundleConverter<Record<string, Uint8Array>>;\n // dataLogger: DataLogger;\n sourceControllers: Record<string, DataSourceController> = {};\n\n // public static fromSchemaConfig(tap: Tap, schema: TapConfiguratorConfig): DataManagerIonic {\n // const bundles = schema.config.data ? schema.config.data.bundles || [] : [];\n // return DataManagerIonic.fromBundleConfig(tap, bundles);\n // }\n\n // public static fromBundleConfig(tap: Tap, bundles: BundleConfig[]): DataManagerIonic {\n // return new DataManagerIonic(\n // tap,\n // DataManagerIonic.createDataFromSchemaConfig(tap, bundles)\n // );\n // }\n\n // public static createDataFromSchemaConfig<VariableByType extends Record<string, any>, VariableKey extends string, BundleByType extends Record<string, any>, BundleKey extends string>(tap: Tap, bundlesConfig: BundleSchemaConfig[]) {\n // const predifinedConfig: BundleConfigPredefined[] = newBundleConfigToOldBundleConfig(bundlesConfig);\n\n // return DataManagerIonic.createDataFromConfig(tap, predifinedConfig);\n // }\n\n // public static createDataFromConfig<VariableByType extends Record<string, any>, VariableKey extends string, BundleByType extends Record<string, any>, BundleKey extends string>(tap: Tap, bundlesConfig: BundleConfigPredefined[]) {\n // // let bundleConfigCopy = [...bundlesConfig];\n // const bundles: Record<string, any> = {};\n // bundlesConfig.forEach((bundleConfig: BundleConfigPredefined) => {\n // const bundle: Bundle = Bundle.createFromConfig(bundleConfig, tap.service.bundle, tap.service.variable);\n // bundles[bundleConfig.name] = bundle;\n // });\n\n // const variables: Record<string, any> = {};\n // bundlesConfig.forEach((bundle) => {\n // bundle.variables.forEach((variableConfig: TapVariable.Config) => {\n // debug(TAG, 'Create from variable config: ', variableConfig);\n // variables[variableConfig.name || variableConfig.id.toString()] = TapVariable.createFromConfig(variableConfig, tap.service.variable);\n // });\n // });\n\n // return {\n // bundles: bundles as Record<BundleKey, Bundle<BundleByType[BundleKey]>>,\n // variables: variables as Record<VariableKey, AbstractVariable<VariableByType[VariableKey]>>\n // };\n // }\n\n listVariables() {\n return this.tap.data.listVariables();\n }\n\n listBundles() {\n return this.tap.data.listBundles();\n }\n\n // public variable<T extends VariableKey>(key: T): AbstractVariable<VariableByType[T]> {\n // if (!(key in this.variables)) {\n // throw new Error(`Variable with identifier \"${key}\" is not configured`);\n // }\n // return this.variables[key] as AbstractVariable<VariableByType[T]>;\n // }\n\n // public bundle<T extends BundleKey>(key: T): Bundle<BundleByType[T]> {\n // if (!(key in this.bundles)) {\n // throw new Error(`Bundle with identifier \"${key}\" is not configured`);\n // }\n // return this.bundles[key] as any;\n // }\n\n public stopAll() {\n debug(TAG, 'Stop monitoring');\n // for (const ctrlKey in this.sourceControllers) {\n // this.sourceControllers[ctrlKey].state = MonitorEngine.State.PAUSE;\n // }\n this.tap.data.monitoring.stop();\n }\n\n public startAll(period?: number) {\n // debug(TAG, `[Controller=${this.sourceController.id}] Start monitoring with period ${period || this.sourceController.period.value}`, this.sourceController);\n // if (period) {\n // this.setMonitoringPeriod(period);\n // }\n // this.sourceController.state = MonitorEngine.State.START;\n this.tap.data.monitoring.start();\n }\n\n setMonitoringPeriod(period: number) {\n debug(TAG, 'Changing monitoring period to ', period);\n this.sourceController.period.next(period);\n }\n\n public destroy() {\n this.stopAll();\n }\n\n public refresh() {\n if (this.sourceController.refresh) {\n this.sourceController.refresh();\n }\n }\n\n changeDataSource(dataSource: DataSourceType) {\n switch (dataSource) {\n case 'datalog':\n this.useDatalogSource();\n break;\n default:\n this.useLiveSource();\n }\n }\n\n useLiveSource() {\n this.switchableStreamController.useSource('live');\n }\n\n useDatalogSource() {\n this.switchableStreamController.useSource('datalog');\n }\n\n // setupDataStream() {\n // const monitoringPeriod = 1000;\n // debug(TAG, 'setupDataStream', `Period: ${monitoringPeriod}`);\n // this.sourceControllers['datalog'] = {\n // state: MonitorEngine.State.STOP,\n // period: new BehaviorSubject(monitoringPeriod),\n // id: `datalog`\n // };\n // const datalogStream = ObservableMonitor.createStreamFromCallable(\n // () => {\n // debug(TAG, 'New data logger call');\n // return this.dataLogger.dequeue()\n // .then((info) => {\n // debug(TAG, '===> NEW PACKET DEQUEUE: ', info);\n // return info;\n // })\n // .then(info => info.packet.rawVariables)\n // .then((rawVariables: any) => {\n // for (const id of Object.keys(rawVariables)) {\n // const variable: any = Object.values(this.variables).find((v: any) => v.config.id == id);\n // if (variable) {\n // rawVariables[variable.config.name] = variable.converter.decode(rawVariables[id]);\n // delete rawVariables[id];\n // } else {\n // debug(TAG, 'Variable with id ', id, ' is not registered');\n // }\n // }\n // return rawVariables;\n // })\n // .then((info) => {\n // debug(TAG, '===> NEW PACKET INTERPERT: ', info);\n // return info;\n // });\n // },\n // {\n // dueTime: 0,\n // ignoreErrors: true\n // },\n // this.sourceControllers['datalog']\n // );\n\n // this.sourceControllers['live'] = {\n // state: MonitorEngine.State.STOP,\n // period: new BehaviorSubject(monitoringPeriod),\n // id: `live`\n // };\n // const liveProfileStream = ObservableMonitor.createStreamFromCallable<any>(\n // async (time: number) => {\n // debug(TAG, `New call ${time} to read profile`);\n // const profileData = (await this.tap.service.variable.readProfile()).body();\n // return this.tlvConverter.decode(profileData);\n // },\n // {\n // dueTime: 0,\n // ignoreErrors: true\n // },\n // this.sourceControllers['live']\n // );\n\n // this.switchableStreamController = new SwitchableStreamController(\n // {\n // 'live': {\n // id: 'live',\n // stream: liveProfileStream\n // },\n // 'datalog': {\n // id: 'datalog',\n // stream: datalogStream\n // }\n // },\n // 'live'\n // );\n\n // return this.switchableStreamController.asObservable();\n // // .pipe(\n // // rxTap(values => {\n // // debug(TAG, '#################', values)\n // // })\n // // );\n // }\n}\n","import { NfcTag } from '@awesome-cordova-plugins/nfc/ngx';\nimport { bufferToHexString } from '@iotize/common/byte-converter';\nimport { isCodeError } from '@iotize/common/error';\nimport { NdefTag, NfcError } from '@iotize/device-com-nfc.cordova';\n\nexport function isSameTag(t1: NdefTag | NfcTag, t2: NdefTag | NfcTag) {\n return (\n bufferToHexString(t1.id as any as Uint8Array) ===\n bufferToHexString(t2.id as any as Uint8Array)\n );\n}\n\nexport function isNfcTapRequiredError(err: Error) {\n const cause = getCauseError(err);\n return (\n isCodeError(NfcError.ErrorCode.TagLostError, cause) ||\n isCodeError(NfcError.ErrorCode.NotConnectedError, cause) ||\n isCodeError(NfcError.ErrorCode.Unknown, cause) ||\n isCodeError(NfcError.ErrorCode.InternalError, cause)\n );\n}\n\nexport function getCauseError(err: Error & { cause?: Error }) {\n let depth = 0;\n while (err.cause && depth < 5) {\n err = err.cause as Error;\n depth++;\n }\n return err;\n}\n","import { ComProtocol } from '@iotize/tap/protocol/api';\nimport { ProtocolMeta } from './extensions/protocol-info';\n\nexport type ProtocolFactoryFct<T extends Array<any> = Array<any>> = (\n meta: any,\n ...args: T\n) => ComProtocol | Promise<ComProtocol>;\n\n/**\n * Do not use this classe.\n * Used for angular DI\n * Create a class that implemement this one\n */\nexport class ProtocolFactoryService {\n constructor() {}\n\n public create(meta: ProtocolMeta): ComProtocol | Promise<ComProtocol> {\n throw new Error(`Unsupported protocol type \"${meta.type}\"`);\n }\n\n public isValid(meta: ProtocolMeta): boolean {\n return false;\n }\n}\n","import { NgZone } from '@angular/core';\nimport { Observable, OperatorFunction } from 'rxjs';\n\nexport function runInZone<T>(zone: NgZone): OperatorFunction<T, T> {\n return (source) => {\n return new Observable((observer) => {\n const onNext = (value: T) => zone.run(() => observer.next(value));\n const onError = (e: any) => zone.run(() => observer.error(e));\n const onComplete = () => zone.run(() => observer.complete());\n return source.subscribe(onNext, onError, onComplete);\n });\n };\n}\n","export class LibError {\n public static missingFromField(fieldKey: string) {\n return new Error(\n `InternalError: form must have a field with name \"${fieldKey}\"`\n );\n }\n\n public static componentArgumentRequired(component: string, input: string) {\n return new Error(`InternalError: ${component} requires input \"${input}\"`);\n }\n\n public static abstractVariableRequired() {\n return new Error('InternalError: AbstractVariable cannot be undefined');\n }\n}\n","import { AbstractControl, FormGroup } from '@angular/forms';\nimport { LibError } from './errors';\nimport { AbstractVariable } from '@iotize/tap/data';\n\nexport function createBleName(appName: string, serialNumber: string) {\n return appName + '_' + serialNumber.substr(serialNumber.length - 5);\n}\n\nexport function getFormFieldOrError(\n form: FormGroup,\n key: string\n): AbstractControl {\n const field = form.get(key);\n if (!field) {\n throw LibError.missingFromField(key);\n }\n return field;\n}\n\nexport function getAbstractVariableOrError(\n variable: AbstractVariable<any> | undefined\n): AbstractVariable<any> {\n if (!variable) {\n throw LibError.abstractVariableRequired();\n } else {\n return variable;\n }\n}\n","import { Injectable, NgZone } from '@angular/core';\nimport { NfcTag } from '@awesome-cordova-plugins/nfc/ngx';\nimport { Platform, ToastController } from '@ionic/angular';\nimport { CodeError, isCodeError } from '@iotize/common/error';\nimport { NdefTag, parseTapNdefMessage } from '@iotize/device-com-nfc.cordova';\nimport {\n HostProtocol,\n Tap,\n TapError,\n TapResponse,\n TapResponseStatusError,\n} from '@iotize/tap';\nimport {\n INITIAL_SESSION_STATE,\n TapAuth,\n _TAP_EXTENSION_AUTH_,\n} from '@iotize/tap/auth';\nimport {\n ExecutionContext,\n RequestHandler,\n ResultCode,\n TapClientInterface,\n} from '@iotize/tap/client/api';\nimport { TapClient, TapRequestHelper } from '@iotize/tap/client/impl';\nimport { TapConfiguratorConfig } from '@iotize/tap/config/schema/v1';\nimport { _TAP_EXTENSION_DATA_ } from '@iotize/tap/ext/data';\nimport { _TAP_EXTENSION_DATA_LOG_ } from '@iotize/tap/ext/data-log';\nimport { factoryReset } from '@iotize/tap/ext/factory-reset';\nimport { _TAP_EXTENSION_KEEP_ALIVE_ } from '@iotize/tap/ext/keep-alive';\nimport { KeepAliveEngine } from '@iotize/tap/keep-alive';\nimport {\n ComProtocol,\n ConnectionState,\n ConnectionStateChangeEvent,\n} from '@iotize/tap/protocol/api';\nimport {\n ProtocolMaxFrameSize,\n _TAP_SERVICE_ALL_EXTENSIONS_,\n} from '@iotize/tap/service/all';\nimport {\n BehaviorSubject,\n Observable,\n OperatorFunction,\n Subject,\n of,\n} from 'rxjs';\nimport {\n distinctUntilChanged,\n filter,\n shareReplay,\n skip,\n startWith,\n switchMap,\n tap,\n} from 'rxjs/operators';\nimport { OnTapEvent } from './definitions';\nimport './extensions';\nimport { DataManagerIonic } from './extensions/data-manager';\nimport { ProtocolMeta, ProtocolMetaNfc } from './extensions/protocol-info';\nimport { debug } from './logger';\nimport { isSameTag } from './nfc/utility';\nimport { ProtocolFactoryService } from './protocol-factory.service';\nimport { runInZone } from './rx-utility/run-in-zone';\nimport { TapSelectedEvent } from './tap-connect/api';\nimport { createBleName } from './utility';\n\nconst TAG = 'CurrentDeviceService';\n\nexport function LONG_RANGE_PROTOCOL_FILTER(meta: ProtocolMeta) {\n return meta.type !== 'nfc';\n}\n\nfunction getProtocolOrUndefined(\n client: TapClientInterface\n): ComProtocol | undefined {\n try {\n return client.getCurrentProtocol();\n } catch (err) {\n return undefined;\n }\n}\n\nexport class TapServiceError extends Error {\n static illegalArgument(msg: string) {\n throw new Error(`Illegal argument: ${msg}`);\n }\n public static illegalStateNoTap() {\n return new TapServiceError('Illegal state: Tap is not set yet');\n }\n}\n\n@Injectable({\n providedIn: 'root',\n})\nexport class CurrentDeviceService {\n /**\n * Hack to prevent angular treeshaking from removing loaded extension.\n */\n private _loadedTapExtensions = [\n _TAP_EXTENSION_DATA_,\n _TAP_EXTENSION_DATA_LOG_,\n _TAP_EXTENSION_KEEP_ALIVE_,\n _TAP_SERVICE_ALL_EXTENSIONS_,\n _TAP_EXTENSION_AUTH_,\n factoryReset,\n ];\n\n private _tapOrUndefined = new BehaviorSubject<undefined | Tap>(undefined);\n\n private _tap?: Tap;\n\n /**\n * Only connection lost event\n */\n private _connectionLost$ = new Subject<ConnectionStateChangeEvent>();\n\n private _sessionState$ = this._tapOrUndefined.pipe(\n switchMap((tapDeviceOrUndefined) => {\n if (!tapDeviceOrUndefined) {\n return of(INITIAL_SESSION_STATE);\n } else {\n return tapDeviceOrUndefined.auth.sessionState;\n }\n })\n );\n\n listeners: OnTapEvent[] = [];\n\n keepAlivePeriod: number = 10 * 1000;\n\n public meta: {\n isClientConfigured?: boolean;\n // config?: IotizeConfigModel,\n // tapConfig?: TapConfiguratorConfig\n } = {};\n\n /**\n * @deprecated\n */\n private _dataManager?: DataManagerIonic;\n\n /**\n * @deprecated\n */\n private _tapConfig$ = new BehaviorSubject<TapConfiguratorConfig | undefined>(\n undefined\n );\n\n private _maxFrameSizeCache: Record<string, ProtocolMaxFrameSize> = {};\n\n /**\n * Event trigger when currrent Tap changed (set or unset)\n * If tap is removed, value will be undefined\n * Immediatly triggered the current value when subscribing\n */\n public tapOrUndefinedChangedWithLastValue: Observable<Tap | undefined> =\n this._tapOrUndefined.pipe(distinctUntilChanged());\n /**\n * Event trigger when currrent Tap changed (set or unset)\n * If tap is removed, value will be undefined\n */\n public tapOrUndefinedChanged: Observable<Tap | undefined> =\n this.tapOrUndefinedChangedWithLastValue.pipe(skip(1));\n /**\n * Event trigger when a new Tap is selected (no event when tap is removed)\n */\n public tapChanged: Observable<Tap> = this.tapOrUndefinedChanged.pipe(\n filter<Tap>((tap) => !!tap) as OperatorFunction<Tap | undefined, Tap>\n );\n /**\n * Event trigger when a new Tap is selected (no event when tap is removed)\n * Immediatly triggered the current value when subscribing\n */\n public tapChangedWithLastValue: Observable<Tap> =\n this.tapOrUndefinedChangedWithLastValue.pipe(\n filter<Tap>((tap) => !!tap) as OperatorFunction<Tap | undefined, Tap>\n );\n /**\n * Event triggered when tap is removed\n */\n public tapRemoved = new Subject<Tap>();\n\n private _protocolMeta = new BehaviorSubject<ProtocolMeta | undefined>(\n undefined\n );\n private _availableProtocols = new BehaviorSubject<ProtocolMeta[]>([]);\n _isManualDisconnection = false;\n\n private _sessionStateSnapshot: TapAuth.SessionState = INITIAL_SESSION_STATE;\n\n get sessionStateSnapshot() {\n return this._sessionStateSnapshot;\n }\n\n get sessionState(): Observable<TapAuth.SessionState> {\n return this._sessionState$;\n }\n\n public get protocolMeta$(): Observable<ProtocolMeta | undefined> {\n return this._protocolMeta.asObservable();\n }\n\n public get availableProtocols$(): Observable<ProtocolMeta[]> {\n return this._availableProtocols.asObservable();\n }\n\n public get protocolMeta(): ProtocolMeta | undefined {\n return this._protocolMeta.value;\n }\n\n public set protocolMeta(meta: ProtocolMeta | undefined) {\n if (meta) {\n this.addProtocolMeta(meta);\n }\n debug(TAG, 'setting protocol meta', meta);\n this._protocolMeta.next(meta);\n }\n\n public get connectionLost(): Observable<any> {\n return this._connectionLost$.asObservable();\n }\n\n public get availableProtocols(): ProtocolMeta[] {\n return this._availableProtocols.value;\n }\n\n public get tap(): Tap {\n if (!this._tap) {\n throw new Error('Connect to a device first');\n // throw TapServiceError.illegalStateNoTap();\n }\n return this._tap;\n }\n\n public get tapOrUndefined(): Tap | undefined {\n return this._tap;\n }\n\n public get hasTap(): boolean {\n return this._tap !== undefined;\n }\n\n public set tapConfig(schema: TapConfiguratorConfig | undefined) {\n debug(TAG, 'Set Tap config', schema);\n this.dataManager = new DataManagerIonic(this.tap);\n this._tapConfig$.next(schema);\n\n if (schema) {\n if (schema.config?.data) {\n // this.tap.data.clear();\n this.tap.data.configureWithDataConfig(schema.config.data);\n /*\n this.tap.data.values.subscribe(v => {\n\n })\n this.tap.data.monitoring.asSubject()\n .subscribe(state => {\n \n })\n */\n } else {\n this.tap.data.clear();\n }\n //\n // this.dataLogger.converter = new DataLogPacketConverter(\n // newBundleConfigToOldBundleConfig(schema.config.data.bundles)\n // );\n\n // debug(TAG, 'Updating datalog converter');\n // this.dataLogger.converter = DataLogPacketConverter.createFromManager(this.tap.bundles, this.tap.variables);\n // if (schema.config.data) {\n // this.dataLogger.converter = new DataLogPacketConverter(\n // newBundleConfigToOldBundleConfig(schema.config.data.bundles || [])\n // );\n // }\n }\n }\n\n public get tapConfig(): TapConfiguratorConfig | undefined {\n return this._tapConfig$.value;\n }\n\n public get tapConfig$(): Observable<TapConfiguratorConfig | undefined> {\n return this._tapConfig$;\n }\n\n public isSameTag(tag: NdefTag | NfcTag): boolean {\n if (this.protocolMeta && this.protocolMeta.type === 'nfc') {\n // Check is it's the same tag\n const currentTag = this.protocolMeta.info.tag;\n if (currentTag.id && currentTag.id?.length == 0) {\n return false;\n }\n return isSameTag(currentTag, tag);\n } else {\n // It's not nfc currently\n const nfcProtocolMeta: ProtocolMetaNfc | undefined =\n this.availableProtocols.find(\n (p) => p.type === 'nfc'\n ) as ProtocolMetaNfc;\n if (nfcProtocolMeta) {\n return isSameTag(nfcProtocolMeta.info.tag, tag);\n }\n }\n return false;\n }\n\n /**\n * Use another communicaiton protocol\n * May be rejected\n * @param meta: ProtocolMeta\n * @param disonnectCurrentProtocol if set to true and if tap is already connected with\n * a communication protocol, it will disconnect from it first\n * @param connectToNew: boolean\n */\n public async useProtocol(\n meta: ProtocolMeta,\n disonnectCurrentProtocol: boolean = true,\n connectToNew: boolean = true\n ): Promise<void> {\n debug(TAG, 'use protocol', meta);\n const protocol = await this.protocolFactory.create(meta);\n if (!this._tap) {\n this.tap = Tap.fromProtocol(protocol);\n } else {\n // let oldConnectionState = this._tap.protocol.getConnectionState();\n if (disonnectCurrentProtocol) {\n try {\n await this.tap.protocol.disconnect().toPromise();\n } catch (err) {\n console.warn('Cannot disconnect current protocol properly: ', err);\n }\n }\n this._tap.useComProtocol(protocol);\n }\n this.protocolMeta = meta;\n if (connectToNew) {\n await this.connect();\n }\n }\n\n async executeFactoryReset() {\n await this.tap.factoryReset();\n this.tap.auth.clearCache();\n this.tap.encryption.stop();\n }\n\n public set tap(t: Tap) {\n this.setTap(t, { emit: true });\n }\n\n constructor(\n private platform: Platform,\n private toastCtrl: ToastController,\n protected protocolFactory: ProtocolFactoryService,\n private ngZone: NgZone\n ) {\n debug(TAG, 'NEW INSTANCE', this._loadedTapExtensions);\n this.tapChangedWithLastValue.subscribe((newTap) => {\n newTap.client.addInterceptor(\n (context: ExecutionContext, next: RequestHandler) => {\n return next.handle(context).pipe(\n tap(\n (tapResponseFrame) => {\n const response = new TapResponse(\n tapResponseFrame,\n context.request\n );\n if (response.status === ResultCode.UNAUTHORIZED) {\n if (\n TapRequestHelper.pathToString(\n context.request.header.path\n ) !== this.tap.service.interface.resources.login.path\n ) {\n this.listeners.forEach((listener) => {\n if (listener.onTapRequestUnauthorized) {\n listener.onTapRequestUnauthorized({\n request: context,\n response: response,\n });\n }\n });\n }\n }\n\n if (!response.isSuccessful()) {\n this.listeners.forEach((listener) => {\n if (listener.onTapRequestError) {\n listener.onTapRequestError({\n request: context.request,\n error: new TapResponseStatusError(response),\n response: response,\n });\n }\n });\n }\n },\n (error) => {\n this.listeners.forEach((listener) => {\n console.warn(`Request ${context.request} errored: ${error}`);\n if (listener.onTapRequestError) {\n listener.onTapRequestError({\n request: context.request,\n error: error,\n });\n }\n });\n }\n )\n );\n }\n );\n });\n this.sessionState.subscribe((newSessionState) => {\n this._sessionStateSnapshot = newSessionState;\n });\n this.connectionStateReplay.subscribe(async (event) => {\n if (event.newState === ConnectionState.DISCONNECTED) {\n this.keepAliveEngine?.stop();\n if (!this._isManualDisconnection) {\n this._connectionLost$?.next(event);\n }\n } else if (event.newState == ConnectionState.CONNECTED) {\n this.ngZone.runOutsideAngular(() => {\n if (this.keepAlivePeriod > 0) {\n debug(TAG, 'Start keep alive with period', this.keepAlivePeriod);\n this.keepAliveEngine?.start();\n } else {\n debug(TAG, 'NO keep alive');\n }\n });\n }\n this.listeners.forEach((listener) =>\n listener.onTapConnectionStateChange(event)\n );\n });\n }\n\n addProtocolMeta(meta: ProtocolMeta) {\n if (meta) {\n const protocols = this.availableProtocols;\n const protocolIndex = protocols.findIndex((existing) => {\n return existing.type === meta.type;\n });\n if (protocolIndex >= 0) {\n debug(TAG, 'Protocol meta', meta, 'already exists. Replacing infos');\n protocols[protocolIndex] = meta;\n this._availableProtocols.next(protocols);\n } else {\n debug(TAG, 'Adding protocol meta', meta);\n protocols.push(meta);\n this._availableProtocols.next(protocols);\n }\n }\n }\n\n /**\n * Parse NDefTag and try to create a ProtocolMeta thanks to record\n * @returns undefined if there is no other protocol in the tag\n */\n async registerProtocolsFromTag(\n tag: NdefTag | NfcTag\n ): Promise<ProtocolMeta | undefined> {\n let meta: ProtocolMeta | undefined;\n if (!tag.ndefMessage) {\n return undefined;\n }\n const info = parseTapNdefMessage(tag.ndefMessage);\n if (info.macAddress && info.macAddress !== '00:00:00:00:00:00') {\n meta = {\n type: 'ble',\n info: {\n mac: info.macAddress,\n name: info.name,\n },\n };\n } else if (info.ssid) {\n const hostname = (await this.tap.service.wifi.getHostname()).body();\n meta = {\n type: 'wifi',\n info: {\n ssid: info.ssid,\n // name: info.name,\n url: `tcp://${hostname}:2000`,\n },\n };\n }\n if (meta) {\n this.addProtocolMeta(meta);\n }\n return meta;\n }\n\n async getCurrentHostProtocolMaxFrameSizeCacheFirst(): Promise<ProtocolMaxFrameSize> {\n const protocol = this.protocolMeta?.type;\n if (!protocol || !this._maxFrameSizeCache[protocol]) {\n const hostProtocol = (\n await this.tap.service.interface.getCurrentHostProtocolMaxFrameSize()\n ).body();\n if (hostProtocol.request > 0xff) {\n hostProtocol.request -= 2; // due to 2 more bytes for apdu.header.lc\n }\n if (protocol) {\n this._maxFrameSizeCache[protocol] = hostProtocol;\n } else {\n return hostProtocol;\n }\n }\n return this._maxFrameSizeCache[protocol];\n }\n\n /**\n * Will register available communication protocols on current tap\n * by asking LWM2M resources.\n * @returns the list of new ProtocolMeta found\n */\n async registerProtocolsFromTap(): Promise<ProtocolMeta[]> {\n const nProtocolMeta: ProtocolMeta[] = [];\n\n const [\n appNameResponse,\n serialNumberResponse,\n authorizedHostProtocolsResponse,\n bleAddressResponse,\n ipResponse,\n ] = await this.tap.service.interface.executeMultipleCalls([\n this.tap.service.interface.getAppNameCall(),\n this.tap.service.device.getSerialNumberCall(),\n this.tap.service.interface.getAuthorizedHostProtocolCall(),\n this.tap.service.ble.getAddressCall(),\n this.tap.service.wifi.getIpCall(),\n ]);\n\n const authorizedHostProtocols: HostProtocol[] =\n authorizedHostProtocolsResponse.body();\n\n if (\n this.protocolMeta?.type !== 'ble' &&\n authorizedHostProtocols.includes(HostProtocol.BLE)\n ) {\n if (this.platform.is('ios')) {\n const bleName = createBleName(\n appNameResponse.body(),\n serialNumberResponse.body()\n );\n nProtocolMeta.push({\n type: 'ble',\n info: {\n name: bleName,\n },\n });\n } else {\n nProtocolMeta.push({\n type: 'ble',\n info: {\n mac: bleAddressResponse.body(),\n },\n });\n }\n }\n\n if (\n this.protocolMeta?.type !== 'socket' &&\n this.platform.is('mobile') &&\n authorizedHostProtocols.includes(HostProtocol.WIFI)\n ) {\n const ip = ipResponse.body();\n if (ip && ip !== '0.0.0.0') {\n nProtocolMeta.push({\n type: 'socket',\n info: {\n url: 'tcp://' + ip + ':2000',\n },\n });\n }\n }\n\n nProtocolMeta.forEach((meta) => this.addProtocolMeta(meta));\n\n return nProtocolMeta;\n }\n\n async useProtocolFromMeta(\n newProtocolMeta: ProtocolMeta,\n disonnectCurrentProtocol: boolean = true,\n connectToNew: boolean = true\n ): Promise<void> {\n if (!this.protocolMeta || newProtocolMeta.type !== this.protocolMeta.type) {\n return await this.useProtocol(\n newProtocolMeta,\n disonnectCurrentProtocol,\n connectToNew\n );\n }\n }\n\n /**\n * Switch from NFC communication protocol to a long range communication\n * @warning this is only available when we are in NFC\n *\n * @return the new protocol meta used or undefined if it does not have a long range protocol to use\n */\n async useLongRangeProtocol(): Promise<ProtocolMeta | undefined> {\n debug(TAG, 'useLongRangeProtocol');\n const currentProtocol = this.protocolMeta;\n if (!this._tap) {\n throw TapServiceError.illegalStateNoTap();\n }\n if (currentProtocol) {\n if (currentProtocol.type === 'nfc') {\n const protocolMeta = this.availableProtocols.find(\n LONG_RANGE_PROTOCOL_FILTER\n );\n if (protocolMeta) {\n debug(TAG, 'Using long range protocol: ', protocolMeta);\n await this.useProtocolFromMeta(protocolMeta);\n return protocolMeta;\n } else {\n debug(\n TAG,\n 'NFC tag does not have long range protocol information...'\n );\n }\n }\n }\n return undefined;\n }\n\n /**\n * Connection state events.\n