From 6e472f645182e94a376f991bb5291c9987b4ae49 Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Mon, 18 May 2020 09:31:04 +0100 Subject: [PATCH] feat: Auto Discovery --- assets/images/loader.gif | Bin 0 -> 6096 bytes src/components/drop-down/styles.scss | 7 +- .../sub-menus/add-integration/script.ts | 29 +++++- src/i18n/en_US.json | 3 +- src/pages/integrations/script.ts | 28 +++++- src/pages/integrations/styles.scss | 42 +++++++- src/pages/integrations/template.html | 89 +++++++++++------ src/pages/software-update/script.ts | 8 +- src/server.ts | 92 ++++++++++++++---- src/store/actions.ts | 4 +- src/store/aggregates/integrations.ts | 4 +- src/store/integrations/initial-state.ts | 4 +- src/store/integrations/reducer.ts | 12 +++ src/types.ts | 51 ++++++++++ 14 files changed, 314 insertions(+), 59 deletions(-) create mode 100644 assets/images/loader.gif diff --git a/assets/images/loader.gif b/assets/images/loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..f50933b9f5b3d833465a3dfcd65a517cf3b3aaf6 GIT binary patch literal 6096 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 SpotifyAuthentication) public spotifyAuthentication: SpotifyAuthentication; + @Prop({ + type: Object, + required: false + }) + public discoveredIntegration: IDiscoveredIntegration; + public isAddingNewIntegration: boolean = this.spotifyAuthentication.isInAuthenticationCycle; public supportedIntegrations: IKeyValuePair; public integrationTypeSelected: boolean = false; @@ -55,6 +61,25 @@ export default class AddIntegration extends Vue { public newDataValue: string = ''; public selectedValue: string = this.isIntegrationTypeSelectedSpotify ? 'spotify' : ''; + public mounted() { + if (this.discoveredIntegration) { + this.isAddingNewIntegration = true; + const matchingItem = this.typeOptions.find((type) => type.value === this.discoveredIntegration.type); + + if (matchingItem) { + this.selectedValue = matchingItem.value; + this.onIntegrationTypeChanged(matchingItem); + this.propertyValues.friendly_name = this.discoveredIntegration.friendly_name; + + this.$nextTick().then(() => { + if (Object.keys(this.propertyValues).includes('ip')) { + this.propertyValues.ip = this.discoveredIntegration.ip; + } + }); + } + } + } + public onIntegrationTypeChanged(item: IDropDownItem) { const selectedIntegration = this.supportedIntegrations[item.value]; const properties = selectedIntegration.properties || {}; diff --git a/src/i18n/en_US.json b/src/i18n/en_US.json index caf0b1e..3ddf5e6 100644 --- a/src/i18n/en_US.json +++ b/src/i18n/en_US.json @@ -107,7 +107,8 @@ "configuredIntegrations": "Configured Integrations", "discoveredIntegrations": "Discovered Integrations", "addIntegration": "Add Integration", - "newIntegration": "New Integration" + "newIntegration": "New Integration", + "searchForIntegrations": "Search For Integrations On Your Network" }, "entities": { "title": "Entities Controlled By YIO Remote", diff --git a/src/pages/integrations/script.ts b/src/pages/integrations/script.ts index 1b91267..826899f 100644 --- a/src/pages/integrations/script.ts +++ b/src/pages/integrations/script.ts @@ -2,7 +2,7 @@ import Vue from 'vue'; import { Component } from 'vue-property-decorator'; import { Inject } from '../../utilities/dependency-injection'; import { YioStore } from '../../store'; -import { IIntegrationInstance, IKeyValuePair, IYioTableComponent } from '../../types'; +import { IIntegrationInstance, IKeyValuePair, IYioTableComponent, IDiscoveredIntegration } from '../../types'; import { ServerConnection } from '../../server'; import YioTable from '../../components/table/index.vue'; import ActionIconButton from '../../components/action-icon-button/index.vue'; @@ -18,7 +18,9 @@ import AddIntegration from '../../components/sub-menus/add-integration/index.vue subscriptions(this: IntegrationsPage) { return { integrations: this.store.integrations.configured$, - supportedIntegrations: this.store.integrations.supported$ + supportedIntegrations: this.store.integrations.supported$, + discoveredIntegrations: this.store.integrations.discovered$, + isSearching: this.store.select('integrations', 'isSearchingForIntegrations') }; } }) @@ -29,13 +31,33 @@ export default class IntegrationsPage extends Vue { @Inject(() => ServerConnection) public server: ServerConnection; public integrations: IKeyValuePair; + public discoveredIntegrations: IDiscoveredIntegration[]; public supportedIntegrations: IKeyValuePair; + public isSearching: boolean = false; public mounted() { this.$menu.show(AddIntegration, {}); } - public onItemSelected(index: number) { + public onSearchForIntegrations() { + if (this.isSearching) { + return; + } + + this.isSearching = true; + this.server.discoverIntegrations().then(() => this.isSearching = false); + } + + public onItemSelected(index: number, isNew: boolean) { + if (isNew) { + this.$menu.hide(); + this.$menu.show(AddIntegration, { + discoveredIntegration: this.discoveredIntegrations[index], + onCancel: this.onItemDeselected + }); + return; + } + const integration = this.integrations[index]; const schema = this.supportedIntegrations[integration.type]; diff --git a/src/pages/integrations/styles.scss b/src/pages/integrations/styles.scss index 17c7210..f6a9d5e 100644 --- a/src/pages/integrations/styles.scss +++ b/src/pages/integrations/styles.scss @@ -4,8 +4,48 @@ height: 100%; } -.title { +.configured, .discovered { + display: flex; + flex-direction: column; + height: 50%; +} + +.discovered-container { + display: flex; + flex-direction: row; +} + +.title-container { font-family: OpenSans; + position: relative; +} + +.title { + line-height: 40px; +} + +.search-container { + display: flex; + flex-direction: row; +} + +.icon-search { + cursor: pointer; +} + +.search-message { + font-size: 10px; + opacity: 0.5; + line-height: 40px; +} + +.spinner { + margin: 12px 10px; + width: 15px; + height: 15px; + background: url('../../../assets/images/loader.gif'); + background-size: contain; + background-repeat: no-repeat; } td { diff --git a/src/pages/integrations/template.html b/src/pages/integrations/template.html index d2ebb0e..0e1fe91 100644 --- a/src/pages/integrations/template.html +++ b/src/pages/integrations/template.html @@ -1,30 +1,63 @@
-

- {{$t('pages.integrations.configuredIntegrations')}} -

- - - - -

- {{$t('pages.integrations.discoveredIntegrations')}} -

+
+

+ {{$t('pages.integrations.configuredIntegrations')}} +

+ + + + +
+
+
+
+
+ {{$t('pages.integrations.discoveredIntegrations')}} +
+
+
+
+ +
{{$t('pages.integrations.searchForIntegrations')}}
+
+
+ + + + +
\ No newline at end of file diff --git a/src/pages/software-update/script.ts b/src/pages/software-update/script.ts index ab3afeb..a63dd2b 100644 --- a/src/pages/software-update/script.ts +++ b/src/pages/software-update/script.ts @@ -35,10 +35,14 @@ export default class SoftwareUpdatePage extends Vue { this.$t('pages.softwareUpdate.upToDate'); } + public updateAutoSoftwareUpdate(value: boolean) { + this.server.setAutoUpdate(value); + } + public checkForUpdate() { - this.isNewVersionAvailable = !this.isNewVersionAvailable; - console.log('TODO: API ENDPOINT NEEDED'); + this.server.checkForUpdate(); } + public update() { console.log('TODO: API ENDPOINT NEEDED'); } diff --git a/src/server.ts b/src/server.ts index c0a214b..e70b3ee 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,10 +1,10 @@ +import Vue from 'vue'; import WebSocketAsPromised from 'websocket-as-promised'; import { BehaviorSubject } from 'rxjs'; import { Guid } from 'guid-typescript'; import { Singleton, Inject } from './utilities/dependency-injection'; import { YioStore } from './store'; -import { IConfigState, IKeyValuePair, IIntegrationInstance, IEntity, IServerResponse, IServerResponseWithData, IProfile, IPage, IGroup, IIntegrationSchema, IProfileAggregate, IPageAggregate, IEntityAggregate, IGroupAggregate, ILanguageSetting } from './types'; -import Vue from 'vue'; +import { IConfigState, IKeyValuePair, IIntegrationInstance, IEntity, IServerResponse, IServerResponseWithData, IProfile, IPage, IGroup, IIntegrationSchema, IProfileAggregate, IPageAggregate, IEntityAggregate, IGroupAggregate, ILanguageSetting, IDiscoveredIntegration } from './types'; import { Localisation } from './i18n'; @Singleton @@ -23,13 +23,17 @@ export class ServerConnection { private configPollingRequestId: number; private entitiesPollingRequestId: number; private requestId: number; + private discoveryPollingRequestId: number; + private hasInitialData: boolean; constructor() { this.host = window.location.hostname; this.port = 946; this.requestId = 0; + this.hasInitialData = false; this.configPollingRequestId = 1316134911; this.entitiesPollingRequestId = 1316134910; + this.discoveryPollingRequestId = 1316134909; this.isConnected$ = new BehaviorSubject(false); this.wsp = new WebSocketAsPromised(`ws://${this.host}:${this.port}`, { packMessage: (data) => JSON.stringify(data), @@ -38,8 +42,9 @@ export class ServerConnection { extractRequestId: (data) => data && data.id }); this.wsp.onClose.addListener(() => { + this.hasInitialData = false; this.isConnected$.next(false); - this.connect(); + window.setTimeout(() => this.connect(), 3000); }); this.wsp.onError.addListener(() => this.isConnected$.next(false)); this.wsp.onResponse.addListener((response) => { @@ -47,7 +52,7 @@ export class ServerConnection { if (response.success && response.id === this.configPollingRequestId) { this.store.dispatch(this.store.actions.updateConfig(response.config)); - this.pollForData(); + this.pollForConfig(); } }); } @@ -66,7 +71,9 @@ export class ServerConnection { .then(() => this.getProfiles()) .then(() => this.getConfig(true)) .then(() => this.getAvailableEntities()) - .then(() => this.pollForData()); + .then(() => this.hasInitialData = true) + .then(() => this.pollForConfig()) + .then(() => this.pollForEntities()); } public authenticate(token: string) { @@ -116,10 +123,42 @@ export class ServerConnection { return this.sendMessage({type: 'get_available_entities'}) .then((response) => response.available_entities) .then((entities) => this.store.dispatch(this.store.actions.setAvailableEntities(entities))) + .then(() => this.pollForEntities()) .catch(() => ({})); } - public getSupportedIntegrations(): Promise < void > { + public discoverIntegrations() { + if (this.store.value.integrations.isSearchingForIntegrations) { + return Promise.resolve(); + } + + this.store.dispatch(this.store.actions.setSearchingForIntegrations(true)); + + return new Promise((resolve, reject) => { + const listener = (response: string) => { + const parsedResponse = JSON.parse(response) as IServerResponseWithData; + + if (parsedResponse.id === this.discoveryPollingRequestId) { + if (parsedResponse.message === 'discovery_done') { + this.wsp.onMessage.removeListener(listener); + resolve(); + return; + } + + if (parsedResponse.discovered_integration) { + this.store.dispatch(this.store.actions.addDiscoveredIntegration(parsedResponse.discovered_integration)); + } + } + }; + + this.wsp.onMessage.addListener(listener); + this.wsp.sendRequest({type: 'discover_integrations'}, { requestId: this.discoveryPollingRequestId }) + .catch((error) => reject(error)); + }) + .then(() => this.store.dispatch(this.store.actions.setSearchingForIntegrations(false))); + } + + public getSupportedIntegrations(): Promise { return this.sendMessage({type: 'get_supported_integrations'}) .then((response) => response.supported_integrations) .then((integrations: string[]) => { @@ -135,7 +174,7 @@ export class ServerConnection { .then((integrations) => this.store.dispatch(this.store.actions.setSupportedIntegrations(integrations))); } - public getIntegrationSchema(integration: string): Promise < IIntegrationSchema > { + public getIntegrationSchema(integration: string): Promise { return this.sendMessage({type: 'get_integration_setup_data', integration}) .then((response) => response.data); } @@ -175,6 +214,12 @@ export class ServerConnection { .catch((response) => this.showToast(response)); } + public setAutoUpdate(value: boolean) { + return this.sendMessage({ type: 'set_auto_update', value }) + .then((response) => this.showToast(response)) + .catch((response) => this.showToast(response)); + } + public addNewProfile(name: string) { const profile = { [`${Guid.create()}`]: { @@ -507,6 +552,14 @@ export class ServerConnection { .catch((response) => this.showToast(response)); } + public checkForUpdate() { + return this.sendMessage({ type: 'check_for_update' }) + .then((r) => console.log(r)) + // .then((response) => this.showToast(response)) + // .then(() => this.store.dispatch(this.store.actions.setLanguage(language))) + .catch((response) => this.showToast(response)); + } + public reboot() { return this.sendMessage({ type: 'reboot' }) .catch((response) => this.showToast(response)); @@ -517,21 +570,24 @@ export class ServerConnection { this.requestId++; return this.wsp.sendRequest(message, {requestId: this.requestId}) - .then((response: IServerResponseWithData) => { - if (!response.success) { - return Promise.reject(response); - } + .then((response: IServerResponseWithData) => { + if (!response.success) { + return Promise.reject(response); + } - if (message.type !== 'get_config') { - this.getConfig(true); - } - return response; - }); + if (message.type !== 'get_config' && this.hasInitialData) { + this.getConfig(true); + } + return response; + }); } - private pollForData() { + private pollForConfig() { window.setTimeout(() => this.getConfig(), 3000); - window.setTimeout(() => this.getAvailableEntities(), 3000); + } + + private pollForEntities() { + window.setTimeout(() => this.getAvailableEntities(), 5000); } private showToast(response: IServerResponse) { diff --git a/src/store/actions.ts b/src/store/actions.ts index bddc190..84298ee 100644 --- a/src/store/actions.ts +++ b/src/store/actions.ts @@ -1,5 +1,5 @@ import { createStandardAction, ActionType } from 'typesafe-actions'; -import { IKeyValuePair, IEntity, IConfigState, IIntegration, IIntegrationSchema, IProfile, IPage, IGroup, ILanguageSetting } from '../types'; +import { IKeyValuePair, IEntity, IConfigState, IIntegration, IIntegrationSchema, IProfile, IPage, IGroup, ILanguageSetting, IDiscoveredIntegration } from '../types'; const actions = { // Config @@ -13,6 +13,8 @@ const actions = { // Integrations setConfiguredIntegrations: createStandardAction('store/integrations/set-configured')>(), setSupportedIntegrations: createStandardAction('store/integrations/set-supported')>(), + addDiscoveredIntegration: createStandardAction('store/integrations/add-discovered')(), + setSearchingForIntegrations: createStandardAction('store/integrations/set-searching')(), // Profiles setProfiles: createStandardAction('store/profiles/set-all')>(), diff --git a/src/store/aggregates/integrations.ts b/src/store/aggregates/integrations.ts index 1bd4eea..7c8ae96 100644 --- a/src/store/aggregates/integrations.ts +++ b/src/store/aggregates/integrations.ts @@ -1,11 +1,12 @@ import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { YioStore } from '..'; -import { IIntegrationInstance, IKeyValuePair } from '../../types'; +import { IIntegrationInstance, IKeyValuePair, IDiscoveredIntegration } from '../../types'; export class IntegrationsAggregate { public configured$: Observable; public supported$: Observable>; + public discovered$: Observable; private store: YioStore; constructor(store: YioStore) { @@ -30,5 +31,6 @@ export class IntegrationsAggregate { ); this.supported$ = this.store.select('integrations', 'supported'); + this.discovered$ = this.store.select('integrations', 'discovered'); } } diff --git a/src/store/integrations/initial-state.ts b/src/store/integrations/initial-state.ts index 30f1ed1..732c736 100644 --- a/src/store/integrations/initial-state.ts +++ b/src/store/integrations/initial-state.ts @@ -2,7 +2,9 @@ import { IIntegrationsState } from '../../types'; const initialState: IIntegrationsState = { configured: {}, - supported: {} + supported: {}, + discovered: [], + isSearchingForIntegrations: false }; export default initialState; diff --git a/src/store/integrations/reducer.ts b/src/store/integrations/reducer.ts index dd15336..5c27a98 100644 --- a/src/store/integrations/reducer.ts +++ b/src/store/integrations/reducer.ts @@ -12,6 +12,18 @@ export default function reducer(state: IIntegrationsState = initialState, action configured: action.payload.integrations } }; + case getType(actions.addDiscoveredIntegration): + const alreadyAdded = !!state.discovered.find((existing) => JSON.stringify(existing) === JSON.stringify(action.payload)); + + return { + ...state, + discovered: [...state.discovered, ...(alreadyAdded ? [] : [action.payload])] + }; + case getType(actions.setSearchingForIntegrations): + return { + ...state, + isSearchingForIntegrations: action.payload + }; case getType(actions.setSupportedIntegrations): return { ...state, diff --git a/src/types.ts b/src/types.ts index f77aef7..7d633f4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -132,8 +132,50 @@ export interface IUiConfig { groups: IKeyValuePair; } +export interface IUpdateInfo { + available: boolean; + release_name: string; + version: string; + // [ + // { + // name: 'app', + // available: true, + // release_name: 'v0.3.0 foobar feature', + // version: '0.3.0', + // }, + // { + // name: 'plugin', + // homeassistant: + // { + // available: true, + // release_name: 'v0.3.0 foobar feature', + // version: '0.3.0', + // }, + // homey: + // { + // available: true, + // release_name: 'v0.3.0 foobar feature', + // version: '0.3.0', + // } + // }, + // { + // name: 'os', + // available: true, + // release_name: 'v0.3.0 foobar feature', + // version: '0.3.0', + // }, + // { + // name: 'dock', + // available: true, + // release_name: 'v0.3.0 foobar feature', + // version: '0.3.0', + // } + // ]; +} + export interface ISettingsState { languages: ILanguageSetting[]; + // updates: any; } export interface IConfigState { @@ -157,6 +199,8 @@ export interface IIntegrationSchema { export interface IIntegrationsState { configured: IKeyValuePair; supported: IKeyValuePair; + discovered: IDiscoveredIntegration[]; + isSearchingForIntegrations: boolean; } export interface IEntitiesState { @@ -277,6 +321,12 @@ export interface ILocale extends LocaleMessageObject { } // Server +export interface IDiscoveredIntegration { + friendly_name: string; + ip: string; + type: string; +} + export interface IServerResponse { type: string; success: boolean; @@ -285,6 +335,7 @@ export interface IServerResponse { } export interface IServerResponseWithData extends IServerResponse { + discovered_integration: T; config: T; languages: T; groups: T;