Skip to content

Commit a320f2e

Browse files
committed
Add support for updated response to CURRENT_APP command for HDMI inputs
1 parent 76e2810 commit a320f2e

File tree

5 files changed

+105
-3
lines changed

5 files changed

+105
-3
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10+
### Added
11+
12+
- Added `getCurrentAppDetails` with additional information returned for HDMI
13+
inputs.
14+
15+
### Fixed
16+
17+
- Updated `getCurrentApp` to work for HDMI inputs on newer webOS versions that
18+
return additional information.
19+
1020
## 4.3.0 - 2024-09-20
1121

1222
## 4.2.0 - 2024-01-03

packages/lgtv-ip-control/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,17 @@ responding.
124124
const currentApp = await lgtv.getCurrentApp();
125125
```
126126

127+
### `.getCurrentAppDetails(): Promise<AppDetails | null>`
128+
129+
Gets the current app details as an object. Might include the current app, HDCP
130+
status, HDCP version, etc. Please note that `signal` seems to always be `true`,
131+
even if no device is connected. Might return `null` if the TV is powered off but
132+
still responding.
133+
134+
```js
135+
const currentAppDetails = await lgtv.getCurrentAppDetails();
136+
```
137+
127138
### `.getCurrentVolume(): Promise<number>`
128139

129140
Gets the current volume as an integer from `0` to `100`.

packages/lgtv-ip-control/src/classes/LGTV.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
PowerStates,
1111
ScreenMuteModes,
1212
} from '../constants/TV.js';
13+
import { AppDetails } from '../types.js';
1314
import { LGEncoder, LGEncryption } from './LGEncryption.js';
1415
import { TimeoutError, TinySocket } from './TinySocket.js';
1516

@@ -58,15 +59,31 @@ export class LGTV {
5859
}
5960

6061
async getCurrentApp(): Promise<Apps | string | null> {
62+
const appDetails = await this.getCurrentAppDetails();
63+
return appDetails?.app ?? null;
64+
}
65+
66+
async getCurrentAppDetails(): Promise<AppDetails | null> {
6167
const response = await this.sendCommand('CURRENT_APP');
6268
if (response === '') {
6369
return null;
6470
}
65-
const match = response.match(/^APP:([\w.]+)$/);
66-
if (!match) {
71+
72+
const pairs: Record<string, string> = {};
73+
for (const match of response.matchAll(/([\w\s]+):(\S+)/g)) {
74+
pairs[match[1].trim()] = match[2];
75+
}
76+
if (!pairs['APP']) {
6777
throw new ResponseParseError(`failed to parse response: ${response}`);
6878
}
69-
return match[1];
79+
80+
return {
81+
app: pairs['APP'],
82+
hotPlug: pairs['Hot plug'],
83+
signal: pairs['Signal'] ? pairs['Signal'] === 'Yes' : undefined,
84+
hdcpVersion: pairs['HDCP'],
85+
hdcpStatus: pairs['HDCP Status'],
86+
};
7087
}
7188

7289
async getCurrentVolume(): Promise<number> {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Apps } from './constants/TV.js';
2+
3+
export type AppDetails = {
4+
app: Apps | string | undefined;
5+
hdcpStatus: string | undefined;
6+
hdcpVersion: string | undefined;
7+
hotPlug: string | undefined;
8+
signal: boolean | undefined;
9+
};

packages/lgtv-ip-control/test/LGTV.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,16 @@ describe.each([
128128
{ response: 'APP:youtube.leanback.v4', expected: Apps.youtube },
129129
{ response: 'APP:com.hbo.hbomax', expected: Apps.hbomax },
130130
{ response: 'APP:netflix', expected: Apps.netflix },
131+
{
132+
response:
133+
'APP:com.webos.app.hdmi1 Hot plug:Connected Signal:Yes HDCP:1.4 HDCP Status:None',
134+
expected: 'com.webos.app.hdmi1',
135+
},
136+
{
137+
response:
138+
'APP:com.webos.app.hdmi4 Hot plug:Connected Signal:Yes HDCP:2.2 HDCP Status:Authenticated',
139+
expected: 'com.webos.app.hdmi4',
140+
},
131141
{ response: 'APP:unsupported', expected: 'unsupported' },
132142
])('gets current app: $response', async ({ response, expected }) => {
133143
const mocking = mockResponse('CURRENT_APP', response);
@@ -137,6 +147,51 @@ describe.each([
137147
await expect(actual).resolves.toBe(expected);
138148
});
139149

150+
it.each([
151+
{ response: '', expected: null },
152+
{
153+
response:
154+
'APP:com.webos.app.hdmi1 Hot plug:Connected Signal:Yes HDCP:1.4 HDCP Status:None',
155+
expected: {
156+
app: 'com.webos.app.hdmi1',
157+
hotPlug: 'Connected',
158+
signal: true,
159+
hdcpVersion: '1.4',
160+
hdcpStatus: 'None',
161+
},
162+
},
163+
{
164+
response:
165+
'APP:com.webos.app.hdmi4 Hot plug:Connected Signal:Yes HDCP:2.2 HDCP Status:Authenticated',
166+
expected: {
167+
app: 'com.webos.app.hdmi4',
168+
hotPlug: 'Connected',
169+
signal: true,
170+
hdcpVersion: '2.2',
171+
hdcpStatus: 'Authenticated',
172+
},
173+
},
174+
{
175+
response: 'APP:unsupported',
176+
expected: {
177+
app: 'unsupported',
178+
hotPlug: undefined,
179+
signal: undefined,
180+
hdcpVersion: undefined,
181+
hdcpStatus: undefined,
182+
},
183+
},
184+
])(
185+
'gets current app details: $response',
186+
async ({ response, expected }) => {
187+
const mocking = mockResponse('CURRENT_APP', response);
188+
await testTV.connect();
189+
const actual = testTV.getCurrentAppDetails();
190+
await expect(mocking).resolves.not.toThrow();
191+
await expect(actual).resolves.toEqual(expected);
192+
},
193+
);
194+
140195
it.each([
141196
{ response: 'VOL:0', expected: 0 },
142197
{ response: 'VOL:43', expected: 43 },

0 commit comments

Comments
 (0)