From 49085cc8d8f50a5e32dceee3e1c081a4f860d9ba Mon Sep 17 00:00:00 2001 From: Grzegorz Date: Sat, 18 May 2024 13:48:21 +0200 Subject: [PATCH] release 2.18.0 --- CHANGELOG.md | 247 ++++++++++++++++++++++- config.schema.json | 260 +++++++++++++++++++++++-- package.json | 2 +- sample-config.json | 20 +- src/constants.json | 5 +- src/lgwebosdevice.js | 452 ++++++++++++++++++++++++++++++++----------- src/lgwebossocket.js | 287 +++++++++++++++++++-------- src/restful.js | 12 +- 8 files changed, 1057 insertions(+), 228 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32c1e26..52d0378 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,27 +1,49 @@ # Changelog + All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +## [2.18.0] - (18.05.2024) + +## Changes + +- added support to control brightness, backlight, contrast, color, picture mode, sound mode, sound output over MQTT +- added support to anable/disable/display type for indyvidual sound and picture mode +- added sound output sensor +- added sound output control +- fixed screen off button state +- updated MQTT and RESTFul +- config schema updated +- cleanup + ## [2.17.0] - (04.03.2024) + ## Changes + - added support to subscribe MQTT and control device - config schema updated - cleanup ## [2.16.0] - (01.01.2023) + ## Changes + - added possibility to disable prefix name for buttons, sensors, picture meodes and sound modes - config schema updated - cleanup ## [2.15.0] - (29.12.2023) + ## Changes + - added possibility to select display inputs order, possible by `None`, `Alphabetically Name`, `Alphabetically Reference` - config schema updated - cleanup ## [2.14.0] - (05.11.2023) + ## Changes + - added possibility to disable load defaults inputs - fix screen on/off [#177](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/177) - fix plugin stopped responding after some times [#170](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/170) @@ -29,13 +51,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - cleanup ## [2.12.0] - (05.09.2023) + ## Changes + - added pixel refresh sensor - added picture mode sensor - fix sound mode sensor ## [2.11.0] - (29.07.2023) + ## Changes + - added RESTFul server - fixed MQTT prefix - code refactor and cleanup @@ -43,34 +69,44 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - fixed some minor issues ## [2.10.0] - (12.06.2023) + ## Changes + - decrease heartbeat time [#172](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/172) - added possibilty to disable TV services [#169](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/169) - config.schema updated - cleanup ## [2.9.1] - (27.03.2023) + ## Changes + - fixed [#165](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/165) - added Sound Mode Contact Sensor, only for webOS >= 6.0 - config.schema updated - cleanup ## [2.9.0] - (26.03.2023) + ## Changes + - added sound mode control, only for webOS >= 6.0 - config.schema updated - cleanup ## [2.8.0] - (14.02.2023) + ## Changes + - rbuild code of specjal socket client to better RC performance - config.schema updated - stability and performance improvements - cleanup ## [2.7.0] - (13.02.2023) + ## Changes + - standarize function of display type and volume control, now volume control -1 None/Disabled, 0 Slider, 1 Fan, please see in readme - removed inputs.type, not used anymore - config.schema updated @@ -79,43 +115,59 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - cleanup ## [2.6.0] - (09.02.2023) + ## Changes + - added heartbeat to keep alive sockets - logging message updated - cleanup ## [2.5.0] - (24.01.2023) + ## Changes + - enchancement [#156](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/156) - config.schema updated - cleanup ## [2.4.5] - (14.01.2023) + ## Changes + - fix sensor volume ## [2.4.4] - (14.01.2023) + ## Changes + - correct some debug info and mqtt topics - fix create ssl client ## [2.4.3] - (14.01.2023) + ## Changes + - added Channel Motion Sensor for use with automations (every Channel change report motion) - config.schema updated ## [2.4.2] - (14.01.2023) + ## Changes + - fix state update after restart - code cleanup ## [2.4.1] - (14.01.2023) + ## Changes + - added Input Motion Sensor for use with automations (every Input change report motion) - config.schema updated ## [2.4.0] - (14.01.2023) + ## Changes + - change websocket library to ws - added SSL for WebSocket, TV with new firmware - fix [#151](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/151) @@ -123,20 +175,28 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - code refactor ## [2.3.6] - (04.01.2023) + ## Changes + - fix save target visibility -- fix save custom names +- fix save custom names ## [2.3.3] - (31.12.2022) + ## Changes + - dynamic update accessory information ## [2.3.1] - (18.12.2022) + ## Changes + - fix [#146](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/146) ## [2.3.0] - (18.12.2022) + ## Changes + - enhancement [#145](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/145) - added Power Motion Sensor for use with automations - added Volume Motion Sensor for use with automations (every volume change report motion) @@ -147,265 +207,375 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - other small fixes [2.2.7] - (20.10.2022) + ## Changes + - fix client.close [2.2.6] - (27.09.2022) + ## Changes + - fix [#139](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/139) [2.2.5] - (14.09.2022) + ## Changes + - bump dependencies - fix read device model in some specific situations [2.2.4] - (10.09.2022) + ## Changes + - cleanup [2.2.3] - (04.09.2022) + ## Changes + - fix turn screen on/off for webOs <= 5 ## [2.2.2] - (03.09.2022) + ## Changes + - cleanup - fix [#138](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/138) ## [2.2.0] - (28.08.2022) + ## Changes + - cleanup - added picture control (backlight, brightness, contrast, color) - fix [#136](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/136) - fix [#109](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/109) ## [2.1.5] - (22.08.2022) + ## Changes + - fix error if apps list unknown - fix error if channels list unknown - fix screen on/off for webOs >= 5 ## [2.1.4] - (18.08.2022) + ## Changes + - fix special soccket reconnect ## [2.1.3] - (18.08.2022) + ## Changes + - fix reconnect error ## [2.1.2] - (18.08.2022) + ## Changes + - fix update device state after first pairing - performance and stability improvement - log corrections ## [2.1.1] - (14.08.2022) + ## Changes + - performance and stability improvement - rebuild debug log - prevent publish accessory if payring key not exist or removed ## [2.1.0] - (13.08.2022) + ## Changes + - rebuild power state and screen state identify - rebuild debug log ## [2.0.4] - (10.08.2022) + ## Changes + - remove outdated code - performance and stability improvements ## [2.0.3] - (10.08.2022) + ## Changes + - fix data update ## [2.0.2] - (09.08.2022) + ## Changes + - fix data update ## [2.0.0] - (08.08.2022) + ## Changes + - full code refactor - stability improvements - response improvemets ## [1.11.9] - (06.08.2022) + ## Changes + - fix [#124](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/124) ## [1.11.8] - (23.07.2022) + ## Changes + - refactor information service ## [1.11.7] - (13.06.2022) + ## Changes + - fix [#130](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/130) ## [1.11.6] - (05.05.2022) + ## Changes + - fix [#127](https://github.com/grzegorz914/homebridge-lgwebos-tv/issues/127) - more debug logging ## [1.11.5] - (25.04.2022) + ## Changes + - refactor debug and info log - refactor mqtt message ## [1.11.4] - (24.04.2022) + ## Changes + - fix PowerModeSelection - update config.schema.json ## [1.11.2] - (27.02.2022) + ## Changes + - fix Info button in RC ## [1.11.1] - (22.02.2022) + ## Added + - possibility to set custom command for Info button in RC ## [1.11.0] - (18.02.2022) + ## Added + - MQTT Client, publish all device data ## Changes + - update dependencies - code refactor ## [1.10.14] - (16.01.2022) + ### Fixed + - wrog power state report if disconnected from net ## [1.10.14] - (16.01.2022) + ### Changs + - code cleanup - update config.schema ### Fixed + - services calculation count - start input with automations/scenes ## [1.10.12/13] - (08.01.2022) + ### Changed -- rebuild device info read and write + +- rebuild device info read and write ## [1.10.11] - (03.01.2022) + ### Added -- ability to disable log device info by every connections device to the network (Advanced Section) + +- ability to disable log device info by every connections device to the network (Advanced Section) ## [1.10.10] - (29.12.2021) + - prevent load plugin if host or mac not set - prepare directory and files synchronously ## [1.10.9] - (28.12.2021) + - update node minimum requirements ## [1.10.8] - (28.12.2021) + ### Added + - Selectable display type of buttons in HomeKit app ## [1.10.7] - (27.12.2021) + ## Changes + - remove branding - fixed characteristic warning volume ## [1.10.6] - (23.12.2021) + ## Changes + - fixed RC Socket reconnect ## [1.10.5] - (19.12.2021) + ## Changes + - added possibility turn ON/OFF the screen for webOS >= 4.0 - fixed power mode selection - fixed set mute ## [1.10.0] - (18.12.2021) + ## Changes + - added RC and Volume control as a button - added correct power state report if screen is Off - stability and performance improvements ## [1.9.41] - (14.12.2021) + ## Changes -- removed refreshInterval + +- removed refreshInterval ## [1.9.39] - (13.12.2021) + ## Changes + - fixed wrong power state if tv lose connection to net - added debug mode ## [1.9.37] - (10.12.2021) + ## Changes + - code rebuild - stability and performance improvements ## [1.9.36] - (28.11.2021) + ## Changes + - code rebuild - stability and performance improvements - prepare for extends new functionality in next release ## [1.9.35] - (28.11.2021) + ## Changes + - stability improvement ## [1.9.30] - (31.10.2021) + ## Changes + - stability improvement ## [1.9.23] - (19.10.2021) + ## Changes + - code cleanup and rebuild - performance improvements - prepare for new functionality ## [1.9.19] - (05.10.2021) + ## Changes + - code cleanup ## [1.9.16] - (01.10.2021) + ## Changes + - fixed SIGBUS crash and other improvements ## [1.9.15] - (26.09.2021) + ## Changes + - config.schema update ## [1.9.14] - (24.09.2021) + ## Changes + - code cleanup - updated ES5 to ES6 - updated config.schema ## [1.9.13] - (24.09.2021) + ## Changes + - code cleanup ## [1.9.11] - (14.09.2021) + ## Changes + - code cleanup ## [1.9.10] - (06.09.2021) + ## Changes + - extend filters - updated config.schema ## [1.9.9] - (05.09.2021) + ## Changes + - extend filter possibility - updated config.schema - code cleanup ## [1.9.3] - (02.09.2021) + ## Changes + - add more filter for unnecesared inputs from inputs list if load inputs list from device ## [1.9.2] - (02.09.2021) + ## Changes + - filter unnecesared inputs from inputs list if load inputs list from device ## [1.9.0] - (31.08.2021) + ## Changes + - code refactorin - removed not nedded library - added load inputs list from device @@ -413,86 +583,123 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - many small changes and stability improvements ## [1.7.0] - (22.02.2021) + ## Changes + - code rebuild, use Characteristic.onSet/onGet - require Homebridge 1.3.x or above ## [1.6.6] - (06.01.2021) + ## Changs + - remove unused dependencies ## [1.6.3] - (20.11.2020) + ## Changs + - fixed slow response on RC control ## [1.6.0] - (29.09.2020) + ## Changs + - always check installed app - code refactoring - update config.schema ## [1.4.0] - (14.09.2020) + ## Changs + - changes in updateDeviceState ## [1.3.0] - (14.09.2020) + ## Changs + - added refreshInterval, default 5sec ## [1.1.0] - (06.09.2020) + ## Changs + - completely reconfigured layout of config schema ## [1.0.0] - (28.06.2020) + ### Added + - release version. ## [0.12.0] - (08.06.2020) + ### Added + - added possibility to switch LiveTV channels from the inputs list ### Fixed + - other fixes ## [0.11.0] - (23.05.2020) + ### Added + - added possibility to select what a type of extra volume control You want to use (None, Slider, Fan) ## [0.10.40] - (22.05.2020) + ### Fixed + - fixed RC control - other improvements ## [0.10.0] - (20.05.2020) + ### Added + - added mute ON/OFF to the slider volume ## [0.9.101] - (18.05.2020) + ### Fixed + - fixed bug in RC control - fixed power state ## [0.9.75] - (17.05.2020) + ### Fixed + - fixed switch input if start with scene or automation ## [0.9.65] - (16.05.2020) -### Fixed + +### Fixed + - fixed power state ## [0.9.10] - (14.05.2020) + ### Added + - added descriptions in config.schema.json ## [0.9.8] - (14.05.2020) + - revert back with defaults inputs - added input type to inputs - added other fixes in code to prevent app crash without configured inputs -## [0.9.0] - (14.05.2020) +## [0.9.0] - (14.05.2020) + - added Types to the inputs references (please update Yours config.json) -- do not add or remove if exist from the config.json default inputs which are now contain in the code -### [Default inputs: +- do not add or remove if exist from the config.json default inputs which are now contain in the code + +### [Default inputs + { "name": "Live TV", "reference": "com.webos.app.livetv", @@ -509,71 +716,91 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) "type": "HDMI" } -## [0.8.0] - (10.05.2020) +## [0.8.0] - (10.05.2020) + - code cleanup -## [0.7.0] - (06.05.2020) +## [0.7.0] - (06.05.2020) + - adapted to HAP-Node JS lib ## [0.5.57] - (06.05.2020) + - code cleanup ## [0.5.50] - (05.05.2020) + - fixes and performance inprovements - correted logging state ## [0.5.16] - (05.05.2020) + - added real time read and write data for (lightbulb slider volume cont ## [0.5.0] - (01.05.2020) + - added support for webOS < 2.5.0 (please update Your config.json) ## [0.4.4] - (01.05.2020) + - fixes in real time data read and write ## [0.4.0] - (30.04.2020) + - added realtime data read and write ## [0.3.4] - (27.04.2020) + - added switch ON/OFF volume control (please update config.json) ## [0.3.0] - (26.04.2020) + - add Siri volume control - add Slider] - (Brightness) volume control ## [0.2.112] - (21.04.2020) + - different fixes. ## [0.2.97] - (07.04.2020) + - fixed store of positin in HomeKit fav. ## [0.2.96] - (06.04.2020) + - name corrections in TV information files ## [0.2.95] - (05.04.2020) + - read and store appListFile from TV ## [0.2.93] - (05.04.2020) + - read and store serListFile from TV - update README.md - update sample-config.json ## [0.2.91] - (29.03.2020) + - fixes crash if no device name defined - fixed config.schema.json - fixed store file inside the Homebridge directory ## [0.2.90] - (29.03.2020) + - some small fixes ## [0.2.77] - (21.03.2020) + - corrections for homebridge git - performance improvement ## [0.2.1] - (16.02.2020) + - fixed most bugs - performance improvements - code cleanup ## [0.0.1] - (10.02.2020) + - initial release diff --git a/config.schema.json b/config.schema.json index 7b99e6a..a313d7f 100644 --- a/config.schema.json +++ b/config.schema.json @@ -38,7 +38,7 @@ "title": "Load Inputs From Device", "type": "boolean", "default": false, - "description": "This function get all available inputs direct from device, manually configured inputs will be skipped.", + "description": "This function get all available inputs and apps direct from device, manually configured inputs will be skipped.", "required": false }, "disableLoadDefaultInputs": { @@ -574,70 +574,77 @@ "title": "Power", "type": "boolean", "default": false, - "description": "This expose Power as a Motion Sensor to use with automations.", + "description": "This expose Power as a Motion Sensor to use with automations, fired if Power ON.", "required": false }, "sensorPixelRefresh": { "title": "Pixel Refresh", "type": "boolean", "default": false, - "description": "This expose Pixel Refresh as a Motion Sensor to use with automations.", + "description": "This expose Pixel Refresh as a Motion Sensor to use with automations, fired if PixelRefresh ON.", "required": false }, "sensorVolume": { "title": "Volume", "type": "boolean", "default": false, - "description": "This expose Volume as a Motion Sensor to use with automations.", + "description": "This expose Volume as a Motion Sensor to use with automations, fired on every Volume change.", "required": false }, "sensorMute": { "title": "Mute", "type": "boolean", "default": false, - "description": "This expose Mute as a Motion Sensor to use with automations.", + "description": "This expose Mute as a Motion Sensor to use with automations, fired if Mute ON.", "required": false }, "sensorInput": { "title": "Input", "type": "boolean", "default": false, - "description": "This expose Input as a Motion Sensor to use with automations.", + "description": "This expose Input as a Motion Sensor to use with automations, fired on every Input change.", "required": false }, "sensorChannel": { "title": "Channel", "type": "boolean", "default": false, - "description": "This expose Channel as a Motion Sensor to use with automations.", + "description": "This expose Channel as a Motion Sensor to use with automations, fired on every Input change.", "required": false }, "sensorSoundMode": { "title": "Sound Mode", "type": "boolean", "default": false, - "description": "This expose Sound Mode as a Motion Sensor to use with automations, only for webOS >= 6.0.", + "description": "This expose Sound Mode as a Motion Sensor to use with automations, fired on every Sound Mode change, webOS >= 6.0.", + "required": false + }, + "sensorSoundOutput": { + "title": "Sound Output", + "type": "boolean", + "default": false, + "description": "This expose Sound Output as a Motion Sensor to use with automations, fired on every Sound Output change.", "required": false }, "sensorPictureMode": { "title": "Picture Mode", "type": "boolean", "default": false, - "description": "This expose Picture Mode as a Motion Sensor to use with automations, only for webOS >= 4.0.", + "description": "This expose Picture Mode as a Motion Sensor to use with automations, fired on every Picture Mode change, webOS >= 4.0.", "required": false }, "sensorScreenOnOff": { - "title": "Screen On/Off", + "title": "Screen Off", "type": "boolean", "default": false, - "description": "This expose Screen ON/OFF as a Motion Sensor to use with automations.", + "description": "This expose Screen ON/OFF as a Motion Sensor to use with automations, fired if Screen Off, webOS >= 4.0.", "required": false }, "sensorScreenSaver": { "title": "Screen Saver", "type": "boolean", "default": false, - "description": "This expose Screen Saver as a Motion Sensor to use with automations.", + "description": "This expose Screen Saver as a Motion Sensor to use with automations, fired if Screen Saver ON, webOS >= 4.0.", "required": false }, "sensorInputs": { @@ -706,7 +713,14 @@ "title": "Turn Screen ON/OFF", "type": "boolean", "default": false, - "description": "This enable possibility turn ON/OFF screen, only for webOS >= 4.0.", + "description": "This enable possibility turn screen ON/OFF, webOS >= 4.0.", + "required": false + }, + "turnScreenSaverOnOff": { + "title": "Turn Screen Saver ON/OFF", + "type": "boolean", + "default": false, + "description": "This enable possibility turn screen saver ON/OFF, webOS >= 4.0.", "required": false }, "brightnessControl": { @@ -741,7 +755,7 @@ "title": "Picture Modes", "type": "boolean", "default": false, - "description": "This enable possibility change the Picture Mode, only for webOS >= 4.0.", + "description": "This enable possibility change the Picture Mode, webOS >= 4.0.", "required": false }, "pictureModes": { @@ -822,6 +836,12 @@ "hdrEffect" ] }, + { + "title": "HDR Game", + "enum": [ + "hdrGame" + ] + }, { "title": "HDR Standard", "enum": [ @@ -876,6 +896,12 @@ "dolbyHdrCinema" ] }, + { + "title": "Dolby HDR Vivid", + "enum": [ + "dolbyHdrVivid" + ] + }, { "title": "Dolby HDR Cinema Bright", "enum": [ @@ -909,6 +935,32 @@ ], "required": true }, + "displayType": { + "title": "Display Type", + "type": "integer", + "oneOf": [ + { + "title": "None/Disabled", + "enum": [ + 0 + ] + }, + { + "title": "Outlet", + "enum": [ + 1 + ] + }, + { + "title": "Switch", + "enum": [ + 2 + ] + } + ], + "description": "Here select display type in HomeKit app.", + "required": true + }, "namePrefix": { "title": "Prefix", "type": "boolean", @@ -973,7 +1025,7 @@ "description": "Here choice the Sound Mode.", "oneOf": [ { - "title": "Ai Sound", + "title": "Ai Sound Plus", "enum": [ "aiSoundPlus" ] @@ -981,17 +1033,17 @@ { "title": "Standard", "enum": [ - "Standard" + "standard" ] }, { - "title": "Cinema", + "title": "Movie", "enum": [ "movie" ] }, { - "title": "ClearVoice", + "title": "Clear Voice", "enum": [ "clearVoice" ] @@ -1023,6 +1075,32 @@ ], "required": true }, + "displayType": { + "title": "Display Type", + "type": "integer", + "oneOf": [ + { + "title": "None/Disabled", + "enum": [ + 0 + ] + }, + { + "title": "Outlet", + "enum": [ + 1 + ] + }, + { + "title": "Switch", + "enum": [ + 2 + ] + } + ], + "description": "Here select display type in HomeKit app.", + "required": true + }, "namePrefix": { "title": "Prefix", "type": "boolean", @@ -1032,6 +1110,129 @@ } } }, + "soundOutputControl": { + "title": "Sound Output", + "type": "boolean", + "default": false, + "description": "This enable possibility change the Sound Output.", + "required": false + }, + "soundOutputs": { + "title": "Sound Output", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "title": "Name", + "type": "string", + "placeholder": "Sound Output Name", + "description": "Here set Your own Sound Output name.", + "required": true + }, + "reference": { + "title": "Output", + "type": "string", + "description": "Here choice the Sound Output.", + "oneOf": [ + { + "title": "TV Speaker", + "enum": [ + "tv_speaker" + ] + }, + { + "title": "External Speaker", + "enum": [ + "external_speaker" + ] + }, + { + "title": "External Optical", + "enum": [ + "external_optical" + ] + }, + { + "title": "External ARC", + "enum": [ + "external_arc" + ] + }, + { + "title": "Line Out", + "enum": [ + "lineout" + ] + }, + { + "title": "Headphone", + "enum": [ + "headphone" + ] + }, + { + "title": "TV External Speaker", + "enum": [ + "tv_external_speaker" + ] + }, + { + "title": "TV Speaker Headphone", + "enum": [ + "tv_speaker_headphone" + ] + }, + { + "title": "BT Soundbar", + "enum": [ + "bt_soundbar" + ] + }, + { + "title": "Soundbar", + "enum": [ + "soundbar" + ] + } + ], + "required": true + }, + "displayType": { + "title": "Display Type", + "type": "integer", + "oneOf": [ + { + "title": "None/Disabled", + "enum": [ + 0 + ] + }, + { + "title": "Outlet", + "enum": [ + 1 + ] + }, + { + "title": "Switch", + "enum": [ + 2 + ] + } + ], + "description": "Here select display type in HomeKit app.", + "required": true + }, + "namePrefix": { + "title": "Prefix", + "type": "boolean", + "description": "Here enable/disable the accessory name as a prefix for sound output.", + "required": true + } + } + } + }, "enableDebugMode": { "title": "Debug", "type": "boolean", @@ -1322,6 +1523,7 @@ "devices[].sensorInput", "devices[].sensorChannel", "devices[].sensorSoundMode", + "devices[].sensorSoundOutput", "devices[].sensorPictureMode", "devices[].sensorScreenOnOff", "devices[].sensorScreenSaver", @@ -1353,6 +1555,7 @@ "title": "Picture / Screen", "items": [ "devices[].turnScreenOnOff", + "devices[].turnScreenSaverOnOff", "devices[].brightnessControl", "devices[].backlightControl", "devices[].contrastControl", @@ -1367,6 +1570,7 @@ "items": [ "devices[].pictureModes[].name", "devices[].pictureModes[].reference", + "devices[].pictureModes[].displayType", "devices[].pictureModes[].namePrefix" ], "condition": { @@ -1392,6 +1596,7 @@ "items": [ "devices[].soundModes[].name", "devices[].soundModes[].reference", + "devices[].soundModes[].displayType", "devices[].soundModes[].namePrefix" ], "condition": { @@ -1399,6 +1604,25 @@ } } ] + }, + "devices[].soundOutputControl", + { + "items": [ + { + "key": "devices[].soundOutputs", + "type": "tabarray", + "title": "{{ value.name || 'mode' }}", + "items": [ + "devices[].soundOutputs[].name", + "devices[].soundOutputs[].reference", + "devices[].soundOutputs[].displayType", + "devices[].soundOutputs[].namePrefix" + ], + "condition": { + "functionBody": "return model.devices[arrayIndices].soundOutputControl === true;" + } + } + ] } ] }, diff --git a/package.json b/package.json index a0d127d..fec9623 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "displayName": "LG webOS TV", "name": "homebridge-lgwebos-tv", - "version": "2.17.14", + "version": "2.18.0-beta.24", "description": "Homebridge plugin to control LG webOS TV.", "license": "MIT", "author": "grzegorz914", diff --git a/sample-config.json b/sample-config.json index 235262e..2da6a4b 100644 --- a/sample-config.json +++ b/sample-config.json @@ -80,18 +80,29 @@ { "name": "Cinema", "reference": "cinema", + "displayType": 0, "namePrefix": false } ], - "volumeControl": 0, "soundModeControl": false, "soundModes": [ { "name": "Cinema", "reference": "movie", + "displayType": 0, + "namePrefix": false + } + ], + "soundOutputControl": false, + "soundOutputs": [ + { + "name": "TV Speaker", + "reference": "tv_speaker", + "displayType": 0, "namePrefix": false } ], + "volumeControl": 0, "sensorPower": false, "sensorPixelRefresh": false, "sensorVolume": false, @@ -110,13 +121,14 @@ "namePrefix": false } ], + "turnScreenOnOff": false, + "turnScreenSaverOnOff": false, + "sslWebSocket": false, + "infoButtonCommand": "MENU", "enableDebugMode": false, "disableLogInfo": false, "disableLogDeviceInfo": false, "disableTvService": false, - "turnScreenOnOff": false, - "sslWebSocket": false, - "infoButtonCommand": "MENU", "enableRestFul": false, "restFulPort": 3000, "restFulDebug": false, diff --git a/src/constants.json b/src/constants.json index 2292025..c47ead7 100644 --- a/src/constants.json +++ b/src/constants.json @@ -78,10 +78,9 @@ "SetMediaFastForward": "ssap://media.controls/fastForward", "SetTvChannelUp": "ssap://tv/channelUp", "SetTvChannelDown": "ssap://tv/channelDown", - "SetToast": "ssap://system.notifications/createToast", - "ShowMessage": "ssap://system.notifications/createToast", + "CreateToast": "ssap://system.notifications/createToast", "CloseToast": "ssap://system.notifications/closeToast", - "CreateToast": "ssap://system.notifications/createAlert", + "CreateAlert": "ssap://system.notifications/createAlert", "CloseAletrt": "ssap://system.notifications/closeAlert", "SetConfig": "luna://com.webos.service.config/setConfigs", "SetSystemSettings": "luna://com.webos.settingsservice/setSystemSettings", diff --git a/src/lgwebosdevice.js b/src/lgwebosdevice.js index 00b0dfe..571602e 100644 --- a/src/lgwebosdevice.js +++ b/src/lgwebosdevice.js @@ -37,6 +37,7 @@ class LgWebOsDevice extends EventEmitter { this.sensorInput = device.sensorInput || false; this.sensorChannel = device.sensorChannel || false; this.sensorSoundMode = device.sensorSoundMode || false; + this.sensorSoundOutput = device.sensorSoundOutput || false; this.sensorPictureMode = device.sensorPictureMode || false; this.sensorScreenOnOff = device.sensorScreenOnOff || false; this.sensorScreenSaver = device.sensorScreenSaver || false; @@ -46,14 +47,17 @@ class LgWebOsDevice extends EventEmitter { this.contrastControl = device.contrastControl || false; this.colorControl = device.colorControl || false; this.pictureModeControl = device.pictureModeControl || false; - this.pictureModes = device.pictureModes || []; + this.pictureModes = this.pictureModeControl ? device.pictureModes || [] : []; this.soundModeControl = device.soundModeControl || false; - this.soundModes = device.soundModes || []; + this.soundModes = this.soundModeControl ? device.soundModes || [] : []; + this.soundOutputControl = device.soundOutputControl || false; + this.soundOutputs = this.soundOutputControl ? device.soundOutputs || [] : []; this.enableDebugMode = device.enableDebugMode || false; this.disableLogInfo = device.disableLogInfo || false; this.disableLogDeviceInfo = device.disableLogDeviceInfo || false; this.disableTvService = device.disableTvService || false; this.turnScreenOnOff = device.turnScreenOnOff || false; + this.turnScreenSaverOnOff = device.turnScreenSaverOnOff || false; this.sslWebSocket = device.sslWebSocket || false; this.infoButtonCommand = device.infoButtonCommand || 'INFO'; this.volumeControl = device.volumeControl || false; @@ -65,8 +69,9 @@ class LgWebOsDevice extends EventEmitter { //accessory services this.allServices = []; this.buttonsServices = []; - this.soundsModesServices = []; this.picturesModesServices = []; + this.soundsModesServices = []; + this.soundsOutputsServices = []; this.sensorsInputsServices = []; //add configured inputs to the default inputs @@ -76,10 +81,9 @@ class LgWebOsDevice extends EventEmitter { //state variable this.power = false; - this.pixelRefresh = false; + this.pixelRefreshState = false; this.screenState = false; - this.screenOnOff = false; - this.screenSaver = false; + this.screenSaverState = false; this.appId = ''; this.volume = 0; this.mute = true; @@ -92,8 +96,57 @@ class LgWebOsDevice extends EventEmitter { this.color = 0; this.pictureMode = 3; this.soundMode = ''; + this.soundOutput = ''; this.invertMediaState = false; + //picture mode variable + this.picturesModesConfigured = []; + for (const mode of this.pictureModes) { + const pictureModeName = mode.name ?? false; + const pictureModeReference = mode.reference ?? false; + const pictureModeDisplayType = mode.displayType ?? 0; + if (pictureModeName && pictureModeReference && pictureModeDisplayType > 0) { + mode.serviceType = ['', Service.Outlet, Service.Switch][pictureModeDisplayType]; + mode.state = false; + this.picturesModesConfigured.push(mode); + } else { + const log = pictureModeDisplayType === 0 ? false : this.emit('message', `Picture Mode Name: ${pictureModeName ? pictureModeName : 'Missing'}, 'Reference: ${pictureModeReference ? pictureModeReference : 'Missing'}.`); + }; + } + this.picturesModesConfiguredCount = this.picturesModesConfigured.length || 0; + + //sound mode variable + this.soundsModesConfigured = []; + for (const mode of this.soundModes) { + const soundModeName = mode.name ?? false; + const soundModeReference = mode.reference ?? false; + const soundModeDisplayType = mode.displayType ?? 0; + if (soundModeName && soundModeReference && soundModeDisplayType > 0) { + mode.serviceType = ['', Service.Outlet, Service.Switch][soundModeDisplayType]; + mode.state = false; + this.soundsModesConfigured.push(mode); + } else { + const log = soundModeDisplayType === 0 ? false : this.emit('message', `Sound Mode Name: ${soundModeName ? soundModeName : 'Missing'}, 'Reference: ${soundModeReference ? soundModeReference : 'Missing'}.`); + }; + } + this.soundsModesConfiguredCount = this.soundsModesConfigured.length || 0; + + //sound output variable + this.soundsOutputsConfigured = []; + for (const output of this.soundOutputs) { + const soundOutputName = output.name ?? false; + const soundOutputReference = output.reference ?? false; + const soundOutputDisplayType = output.displayType ?? 0; + if (soundOutputName && soundOutputReference && soundOutputDisplayType > 0) { + output.serviceType = ['', Service.Outlet, Service.Switch][soundOutputDisplayType]; + output.state = false; + this.soundsOutputsConfigured.push(output); + } else { + const log = soundOutputDisplayType === 0 ? false : this.emit('message', `Sound Mode Name: ${soundOutputName ? soundOutputName : 'Missing'}, 'Reference: ${soundOutputReference ? soundOutputReference : 'Missing'}.`); + }; + } + this.soundsOutputsConfiguredCount = this.soundsOutputsConfigured.length || 0; + //sensors variable this.sensorsInputsConfigured = []; for (const sensor of this.sensorInputs) { @@ -114,6 +167,7 @@ class LgWebOsDevice extends EventEmitter { this.sensorInputState = false; this.sensorChannelState = false; this.sensorSoundModeState = false; + this.sensorSoundOutputState = false; this.sensorPicturedModeState = false; //buttons variable @@ -200,39 +254,45 @@ class LgWebOsDevice extends EventEmitter { this.emit('devInfo', `----------------------------------`); }; }) - .on('powerState', (power, pixelRefresh, screenState, tvScreenState) => { + .on('powerState', (power, screenState) => { if (this.televisionService) { this.televisionService .updateCharacteristic(Characteristic.Active, power); }; + if (this.turnScreenSaverOnOffService) { + const state = power ? screenState === 'Screen Off' : false; + this.turnScreenSaverOnOffService + .updateCharacteristic(Characteristic.On, state) + } + if (this.turnScreenOnOffService) { + const state = power ? screenState === 'Screen Saver' : false; this.turnScreenOnOffService - .updateCharacteristic(Characteristic.On, screenState); + .updateCharacteristic(Characteristic.On, state); }; if (this.sensorPowerService) { this.sensorPowerService - .updateCharacteristic(Characteristic.ContactSensorState, power) + .updateCharacteristic(Characteristic.ContactSensorState, power); } if (this.sensorPixelRefreshService) { + const state = power ? screenState === 'Active Standby' : false; this.sensorPixelRefreshService - .updateCharacteristic(Characteristic.ContactSensorState, pixelRefresh) + .updateCharacteristic(Characteristic.ContactSensorState, state); } if (this.sensorScreenOnOffService) { - const state = power ? (tvScreenState === 'Screen Off') : false; - this.screenOnOff = state; + const state = power ? screenState === 'Screen Off' : false; this.sensorScreenOnOffService - .updateCharacteristic(Characteristic.ContactSensorState, state) + .updateCharacteristic(Characteristic.ContactSensorState, state); } if (this.sensorScreenSaverService) { - const state = power ? (tvScreenState === 'Screen Saver') : false; - this.screenSaver = state; + const state = power ? screenState === 'Screen Saver' : false; this.sensorScreenSaverService - .updateCharacteristic(Characteristic.ContactSensorState, state) + .updateCharacteristic(Characteristic.ContactSensorState, state); } if (this.buttonsServices && !power) { @@ -245,8 +305,9 @@ class LgWebOsDevice extends EventEmitter { } this.power = power; - this.pixelRefresh = pixelRefresh; - this.screenState = screenState; + this.pixelRefreshState = power ? screenState === 'Active Standby' : false; + this.screenState = power ? screenState === 'Screen Off' : false; + this.screenSaverState = power ? screenState === 'Screen Saver' : false; if (!this.disableLogInfo) { this.emit('message', `Power: ${power ? 'ON' : 'OFF'}`); }; @@ -298,7 +359,7 @@ class LgWebOsDevice extends EventEmitter { this.emit('message', `Reference: ${appId}`); }; }) - .on('audioState', (volume, mute, audioOutput) => { + .on('audioState', (volume, mute) => { if (this.speakerService) { this.speakerService .updateCharacteristic(Characteristic.Volume, volume) @@ -417,9 +478,10 @@ class LgWebOsDevice extends EventEmitter { }; if (this.picturesModesServices) { - const servicesCount = this.picturesModesServices.length; - for (let i = 0; i < servicesCount; i++) { - const state = power ? (this.pictureModes[i].reference === pictureMode) : false; + for (let i = 0; i < this.picturesModesConfiguredCount; i++) { + const mode = this.picturesModesConfigured[i]; + const state = power ? mode.reference === pictureMode : false; + mode.state = state; this.picturesModesServices[i] .updateCharacteristic(Characteristic.On, state); }; @@ -449,9 +511,10 @@ class LgWebOsDevice extends EventEmitter { }) .on('soundMode', (soundMode, power) => { if (this.soundsModesServices) { - const servicesCount = this.soundsModesServices.length; - for (let i = 0; i < servicesCount; i++) { - const state = power ? this.soundModes[i].reference === soundMode : false; + for (let i = 0; i < this.soundsModesConfiguredCount; i++) { + const mode = this.soundsModesConfigured[i]; + const state = power ? mode.reference === soundMode : false; + mode.state = state; this.soundsModesServices[i] .updateCharacteristic(Characteristic.On, state); }; @@ -471,6 +534,31 @@ class LgWebOsDevice extends EventEmitter { this.emit('message', `Sound Mode: ${soundMode}`); }; }) + .on('soundOutput', (soundOutput, power) => { + if (this.soundsOutputsServices) { + for (let i = 0; i < this.soundsOutputsConfiguredCount; i++) { + const output = this.soundsOutputsConfigured[i]; + const state = power ? output.reference === soundOutput : false; + output.state = state; + this.soundsOutputsServices[i] + .updateCharacteristic(Characteristic.On, state); + }; + }; + + if (this.sensorSoundOutputService && soundMode !== this.soundOutput) { + for (let i = 0; i < 2; i++) { + const state = power ? [true, false][i] : false; + this.sensorSoundOutputService + .updateCharacteristic(Characteristic.ContactSensorState, state) + this.sensorSoundOutputState = state; + } + } + + this.soundOutput = soundOutput; + if (!this.disableLogInfo) { + this.emit('message', `Sound Output: ${soundOutput}`); + }; + }) .on('prepareAccessory', async () => { //RESTFul server const restFulEnabled = device.enableRestFul || false; @@ -521,17 +609,17 @@ class LgWebOsDevice extends EventEmitter { await this.wol.wakeOnLan(); break; case 0: - const cid = await this.lgWebOsSocket.getCids('Power'); + const cid = await this.lgWebOsSocket.getCid('Power'); await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.TurnOff, undefined, cid); break; } break; case 'App': - const cid = await this.lgWebOsSocket.getCids('App'); + const cid = await this.lgWebOsSocket.getCid('App'); await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.LaunchApp, { id: value }, cid); break; case 'Channel': - const cid1 = await this.lgWebOsSocket.getCids('Channel'); + const cid1 = await this.lgWebOsSocket.getCid('Channel'); await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.OpenChannel, { channelId: value }, cid1) break; case 'Volume': @@ -540,21 +628,88 @@ class LgWebOsDevice extends EventEmitter { volume: volume, soundOutput: this.soundOutput }; - const cid2 = await this.lgWebOsSocket.getCids('Audio'); + const cid2 = await this.lgWebOsSocket.getCid('Audio'); await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetVolume, payload, cid2); break; case 'Mute': - const payload1 = { + const payload3 = { mute: value }; - const cid3 = await this.lgWebOsSocket.getCids('Audio'); + const cid3 = await this.lgWebOsSocket.getCid('Audio'); await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetMute, payload1, cid3); break; + case 'Brightness': + const payload4 = { + category: 'picture', + settings: { + brightness: value + } + }; + const cid4 = await this.lgWebOsSocket.getCid(); + await this.lgWebOsSocket.send('alert', CONSTANTS.ApiUrls.SetMute, payload4, cid4); + break; + case 'Backlight': + const payload5 = { + category: 'picture', + settings: { + backlight: value + } + }; + const cid5 = await this.lgWebOsSocket.getCid(); + await this.lgWebOsSocket.send('alert', CONSTANTS.ApiUrls.SetMute, payload5, cid5); + break; + case 'Contrast': + const payload6 = { + category: 'picture', + settings: { + contrast: value + } + }; + const cid6 = await this.lgWebOsSocket.getCid(); + await this.lgWebOsSocket.send('alert', CONSTANTS.ApiUrls.SetMute, payload6, cid6); + break; + case 'Color': + const payload7 = { + category: 'picture', + settings: { + color: value + } + }; + const cid7 = await this.lgWebOsSocket.getCid(); + await this.lgWebOsSocket.send('alert', CONSTANTS.ApiUrls.SetMute, payload7, cid7); + break; + case 'PictureMode': + const payload8 = { + category: 'picture', + settings: { + pictureMode: value + } + }; + const cid8 = await this.lgWebOsSocket.getCid(); + await this.lgWebOsSocket.send('alert', CONSTANTS.ApiUrls.SetMute, payload8, cid8); + break; + case 'SoundMode': + const payload9 = { + category: 'picture', + settings: { + soundMode: value + } + }; + const cid9 = await this.lgWebOsSocket.getCid(); + await this.lgWebOsSocket.send('alert', CONSTANTS.ApiUrls.SetMute, payload9, cid9); + break; + case 'SoundOutput': + const payload10 = { + output: value + }; + const cid10 = await this.lgWebOsSocket.getCid('SoundOutput'); + await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetSoundOutput, payload10, cid10); + break; case 'RcControl': - const payload2 = { + const payload11 = { name: value }; - await this.lgWebOsSocket.send('button', undefined, payload2); + await this.lgWebOsSocket.send('button', undefined, payload11); break; default: this.emit('message', `MQTT Received unknown key: ${key}, value: ${value}`); @@ -581,7 +736,7 @@ class LgWebOsDevice extends EventEmitter { //read inputs file const savedInputs = await this.readData(inputsFile); - this.savedInputs = this.getInputsFromDevice && savedInputs.toString().trim() !== '' ? JSON.parse(savedInputs) : this.inputs; + this.savedInputs = savedInputs.toString().trim() !== '' ? JSON.parse(savedInputs) : this.inputs; const debug1 = this.enableDebugMode ? this.emit('debug', `Read saved Inputs: ${JSON.stringify(this.savedInputs, null, 2)}`) : false; //read channels from file @@ -726,7 +881,7 @@ class LgWebOsDevice extends EventEmitter { await this.wol.wakeOnLan(); break; case 0: - const cid = await this.lgWebOsSocket.getCids('Power'); + const cid = await this.lgWebOsSocket.getCid('Power'); await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.TurnOff, undefined, cid); break; } @@ -757,14 +912,14 @@ class LgWebOsDevice extends EventEmitter { case true: switch (inputMode) { case 0: - const cid = await this.lgWebOsSocket.getCids('App'); + const cid = await this.lgWebOsSocket.getCid('App'); await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.LaunchApp, { id: inputReference }, cid); break; case 1: const liveTv = 'com.webos.app.livetv'; - const cid1 = this.appId !== liveTv ? await this.lgWebOsSocket.getCids('App') : false; + const cid1 = this.appId !== liveTv ? await this.lgWebOsSocket.getCid('App') : false; const openLiveTv = this.appId !== liveTv ? await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.LaunchApp, { id: liveTv }, cid1) : false; - const cid2 = await this.lgWebOsSocket.getCids('Channel'); + const cid2 = await this.lgWebOsSocket.getCid('Channel'); await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.OpenChannel, { channelId: inputReference }, cid2) break; } @@ -901,13 +1056,14 @@ class LgWebOsDevice extends EventEmitter { .onSet(async (value) => { try { const payload = { - 'settings': { - 'brightness': value + category: 'picture', + settings: { + brightness: value } }; - const cid = await this.lgWebOsSocket.getCids('Pisture'); - await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetSystemSettings, payload, cid); + const cid = await this.lgWebOsSocket.getCid(); + await this.lgWebOsSocket.send('alert', CONSTANTS.ApiUrls.SetSystemSettings, payload, cid, 'Set Brightness', `Value: ${value}`); const info = this.disableLogInfo ? false : this.emit('message', `set Brightness: ${value}`); } catch (error) { this.emit('error', `set Brightness error: ${error}`); @@ -951,12 +1107,12 @@ class LgWebOsDevice extends EventEmitter { const payload = { category: 'picture', settings: { - 'pictureMode': command + pictureMode: command } }; - const cid = await this.lgWebOsSocket.getCids('Pisture'); - await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetSystemSettings, payload, cid); + const cid = await this.lgWebOsSocket.getCid(); + await this.lgWebOsSocket.send('alert', CONSTANTS.ApiUrls.SetSystemSettings, payload, cid), 'Set Picture Mode', `Value: ${command}`; const info = this.disableLogInfo ? false : this.emit('message', `set Picture Mode: ${command}`); } catch (error) { this.emit('error', `set Picture Mode error: ${error}`); @@ -1020,7 +1176,7 @@ class LgWebOsDevice extends EventEmitter { soundOutput: this.soundOutput }; - const cid = await this.lgWebOsSocket.getCids('Audio'); + const cid = await this.lgWebOsSocket.getCid('Audio'); await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetVolume, payload, cid); const info = this.disableLogInfo ? false : this.emit('message', `set Volume: ${volume}`); } catch (error) { @@ -1039,7 +1195,7 @@ class LgWebOsDevice extends EventEmitter { mute: state }; - const cid = await this.lgWebOsSocket.getCids('Audio'); + const cid = await this.lgWebOsSocket.getCid('Audio'); await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetMute, payload, cid); const info = this.disableLogInfo ? false : this.emit('message', `set Mute: ${state ? 'ON' : 'OFF'}`); } catch (error) { @@ -1159,7 +1315,7 @@ class LgWebOsDevice extends EventEmitter { soundOutput: this.soundOutput }; - const cid = await this.lgWebOsSocket.getCids('Audio'); + const cid = await this.lgWebOsSocket.getCid('Audio'); await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetVolume, payload, cid); const info = this.disableLogInfo ? false : this.emit('message', `set Volume: ${volume}`); } catch (error) { @@ -1177,7 +1333,7 @@ class LgWebOsDevice extends EventEmitter { mute: !state }; - const cid = await this.lgWebOsSocket.getCids('Audio'); + const cid = await this.lgWebOsSocket.getCid('Audio'); await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetMute, payload, cid); const info = this.disableLogInfo ? false : this.emit('message', `set Mute: ${!state ? 'ON' : 'OFF'}`); } catch (error) { @@ -1204,7 +1360,7 @@ class LgWebOsDevice extends EventEmitter { soundOutput: this.soundOutput }; - const cid = await this.lgWebOsSocket.getCids('Audio'); + const cid = await this.lgWebOsSocket.getCid('Audio'); await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetVolume, payload, cid); const info = this.disableLogInfo ? false : this.emit('message', `set Volume: ${volume}`); } catch (error) { @@ -1222,7 +1378,7 @@ class LgWebOsDevice extends EventEmitter { mute: !state }; - const cid = await this.lgWebOsSocket.getCids('Audio') + const cid = await this.lgWebOsSocket.getCid('Audio') await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetMute, payload, cid); const info = this.disableLogInfo ? false : this.emit('message', `set Mute: ${!state ? 'ON' : 'OFF'}`); } catch (error) { @@ -1255,13 +1411,14 @@ class LgWebOsDevice extends EventEmitter { .onSet(async (value) => { try { const payload = { - 'settings': { - 'backlight': value + category: 'picture', + settings: { + backlight: value } }; - const cid = await this.lgWebOsSocket.getCids('Picture'); - await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetSystemSettings, payload, cid); + const cid = await this.lgWebOsSocket.getCid(); + await this.lgWebOsSocket.send('alert', CONSTANTS.ApiUrls.SetSystemSettings, payload, cid, 'Set Backlight', `Value: ${value}`); const info = this.disableLogInfo ? false : this.emit('message', `set Backlight: ${value}`); } catch (error) { this.emit('error', `set Backlight error: ${error}`); @@ -1290,13 +1447,14 @@ class LgWebOsDevice extends EventEmitter { .onSet(async (value) => { try { const payload = { - 'settings': { - 'brightness': value + category: 'picture', + settings: { + brightness: value } }; - const cid = await this.lgWebOsSocket.getCids('Picture'); - await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetSystemSettings, payload), cid; + const cid = await this.lgWebOsSocket.getCid(); + await this.lgWebOsSocket.send('alert', CONSTANTS.ApiUrls.SetSystemSettings, payload, cid, 'Set Brightness', `Value: ${value}`); const info = this.disableLogInfo ? false : this.emit('message', `set Brightness: ${value}`); } catch (error) { this.emit('error', `set Brightness error: ${error}`); @@ -1325,13 +1483,14 @@ class LgWebOsDevice extends EventEmitter { .onSet(async (value) => { try { const payload = { - 'settings': { - 'contrast': value + category: 'picture', + settings: { + contrast: value } }; - const cid = await this.lgWebOsSocket.getCids('Picture'); - await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetSystemSettings, payload, cid); + const cid = await this.lgWebOsSocket.getCid(); + await this.lgWebOsSocket.send('alert', CONSTANTS.ApiUrls.SetSystemSettings, payload, cid, 'Set Contrast', `Value: ${value}`); const info = this.disableLogInfo ? false : this.emit('message', `set Contrast: ${value}`); } catch (error) { this.emit('error', `set Contrast error: ${error}`); @@ -1360,13 +1519,14 @@ class LgWebOsDevice extends EventEmitter { .onSet(async (value) => { try { const payload = { - 'settings': { - 'color': value + category: 'picture', + settings: { + color: value } }; - const cid = await this.lgWebOsSocket.getCids('Picture'); - await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetSystemSettings, payload, cid); + const cid = await this.lgWebOsSocket.getCid(); + await this.lgWebOsSocket.send('alert', CONSTANTS.ApiUrls.SetSystemSettings, payload, cid, 'Set Color', `Value: ${value}`); const info = this.disableLogInfo ? false : this.emit('message', `set Color: ${value}`); } catch (error) { this.emit('error', `set Color error: ${error}`); @@ -1376,21 +1536,21 @@ class LgWebOsDevice extends EventEmitter { } //Picture mode - if (this.pictureModeControl) { + if (this.picturesModesConfiguredCount > 0) { const debug = this.enableDebugMode ? this.emit('debug', `Prepare picture mode service`) : false; - const pictureModes = this.pictureModes; - const pictureModesCount = pictureModes.length; - for (let i = 0; i < pictureModesCount; i++) { - const pictureModeName = pictureModes[i].name; - const pictureModeReference = pictureModes[i].reference; - const pictureModeNamePrefix = pictureModes[i].namePrefix; - const pictureModeServiceName = pictureModeNamePrefix ? `${accessoryName} ${pictureModeName}` : pictureModeName; - const pictureModeService = accessory.addService(Service.Switch, pictureModeServiceName, `Picture Mode ${i}`); + for (let i = 0; i < this.picturesModesConfiguredCount; i++) { + const mode = this.picturesModesConfigured[i]; + const modeName = mode.name; + const modeReference = mode.reference; + const modeNamePrefix = mode.namePrefix || false; + const serviceType = mode.serviceType; + const serviceName = modeNamePrefix ? `${accessoryName} ${modeName}` : modeName; + const pictureModeService = new serviceType(serviceName, `Picture Mode ${i}`); pictureModeService.addOptionalCharacteristic(Characteristic.ConfiguredName); - pictureModeService.setCharacteristic(Characteristic.ConfiguredName, pictureModeServiceName); + pictureModeService.setCharacteristic(Characteristic.ConfiguredName, serviceName); pictureModeService.getCharacteristic(Characteristic.On) .onGet(async () => { - const state = this.power ? (this.pictureMode === pictureModeReference) : false; + const state = this.power ? mode.state : false; return state; }) .onSet(async (state) => { @@ -1398,22 +1558,47 @@ class LgWebOsDevice extends EventEmitter { const payload = { category: 'picture', settings: { - 'pictureMode': pictureModeReference + pictureMode: modeReference } } - const cid = await this.lgWebOsSocket.getCids('Picture'); - await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetSystemSettings, payload, cid); - const info = this.disableLogInfo ? false : this.emit('message', `set Picture Mode: ${pictureModeName}`); + const cid = state ? await this.lgWebOsSocket.getCid() : false; + const set = state ? await this.lgWebOsSocket.send('alert', CONSTANTS.ApiUrls.SetSystemSettings, payload, cid, 'Set Picture Mode', `Value: ${modeName}`) : false; + const info = this.disableLogInfo ? false : this.emit('message', `set Picture Mode: ${modeName}`); } catch (error) { this.emit('error', `set Picture Mode error: ${error}`); }; }); this.picturesModesServices.push(pictureModeService); this.allServices.push(pictureModeService); + accessory.addService(pictureModeService); } } + //turn screen saver ON/OFF + if (this.turnScreenSaverOnOff) { + const debug = this.enableDebugMode ? this.emit('debug', `Prepare screen saver service`) : false; + this.turnScreenSaverOnOffService = accessory.addService(Service.Switch, `${accessoryName} Screen Saver`, 'Screen Saver'); + this.turnScreenSaverOnOffService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.turnScreenSaverOnOffService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Screen Saver`); + this.turnScreenSaverOnOffService.getCharacteristic(Characteristic.On) + .onGet(async () => { + const state = this.screenSaverState; + return state; + }) + .onSet(async (state) => { + try { + const payload = {}; + const cid = await this.lgWebOsSocket.getCid(); + const set = state ? await this.lgWebOsSocket.send('alert', CONSTANTS.ApiUrls.TurnOnScreenSaver, payload, cid, 'Set Screen Saver', `Value: ON`) : await this.lgWebOsSocket.send('button', undefined, { name: 'EXIT' }); + const info = this.disableLogInfo ? false : this.emit('message', `set Screen Saver: ${state}`); + } catch (error) { + this.emit('error', `set Color error: ${error}`); + }; + }); + this.allServices.push(this.turnScreenSaverOnOffService); + } + //turn screen ON/OFF if (this.turnScreenOnOff) { const debug = this.enableDebugMode ? this.emit('debug', `Prepare screen off service`) : false; @@ -1437,7 +1622,7 @@ class LgWebOsDevice extends EventEmitter { break; } - const cid = await this.lgWebOsSocket.getCids('Power'); + const cid = await this.lgWebOsSocket.getCid('Power'); await this.lgWebOsSocket.send('request', url, undefined, cid); const info = this.disableLogInfo ? false : this.emit('message', `Turn Screen ${state ? 'ON' : 'OFF'}.`); } catch (error) { @@ -1449,22 +1634,21 @@ class LgWebOsDevice extends EventEmitter { }; //Sound mode - if (this.soundModeControl && this.webOS >= 6.0) { + if (this.soundsModesConfiguredCount > 0 && this.webOS >= 6.0) { const debug = this.enableDebugMode ? this.emit('debug', `Prepare sound mode service`) : false; - const soundModes = this.soundModes; - const soundModesCount = soundModes.length; - for (let i = 0; i < soundModesCount; i++) { - //get button name prefix - const soundModeName = soundModes[i].name; - const soundModeReference = soundModes[i].reference; - const soundModeNamePrefix = soundModes[i].namePrefix; - const soundModeServiceName = soundModeNamePrefix ? `${accessoryName} ${soundModeName}` : soundModeName; - const soundModeService = accessory.addService(Service.Switch, soundModeServiceName, `Sound Mode ${i}`); + for (let i = 0; i < this.soundsModesConfiguredCount; i++) { + const mode = this.soundsModesConfigured[i]; + const modeName = mode.name; + const modeReference = mode.reference; + const modeNamePrefix = mode.namePrefix || false; + const serviceType = mode.serviceType; + const serviceName = modeNamePrefix ? `${accessoryName} ${modeName}` : modeName; + const soundModeService = new serviceType(serviceName, `Sound Mode ${i}`); soundModeService.addOptionalCharacteristic(Characteristic.ConfiguredName); - soundModeService.setCharacteristic(Characteristic.ConfiguredName, soundModeServiceName); + soundModeService.setCharacteristic(Characteristic.ConfiguredName, serviceName); soundModeService.getCharacteristic(Characteristic.On) .onGet(async () => { - const state = this.power ? (this.soundMode === soundModeReference) : false; + const state = this.power ? mode.state : false; return state; }) .onSet(async (state) => { @@ -1472,18 +1656,57 @@ class LgWebOsDevice extends EventEmitter { const payload = { category: 'sound', settings: { - 'soundMode': soundModeReference + soundMode: modeReference } } - await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetSystemSettings, payload); - const info = this.disableLogInfo ? false : this.emit('message', `set Sound Mode: ${soundModeName}`); + const cid = state ? await this.lgWebOsSocket.getCid() : false; + const set = state ? await this.lgWebOsSocket.send('alert', CONSTANTS.ApiUrls.SetSystemSettings, payload, cid, 'Set Sound Mode', `Value: ${modeName}`) : false; + const info = this.disableLogInfo ? false : this.emit('message', `set Sound Mode: ${modeName}`); } catch (error) { this.emit('error', `set Sound Mode error: ${error}`); }; }); this.soundsModesServices.push(soundModeService); this.allServices.push(soundModeService); + accessory.addService(soundModeService); + } + } + + //Sound output + if (this.soundsOutputsConfiguredCount > 0) { + const debug = this.enableDebugOutput ? this.emit('debug', `Prepare sound output service`) : false; + for (let i = 0; i < this.soundsOutputsConfiguredCount; i++) { + const output = this.soundsOutputsConfigured[i]; + const outputName = output.name; + const outputReference = output.reference; + const outputNamePrefix = output.namePrefix || false; + const serviceType = output.serviceType; + const serviceName = outputNamePrefix ? `${accessoryName} ${outputName}` : outputName; + const soundOutputService = new serviceType(serviceName, `Sound Output ${i}`); + soundOutputService.addOptionalCharacteristic(Characteristic.ConfiguredName); + soundOutputService.setCharacteristic(Characteristic.ConfiguredName, serviceName); + soundOutputService.getCharacteristic(Characteristic.On) + .onGet(async () => { + const state = this.power ? output.state : false; + return state; + }) + .onSet(async (state) => { + try { + const payload = { + output: outputReference + } + + const cid = state ? await this.lgWebOsSocket.getCid('SoundOutput') : false; + const send = state ? await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.SetSoundOutput, payload, cid) : false; + const info = this.disableLogInfo ? false : this.emit('message', `set Sound Output: ${outputName}`); + } catch (error) { + this.emit('error', `set Sound Output error: ${error}`); + }; + }); + this.soundsOutputsServices.push(soundOutputService); + this.allServices.push(soundOutputService); + accessory.addService(soundOutputService); } } @@ -1508,7 +1731,7 @@ class LgWebOsDevice extends EventEmitter { this.sensorPixelRefreshService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Pixel Refresh Sensor`); this.sensorPixelRefreshService.getCharacteristic(Characteristic.ContactSensorState) .onGet(async () => { - const state = this.pixelRefresh; + const state = this.pixelRefreshState; return state; }); this.allServices.push(this.sensorPixelRefreshService); @@ -1573,7 +1796,7 @@ class LgWebOsDevice extends EventEmitter { this.sensorScreenOnOffService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Screen On/Off Sensor`); this.sensorScreenOnOffService.getCharacteristic(Characteristic.ContactSensorState) .onGet(async () => { - const state = this.power ? this.screenOnOff : false; + const state = this.power ? this.screenState : false; return state; }); this.allServices.push(this.sensorScreenOnOffService); @@ -1586,7 +1809,7 @@ class LgWebOsDevice extends EventEmitter { this.sensorScreenSaverService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Screen Saver Sensor`); this.sensorScreenSaverService.getCharacteristic(Characteristic.ContactSensorState) .onGet(async () => { - const state = this.power ? this.screenSaver : false; + const state = this.power ? this.screenSaverState : false; return state; }); this.allServices.push(this.sensorScreenSaverService); @@ -1599,12 +1822,25 @@ class LgWebOsDevice extends EventEmitter { this.sensorSoundModeService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Sound Mode Sensor`); this.sensorSoundModeService.getCharacteristic(Characteristic.ContactSensorState) .onGet(async () => { - const state = this.power ? this.soundModeState : false; + const state = this.power ? this.sensorSoundModeState : false; return state; }); this.allServices.push(this.sensorSoundModeService); }; + if (this.sensorSoundOutput) { + const debug = this.enableDebugOutput ? this.emit('debug', `Prepare sound output sensor service`) : false; + this.sensorSoundOutputService = accessory.addService(Service.ContactSensor, `${accessoryName} Sound Output Sensor`, `Sound Output Sensor`); + this.sensorSoundOutputService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorSoundOutputService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Sound Output Sensor`); + this.sensorSoundOutputService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.power ? this.sensorSoundOutputState : false; + return state; + }); + this.allServices.push(this.sensorSoundOutputService); + }; + if (this.sensorPictureMode && this.webOS >= 4.0) { const debug = this.enableDebugMode ? this.emit('debug', `Prepare picture mode sensor service`) : false; this.sensorPictureModeService = accessory.addService(Service.ContactSensor, `${accessoryName} Picture Mode Sensor`, `Picture Mode Sensor`); @@ -1694,31 +1930,31 @@ class LgWebOsDevice extends EventEmitter { try { switch (buttonMode) { case 0: //App Control - const cid = this.power && state ? await this.lgWebOsSocket.getCids('App') : false; + const cid = this.power && state ? await this.lgWebOsSocket.getCid('App') : false; const send = this.power && state ? await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.LaunchApp, { id: buttonReference }, cid) : false; const debug = this.power && state && this.enableDebugMode ? this.emit('debug', `Set Input, Name: ${buttonName}, Reference: ${buttonReference}`) : false; break; case 1: //Channel Control const liveTv = 'com.webos.app.livetv'; - const cid1 = this.power && state && this.appId !== liveTv ? await this.lgWebOsSocket.getCids('App') : false; + const cid1 = this.power && state && this.appId !== liveTv ? await this.lgWebOsSocket.getCid('App') : false; const openLiveTv = this.appId !== liveTv ? await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.LaunchApp, { id: liveTv }, cid1) : false; - const cid2 = this.power && state ? await this.lgWebOsSocket.getCids('Channel') : false; + const cid2 = this.power && state ? await this.lgWebOsSocket.getCid('Channel') : false; const send1 = this.power && state ? await this.lgWebOsSocket.send('request', CONSTANTS.ApiUrls.OpenChannel, { channelId: buttonReference }, cid2) : false; const debug1 = this.power && state && this.enableDebugMode ? this.emit('debug', `Set Channel, Name: ${buttonName}, Reference: ${buttonReference}`) : false; break; case 2: //RC Control const send2 = state ? await this.lgWebOsSocket.send('button', undefined, { name: buttonCommand }) : false; const debug2 = state && this.enableDebugMode ? this.emit('debug', `Set Command, Name: ${buttonName}, Reference: ${buttonCommand}`) : false; - buttonService.updateCharacteristic(Characteristic.On, false); + button.state = false; break; default: const debug3 = this.enableDebugMode ? this.emit('debug', `Set Unknown Button Mode: ${buttonMode}.`) : false; - buttonService.updateCharacteristic(Characteristic.On, false); + button.state = false; break; } } catch (error) { this.emit('error', `set ${['Input', 'Channel', 'Command'][buttonMode]} error: ${error}`); - buttonService.updateCharacteristic(Characteristic.On, false); + button.state = false; }; }); this.buttonsServices.push(buttonService); diff --git a/src/lgwebossocket.js b/src/lgwebossocket.js index 4e2a494..77a2e19 100644 --- a/src/lgwebossocket.js +++ b/src/lgwebossocket.js @@ -28,21 +28,27 @@ class LgWebOsSocket extends EventEmitter { this.filterSystemApps = filterSystemApps; this.debugLog = debugLog; + this.externalInputsArr = []; this.startPrepareAccessory = true; this.socketConnected = false; this.specjalizedSocketConnected = false; this.power = false; - this.pixelRefresh = false; - this.screenState = false; - this.tvScreenState = 'Suspend'; + this.screenState = 'Suspend'; this.appId = ''; this.volume = 0; this.mute = true; this.soundMode = ''; + this.soundOutput = ''; this.cidCount = 0; this.webOS = 2.0; this.modelName = 'LG TV'; + this.brightness = 0; + this.backlight = 0; + this.contrast = 0; + this.color = 0; + this.pictureMode = 0; + this.connectSocket = async () => { //Read pairing key from file try { @@ -229,6 +235,14 @@ class LgWebOsSocket extends EventEmitter { this.emit('error', `Request channels error: ${error}`); }; + //Request external inputs list + try { + this.externalInputListId = await this.getCid(); + await this.send('subscribe', CONSTANTS.ApiUrls.GetExternalInputList, undefined, this.externalInputListId); + } catch (error) { + this.emit('error', `Request external inputs error: ${error}`); + }; + //Request apps list try { this.appsId = await this.getCid(); @@ -290,6 +304,42 @@ class LgWebOsSocket extends EventEmitter { break; }; break; + case this.externalInputListId: + switch (messageType) { + case 'response': + const debug = debugLog ? this.emit('debug', `External input list: ${stringifyMessage}`) : false; + const externalInputList = messageData.devices; + const externalInputListExist = Array.isArray(externalInputList) ? externalInputList.length > 0 : false; + if (!externalInputListExist) { + return; + }; + + for (const input of externalInputList) { + const name = input.label; + const reference = input.appId; + const mode = 0; + const obj = { + 'name': name, + 'reference': reference, + 'mode': mode + } + this.externalInputsArr.push(obj); + }; + + //restFul + this.emit('restFul', 'externalinputlist', messageData); + + //mqtt + this.emit('mqtt', 'External Input List', messageData); + break; + case 'error': + const debug1 = debugLog ? this.emit('debug', `External input list error: ${stringifyMessage}`) : false; + break; + default: + const debug2 = debugLog ? this.emit('debug', this.emit('debug', `External input list received message, type: ${messageType}, id: ${messageId}, data: ${stringifyMessage}`)) : false; + break; + }; + break; case this.appsId: switch (messageType) { case 'response': @@ -300,8 +350,22 @@ class LgWebOsSocket extends EventEmitter { return; }; + const appsArr = []; + for (const input of appsList) { + const name = input.title; + const reference = input.id; + const mode = 0; + const obj = { + 'name': name, + 'reference': reference, + 'mode': mode + } + appsArr.push(obj); + }; + //save apps to the file - await this.saveInputs(inputsFile, appsList); + const inputsApps = this.getInputsFromDevice ? [...this.externalInputsArr, ...appsArr] : this.inputs; + await this.saveInputs(inputsFile, inputsApps); //restFul this.emit('restFul', 'apps', messageData); @@ -325,45 +389,35 @@ class LgWebOsSocket extends EventEmitter { switch (messageData.state) { case 'Active': this.power = true; - this.screenState = true; - this.pixelRefresh = false; - this.tvScreenState = 'Active'; + this.screenState = 'Active'; break; case 'Active Standby': this.power = false; - this.screenState = false; - this.pixelRefresh = true; - this.tvScreenState = 'Active Standby'; + this.screenState = 'Active Standby'; break; case 'Screen Saver': this.power = true; - this.screenState = true; - this.pixelRefresh = false; - this.tvScreenState = 'Screen Saver'; + this.screenState = 'Screen Saver'; break; case 'Screen Off': this.power = true; - this.screenState = false; - this.pixelRefresh = false; - this.tvScreenState = 'Screen Off'; + this.screenState = 'Screen Off'; break; case 'Suspend': this.power = false; - this.screenState = false; - this.pixelRefresh = false; - this.tvScreenState = 'Suspend'; + this.screenState = 'Suspend'; break; default: - this.tvScreenState = messageData.state; - this.emit('debug', `Unknown power state: ${this.tvScreenState}`); + this.screenState = messageData.state; + this.emit('debug', `Unknown power state: ${this.screenState}`); break; } - this.emit('powerState', this.power, this.pixelRefresh, this.screenState, this.tvScreenState); + this.emit('powerState', this.power, this.screenState); const disconnect = !this.power ? socket.emit('powerOff') : false; //emit screen saver as appId - const emitAppId = this.tvScreenState === 'Screen Saver' ? this.emit('currentApp', 'com.webos.app.screensaver') : this.emit('currentApp', this.appId); + const emitAppId = this.screenState === 'Screen Saver' ? this.emit('currentApp', 'com.webos.app.screensaver') : this.emit('currentApp', this.appId); //restFul this.emit('restFul', 'power', messageData); @@ -374,8 +428,8 @@ class LgWebOsSocket extends EventEmitter { case 'error': if (this.webOS < 3.0) { this.power = this.socketConnected; - this.tvScreenState = this.socketConnected ? 'Active' : 'Suspend'; - this.emit('powerState', this.power, this.pixelRefresh, this.screenState, this.tvScreenState); + this.screenState = this.socketConnected ? 'Active' : 'Suspend'; + this.emit('powerState', this.power, this.screenState); const disconnect = !this.power ? socket.emit('powerOff') : false; const debug1 = debugLog ? this.emit('debug', `Installed system webOS: ${this.webOS}`) : false; } else { @@ -392,7 +446,6 @@ class LgWebOsSocket extends EventEmitter { case 'response': const debug = debugLog ? this.emit('debug', `App: ${stringifyMessage}`) : false; const appId = messageData.appId ?? false; - if (!appId) { return; }; @@ -425,15 +478,13 @@ class LgWebOsSocket extends EventEmitter { const soundOutputExist = volumeStatusKeys ? volumeStatusKeys.includes('soundOutput') : false; //data - const audioOutput = scenarioExist ? messageData.scenario : soundOutputExist ? messageData.volumeStatus.soundOutput : 'Unknown'; const volume = messageData.volume ?? -1; const mute = messageData.mute === true; - if (volume === -1) { return; };; - this.emit('audioState', volume, mute, audioOutput); + this.emit('audioState', volume, mute); this.volume = volume; this.mute = mute @@ -458,7 +509,6 @@ class LgWebOsSocket extends EventEmitter { const channelId = messageData.channelId ?? false; const channelName = messageData.channelName; const channelNumber = messageData.channelNumber; - if (!channelId) { return; }; @@ -483,11 +533,18 @@ class LgWebOsSocket extends EventEmitter { switch (messageType) { case 'response': const debug = debugLog ? this.emit('debug', `Picture: ${stringifyMessage}`) : false; - const brightness = messageData.settings.brightness; - const backlight = messageData.settings.backlight; - const contrast = messageData.settings.contrast; - const color = messageData.settings.color; + const brightness = messageData.settings.brightness ?? this.brightness; + const backlight = messageData.settings.backlight ?? this.backlight; + const contrast = messageData.settings.contrast ?? this.contrast; + const color = messageData.settings.color ?? this.color; const pictureMode = 3; + + this.brightness = brightness; + this.backlight = backlight; + this.contrast = contrast; + this.color = color; + this.pictureMode = pictureMode; + this.emit('pictureSettings', brightness, backlight, contrast, color, pictureMode, this.power); //restFul @@ -509,7 +566,6 @@ class LgWebOsSocket extends EventEmitter { case 'response': const debug = debugLog ? this.emit('debug', `Sound mode: ${stringifyMessage}`) : false; const soundMode = messageData.settings.soundMode ?? false; - if (!soundMode) { return; } @@ -531,42 +587,77 @@ class LgWebOsSocket extends EventEmitter { break; }; break; - default: + case this.soundOutputId: switch (messageType) { case 'response': - const debug1 = debugLog ? this.emit('debug', `Received response message: ${stringifyMessage}`) : false; + const debug = debugLog ? this.emit('debug', `Sound output: ${stringifyMessage}`) : false; + const soundOutput = messageData.soundOutput ?? false; + if (!soundOutput) { + return; + } + + this.emit('soundOutput', soundOutput, this.power); + this.soundOutput = soundOutput; + + //restFul + this.emit('restFul', 'soundoutput', messageData); + + //mqtt + this.emit('mqtt', 'Sound Output', messageData); + break; case 'error': - const debug2 = debugLog ? this.emit('debug', `Received error message: ${stringifyMessage}`) : false; + const debug1 = debugLog ? this.emit('debug', `Sound output error: ${stringifyMessage}`) : false; break; default: - const debug3 = debugLog ? this.emit('debug', `Received message type: ${messageType}, id: ${messageId}, data: ${stringifyMessage}`) : false; + const debug2 = debugLog ? this.emit('debug', this.emit('debug', `Sound output received message, type: ${messageType}, id: ${messageId}, data: ${stringifyMessage}`)) : false; break; }; break; + case this.alertCid: + const debug = debugLog ? this.emit('debug', `Alert: ${stringifyMessage}`) : false; + const alertId = messageData.alertId ?? false; + if (!alertId) { + return; + } + + const closeAlert = this.webOS >= 4.0 ? await this.send('request', CONSTANTS.ApiUrls.CloseAletrt, { alertId: alertId }) : await this.send('button', undefined, { name: 'ENTER' }); + break; + case this.toastCid: + const debug1 = debugLog ? this.emit('debug', `Toast: ${stringifyMessage}`) : false; + const toastId = messageData.toastId ?? false; + if (!toastId) { + return; + } + + const closeToast = this.webOS >= 4.0 ? await this.send('request', CONSTANTS.ApiUrls.CloseToast, { toastId: toastId }) : await this.send('button', undefined, { name: 'ENTER' }); + break; + default: + const debug3 = debugLog ? this.emit('debug', `Received message type: ${messageType}, id: ${messageId}, data: ${stringifyMessage}`) : false; + break; }; }).on('error', (error) => { const debug = debugLog ? this.emit('debug', `Socket connect error: ${error}.`) : false; socket.emit('disconnect'); }).on('powerOff', async () => { //update TV state - this.emit('powerState', false, this.pixelRefresh, this.screenState, this.tvScreenState); + this.emit('powerState', false, this.screenState); this.emit('audioState', this.volume, true); this.emit('pictureSettings', 0, 0, 0, 0, 3, false); this.emit('soundMode', this.soundMode, false); + this.emit('soundOutput', this.soundOutput, false); }).on('disconnect', async () => { const message = this.socketConnected ? this.emit('message', 'Socket disconnected.') : false; this.socketConnected = false; this.cidCount = 0; this.power = false; - this.pixelRefresh = false; - this.screenState = false; - this.tvScreenState = 'Suspend'; + this.screenState = 'Suspend'; //update TV state - this.emit('powerState', false, false, false, 'Suspend'); + this.emit('powerState', false, 'Suspend'); this.emit('audioState', this.volume, true); this.emit('pictureSettings', 0, 0, 0, 0, 3, false); this.emit('soundMode', this.soundMode, false); + this.emit('soundOutput', this.soundOutput, false); }); } @@ -670,26 +761,11 @@ class LgWebOsSocket extends EventEmitter { saveInputs(path, appsList) { return new Promise(async (resolve, reject) => { try { - const inputs = this.getInputsFromDevice ? appsList : this.inputs; - const tempInputs = []; - const inputsArr = []; - for (const input of inputs) { - const name = input.title; - const reference = input.id; - const mode = this.getInputsFromDevice ? 0 : input.mode; - const obj = { - 'name': name, - 'reference': reference, - 'mode': mode - } - tempInputs.push(obj); - }; - //chack duplicated inputs - for (const input of tempInputs) { + const inputsArr = []; + for (const input of appsList) { const inputName = input.name; const inputReference = input.reference; - const inputMode = input.mode ?? 0; const duplicatedInput = inputsArr.some(input => input.reference === inputReference); const filterSystemApps = this.filterSystemApps ? CONSTANTS.SystemApps.includes(inputReference) : false; const push = inputName && inputReference && !filterSystemApps && !duplicatedInput ? inputsArr.push(input) : false; @@ -735,6 +811,8 @@ class LgWebOsSocket extends EventEmitter { await this.send('subscribe', CONSTANTS.ApiUrls.GetCurrentChannel, undefined, this.currentChannelId); this.audioStateId = await this.getCid(); await this.send('subscribe', CONSTANTS.ApiUrls.GetAudioStatus, undefined, this.audioStateId); + this.soundOutputId = await this.getCid(); + await this.send('subscribe', CONSTANTS.ApiUrls.GetSoundOutput, undefined, this.soundOutputId); if (this.webOS >= 4.0) { const payload = { @@ -760,21 +838,7 @@ class LgWebOsSocket extends EventEmitter { }); } - getCid() { - return new Promise((resolve, reject) => { - try { - this.cidCount++; - const randomPart = (`0000000${Math.floor(Math.random() * 0xFFFFFFFF).toString(16)}`).slice(-8); - const counterPart = (`000${(this.cidCount).toString(16)}`).slice(-4); - const cid = randomPart + counterPart; - resolve(cid); - } catch (error) { - reject(error); - } - }); - } - - getCids(type) { + getCid(type) { return new Promise((resolve, reject) => { try { switch (type) { @@ -793,6 +857,22 @@ class LgWebOsSocket extends EventEmitter { case 'Picture': resolve(this.pictureSettingsId); break; + case 'SoundMode': + resolve(this.soundModeId); + break; + case 'SoundOutput': + resolve(this.soundOutputId); + break; + case 'ExternalInputList': + resolve(this.externalInoutListId); + break; + default: + this.cidCount++; + const randomPart = (`0000000${Math.floor(Math.random() * 0xFFFFFFFF).toString(16)}`).slice(-8); + const counterPart = (`000${(this.cidCount).toString(16)}`).slice(-4); + const cid = randomPart + counterPart; + resolve(cid); + break; } } catch (error) { reject(error); @@ -800,10 +880,13 @@ class LgWebOsSocket extends EventEmitter { }); } - send(type, uri, payload, cid) { + send(type, uri, payload, cid, title, message) { return new Promise(async (resolve, reject) => { try { + payload = payload ?? {}; + cid = cid ?? await this.getCid(); + switch (type) { case 'button': if (!this.specjalizedSocketConnected) { @@ -812,9 +895,48 @@ class LgWebOsSocket extends EventEmitter { const keyValuePairs = Object.entries(payload).map(([key, value]) => `${key}:${value}`); keyValuePairs.unshift(`type:${type}`); - const message = keyValuePairs.join('\n') + '\n\n'; + const message0 = keyValuePairs.join('\n') + '\n\n'; - this.specializedSocket.send(message); + this.specializedSocket.send(message0); + resolve(); + break; + case 'alert': + if (!this.socketConnected) { + reject('Socket not connected.'); + }; + + this.alertCid = cid; + const buttons = [{ label: 'Ok', focus: true, buttonType: 'ok', onClick: uri, params: payload }]; + const onClose = { uri: uri, params: payload }; + const onFail = { uri: uri, params: payload }; + const payload1 = { title: title, message: message, modal: true, buttons: buttons, onclose: onClose, onfail: onFail, type: 'confirm', isSysReq: true }; + const data1 = { + id: cid, + type: 'request', + uri: CONSTANTS.ApiUrls.CreateAlert, + payload: payload1 + }; + + const message1 = JSON.stringify(data1); + this.socket.send(message1); + resolve(); + break; + case 'toast': + if (!this.socketConnected) { + reject('Socket not connected.'); + }; + + this.toastCid = cid; + const payload2 = { message: message, iconData: null, iconExtension: null, onClick: payload }; + const data2 = { + id: cid, + type: 'request', + uri: CONSTANTS.ApiUrls.CreateToast, + payload: payload2 + }; + + const message2 = JSON.stringify(data2); + this.socket.send(message2); resolve(); break; default: @@ -822,16 +944,15 @@ class LgWebOsSocket extends EventEmitter { reject('Socket not connected.'); }; - cid = cid === undefined ? await this.getCid() : cid; - const data = { + const data3 = { id: cid, type: type, uri: uri, payload: payload }; - const message1 = JSON.stringify(data); - this.socket.send(message1); + const message3 = JSON.stringify(data3); + this.socket.send(message3); resolve(); break }; diff --git a/src/restful.js b/src/restful.js index 35bc25d..fbbff81 100644 --- a/src/restful.js +++ b/src/restful.js @@ -18,7 +18,9 @@ class RestFul extends EventEmitter { currentapp: 'This data is not available in your system at this time.', currentchannel: 'This data is not available in your system at this time.', picturesettings: 'This data is not available in your system at this time.', - soundmode: 'This data is not available in your system at this time.' + soundmode: 'This data is not available in your system at this time.', + soundoutput: 'This data is not available in your system at this time.', + externalinputlist: 'This data is not available in your system at this time.' }; this.connect(); }; @@ -37,6 +39,8 @@ class RestFul extends EventEmitter { restFul.get('/currentchannel', (req, res) => { res.json(this.restFulData.currentchannel) }); restFul.get('/picturesettings', (req, res) => { res.json(this.restFulData.picturesettings) }); restFul.get('/soundmode', (req, res) => { res.json(this.restFulData.soundmode) }); + restFul.get('/soundoutput', (req, res) => { res.json(this.restFulData.soundoutput) }); + restFul.get('/soundoutput', (req, res) => { res.json(this.restFulData.externalinputlist) }); restFul.listen(this.restFulPort, () => { this.emit('connected', `RESTful started on port: ${this.restFulPort}`) @@ -79,6 +83,12 @@ class RestFul extends EventEmitter { case 'soundmode': this.restFulData.soundmode = data; break; + case 'soundoutput': + this.restFulData.soundoutput = data; + break; + case 'externalinputlist': + this.restFulData.externalinputlist = data; + break; default: this.emit('debug', `RESTFul update unknown path: ${path}, data: ${data}`) break;