Skip to content

Commit

Permalink
Add getPowerState to LGTV to check the TV power state
Browse files Browse the repository at this point in the history
  • Loading branch information
WesSouza committed Jan 2, 2024
1 parent fc9d1b1 commit bd44d27
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 2 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Added

- New `getPowerState` function on `LGTV` allowing testing if the TV is on, off,
or in an unknown state.

## 4.1.1 - 2023-12-31

### Fixed
Expand Down
21 changes: 21 additions & 0 deletions packages/lgtv-ip-control/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,19 @@ Gets the mute state.
const muteState = await lgtv.getMuteState();
```

### `.getPowerState(): Promise<PowerStates>`

Gets the current TV power state.

Because the TV might be offline, you should call this function before calling
`.connect()`, otherwise you can get a `TimeoutError`.

```js
const powerState = await lgtv.getPowerState();
```

See [`PowerStates`](#PowerStates) for available states.

### `.powerOff(): Promise<void>`

Powers the TV off.
Expand Down Expand Up @@ -319,6 +332,14 @@ See [`ScreenMuteModes`](#ScreenMuteModes) for available modes.
| volumeUp | Volume Up |
| yellowButton | Yellow Button |

### PowerStates

| Key | State |
| ------- | -------------------------------------------- |
| on | The TV is on and responding to connections |
| off | The TV is off or powering off |
| unknown | The state of the TV is unknown, possibly off |

### ScreenMuteModes

| Key | Effect |
Expand Down
27 changes: 26 additions & 1 deletion packages/lgtv-ip-control/src/classes/LGTV.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import {
Inputs,
Keys,
PictureModes,
PowerStates,
ScreenMuteModes,
} from '../constants/TV.js';
import { LGEncoder, LGEncryption } from './LGEncryption.js';
import { TinySocket } from './TinySocket.js';
import { TimeoutError, TinySocket } from './TinySocket.js';

export class ResponseParseError extends Error {}

Expand Down Expand Up @@ -101,6 +102,30 @@ export class LGTV {
return false;
}

async getPowerState(): Promise<PowerStates> {
const testPowerState = async () => {
const currentApp = await this.getCurrentApp();
return currentApp === null ? PowerStates.off : PowerStates.on;
};

if (this.connected) {
return testPowerState();
}

try {
await this.connect();
return await testPowerState();
} catch (error) {
if (error instanceof TimeoutError) {
return PowerStates.unknown;
} else {
throw error;
}
} finally {
this.disconnect();
}
}

async powerOff(): Promise<void> {
throwIfNotOK(await this.sendCommand(`POWER off`));
}
Expand Down
6 changes: 6 additions & 0 deletions packages/lgtv-ip-control/src/constants/TV.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ export enum PictureModes {
vivid = 'vivid',
}

export enum PowerStates {
on = 'on',
off = 'off',
unknown = 'unknown',
}

export enum ScreenMuteModes {
screenMuteOn = 'screenmuteon',
videoMuteOn = 'videomuteon',
Expand Down
35 changes: 34 additions & 1 deletion packages/lgtv-ip-control/test/LGTV.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import {
Inputs,
Keys,
PictureModes,
PowerStates,
ScreenMuteModes,
} from '../src/constants/TV.js';

const NULL_IP = '0.0.0.0';
const CRYPT_KEY = 'M9N0AZ62';
const MAC = 'DA:0A:0F:E1:60:CB';

Expand Down Expand Up @@ -77,7 +79,11 @@ describe.each([
mockServerSocket = socket;
}).listen();
const port = (<AddressInfo>mockServer.address()).port;
testSettings = { ...DefaultSettings, networkPort: port };
testSettings = {
...DefaultSettings,
networkPort: port,
networkTimeout: 50,
};
testTV = new LGTV(address, MAC, crypt ? CRYPT_KEY : null, testSettings);
});

Expand Down Expand Up @@ -179,6 +185,33 @@ describe.each([
}
});

it.each([{ powerState: PowerStates.on }, { powerState: PowerStates.off }])(
'gets the TV power state when connected: $powerState',
async ({ powerState }) => {
const mocking = mockResponse(
'CURRENT_APP',
powerState === PowerStates.on ? 'APP:ANYTHING' : '',
);
await testTV.connect();
const actual = testTV.getPowerState();
await expect(mocking).resolves.not.toThrow();
await expect(actual).resolves.toBe(powerState);
},
);

it('gets "unknown" TV power state when disconnected', async () => {
const offlineTV = new LGTV(
NULL_IP,
MAC,
crypt ? CRYPT_KEY : null,
testSettings,
);

await expect(offlineTV.getPowerState()).resolves.toBe(
PowerStates.unknown,
);
});

it.each([
{ response: 'OK', error: false },
{ response: 'FOO', error: true },
Expand Down

0 comments on commit bd44d27

Please sign in to comment.