Skip to content

Commit 35c314e

Browse files
committed
Fix the use of custom display configuration files
Axios when running within Node.js doesn't support the file:// protocol. This changes it so that it explicitly uses Node.js's `fs` package to load local files if `file://` is detected in the URI. This also fixes the fact that the custom display configuration was not being loaded on save, only on startup of the plugin. It also fixes it so that if you remove a custom display configuration it will fall back to the default configuration without needing to restart the plugin. This fixes #342
1 parent c91adb3 commit 35c314e

6 files changed

Lines changed: 43 additions & 5 deletions

File tree

src/actions/genericEntityAction.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ export class GenericEntityAction extends SingletonAction<Settings> {
329329

330330
private async onReconnect(ev: ReconnectEvent) {
331331
await this.homeAssistant.connect(ev.serverUrl, ev.accessToken)
332+
await this.entityConfigFactory.setDisplayConfigurationUrl(ev.customDisplayConfigurationUrl)
332333
await this.sendConnectionState()
333334
}
334335

src/models/events/sendToPluginEvents.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export type ReconnectEvent = SendToPluginBaseEvent & {
1414
event: 'reconnect'
1515
serverUrl: string
1616
accessToken: string
17+
customDisplayConfigurationUrl: Nullable<string>
1718
}
1819

1920
export type GetEntityEvent = SendToPluginBaseEvent & {

src/render/entityConfigFactoryNg.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import fs from 'fs'
2+
13
import streamDeck, { type FeedbackPayload } from '@elgato/streamdeck'
24
import axios from 'axios'
35
import type { HassEntity } from 'home-assistant-js-websocket'
@@ -27,12 +29,20 @@ export class EntityConfigFactory {
2729
}
2830

2931
async setDisplayConfigurationUrl(url: Nullable<string>) {
30-
if (!url) return
32+
if (!url) {
33+
this.displayConfiguration = defaultDisplayConfiguration as DisplayConfig
34+
return
35+
}
3136

3237
streamDeck.logger.info(`Loading display configuration from ${url}`)
3338
try {
34-
const response = await axios.get<string>(url)
35-
this.displayConfiguration = yaml.load(response.data) as DisplayConfig
39+
let response: string
40+
if (url.startsWith('file://')) {
41+
response = fs.readFileSync(url.replace('file://', ''), 'utf8')
42+
} else {
43+
response = (await axios.get<string>(url)).data
44+
}
45+
this.displayConfiguration = yaml.load(response) as DisplayConfig
3646
} catch (error) {
3747
streamDeck.logger.error(`Failed to download display configuration from ${url}`, error)
3848
}
@@ -96,7 +106,12 @@ export class EntityConfigFactory {
96106

97107
const feedbackLayout = this.render(feedbackLayoutString, stateObject)
98108
const renderedFeedback = this.render(feedbackValueString, stateObject)
99-
const feedback = renderedFeedback ? (JSON.parse(renderedFeedback) as FeedbackPayload) : {}
109+
let feedback: FeedbackPayload = {}
110+
try {
111+
feedback = renderedFeedback ? (JSON.parse(renderedFeedback) as FeedbackPayload) : {}
112+
} catch (error) {
113+
streamDeck.logger.error('Failed to parse feedback template', error)
114+
}
100115

101116
const icon = this.render(iconString, stateObject)
102117
const color = this.render(colorString, stateObject)

src/view/components/PiComponent.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,8 @@ async function saveGlobalSettings() {
427427
await streamDeckClient.send('sendToPlugin', {
428428
event: 'reconnect',
429429
serverUrl: serverUrl.value,
430-
accessToken: accessToken.value
430+
accessToken: accessToken.value,
431+
customDisplayConfigurationUrl: displayConfigurationUrlOverride.value
431432
})
432433
}
433434

test/render/entityConfigFactoryNg.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,21 @@ it('test setting bad custom display configuration', async () => {
152152
const renderConfig = entityConfigFactory.buildRenderConfig(entity, settings)
153153
expect(renderConfig.icon).toBe('mdi:lightbulb')
154154
})
155+
156+
it('test setting custom display configuration with file', async () => {
157+
const entityConfigFactory = new EntityConfigFactory()
158+
const entity = buildTestEntity()
159+
const settings = buildTestSettings()
160+
161+
// Set the display configuration URL to a local file
162+
await entityConfigFactory.setDisplayConfigurationUrl(
163+
'file://resources/custom-display-configuration.yml'
164+
)
165+
const renderConfig1 = entityConfigFactory.buildRenderConfig(entity, settings)
166+
expect(renderConfig1.icon).toBe('mdi:ceiling-light')
167+
168+
// Set the display configuration URL back to the default
169+
await entityConfigFactory.setDisplayConfigurationUrl('')
170+
const renderConfig2 = entityConfigFactory.buildRenderConfig(entity, settings)
171+
expect(renderConfig2.icon).toBe('mdi:lightbulb')
172+
})
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
light:
2+
icon: mdi:ceiling-light

0 commit comments

Comments
 (0)