From b01e27e9fe8e58f3411814d89adf9debc72f6e7e Mon Sep 17 00:00:00 2001 From: Petar Hristov Date: Thu, 25 Apr 2024 21:18:19 +0200 Subject: [PATCH 01/14] New: Added support for Samsung vendor specific media input sources --- config.schema.json | 10 +++++++--- lib/services/input.js | 2 +- lib/smartthings.js | 8 +++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/config.schema.json b/config.schema.json index 60d69f4..c74e2a6 100644 --- a/config.schema.json +++ b/config.schema.json @@ -158,7 +158,7 @@ }, "input": { "type": "string", - "enum": ["digitalTv", "HDMI1", "HDMI2", "HDMI3", "HDMI4", "HDMI5", "HDMI6", "USB", "AM", "FM"] + "enum": ["digitalTv", "HDMI1", "HDMI2", "HDMI3", "HDMI4", "HDMI5", "HDMI6", "Display Port", "USB", "USB-C", "AM", "FM"] }, "sleep": { "type": "integer" @@ -254,7 +254,7 @@ }, { "key": "devices[].mac", - "title": "MAC Address", + "title": "Network MAC Address", "placeholder": "A0:B1:C2:D3:E4:F5" } ] @@ -327,7 +327,7 @@ "condition": { "functionBody": "return model.devices[arrayIndices[0]].inputs[arrayIndices[1]].type == 'input';" }, - "enum": ["digitalTv", "HDMI1", "HDMI2", "HDMI3", "HDMI4", "HDMI5", "HDMI6", "USB", "AM", "FM"], + "enum": ["digitalTv", "HDMI1", "HDMI2", "HDMI3", "HDMI4", "HDMI5", "HDMI6", "Display Port", "USB", "USB-C", "AM", "FM"], "titleMap": { "digitalTv": "Live TV", "HDMI1": "HDMI 1", @@ -336,7 +336,9 @@ "HDMI4": "HDMI 4", "HDMI5": "HDMI 5", "HDMI6": "HDMI 6", + "Display Port": "Display Port", "USB": "USB", + "USB-C": "USB-C", "AM": "AM", "FM": "FM" } @@ -391,6 +393,8 @@ "titleMap": { "digitalTv": "Live TV", "USB": "USB", + "USB-C": "USB-C", + "Display Port": "Display Port", "HDMI1": "HDMI 1", "HDMI2": "HDMI 2", "HDMI3": "HDMI 3", diff --git a/lib/services/input.js b/lib/services/input.js index bfbea9f..f1797b5 100644 --- a/lib/services/input.js +++ b/lib/services/input.js @@ -37,7 +37,7 @@ module.exports = class Input { return Hap.Characteristic.InputSourceType.TUNER; } - if (this.config.value == 'USB') { + if (this.config.value && this.config.value.startsWith('USB')) { return Hap.Characteristic.InputSourceType.USB; } diff --git a/lib/smartthings.js b/lib/smartthings.js index d145832..63d1022 100644 --- a/lib/smartthings.js +++ b/lib/smartthings.js @@ -66,7 +66,13 @@ module.exports = class SmartThings { } setInputSource(value) { - return this.sendCommands({component: 'main', capability: 'mediaInputSource', command: 'setInputSource', arguments: [value]}); + // Try to set input source with Samsung-specific vendor extended capability (used in e.g. smart monitors) + return this.sendCommands({component: 'main', capability: 'samsungvd.mediaInputSource', command: 'setInputSource', arguments: [value]}) + .catch((error) => { + // Catch errors (422) potentially due to unsupported capability and fallback to standard capability (used in most Samsung TVs) + this.device.log.debug('Failed to set input source with Samsung-specific vendor extended capability, falling back to standard capability.', error); + return this.sendCommands({component: 'main', capability: 'mediaInputSource', command: 'setInputSource', arguments: [value]}); + }); } setPictureMode(value) { From 5df3769eee8746370829ae6ecc4f2332a19ca916 Mon Sep 17 00:00:00 2001 From: Petar Hristov Date: Thu, 25 Apr 2024 23:04:57 +0200 Subject: [PATCH 02/14] Fix: Formatting --- lib/smartthings.js | 68 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/lib/smartthings.js b/lib/smartthings.js index 63d1022..846b3ca 100644 --- a/lib/smartthings.js +++ b/lib/smartthings.js @@ -22,12 +22,16 @@ module.exports = class SmartThings { this.api_device = this.api_devices + this.device_id; this.api_status = this.api_device + '/states'; this.api_command = this.api_device + '/commands'; - this.api_headers = {'Authorization': 'Bearer ' + this.api_key}; + this.api_headers = { 'Authorization': 'Bearer ' + this.api_key }; // Emit init event setTimeout(() => this.device.emit('smartthings.init')); } + /** + * Retrieves the status of the device. + * @returns {Promise} A promise that resolves to an object containing the device status. + */ getStatus() { return this.device.cache.get(`st-status`, () => this.refresh() .then(() => this._send(this.api_status)) @@ -44,6 +48,10 @@ module.exports = class SmartThings { return this.getStatus().then(response => response.pictureMode); } + /** + * Retrieves the current input source of the TV. + * @returns {Promise} A promise that resolves with the input source of the TV. + */ getInputSource() { return this.getStatus().then(response => { // Some TVs report inputSource as dtv, transform it to digitalTv @@ -61,32 +69,65 @@ module.exports = class SmartThings { }); } + /** + * Refreshes the main component's capability. + * @returns {Promise} A promise that resolves when the refresh command is sent. + */ refresh() { - return this.sendCommands({component: 'main', capability: 'refresh', command: 'refresh'}); + return this.sendCommands({ component: 'main', capability: 'refresh', command: 'refresh' }); } + /** + * Sets the input source for the device. + * + * @param {string} value - The input source value to set. + * @returns {Promise} A promise that resolves when the input source is set successfully. + */ setInputSource(value) { // Try to set input source with Samsung-specific vendor extended capability (used in e.g. smart monitors) - return this.sendCommands({component: 'main', capability: 'samsungvd.mediaInputSource', command: 'setInputSource', arguments: [value]}) + return this.sendCommands({ component: 'main', capability: 'samsungvd.mediaInputSource', command: 'setInputSource', arguments: [value] }) .catch((error) => { // Catch errors (422) potentially due to unsupported capability and fallback to standard capability (used in most Samsung TVs) this.device.log.debug('Failed to set input source with Samsung-specific vendor extended capability, falling back to standard capability.', error); - return this.sendCommands({component: 'main', capability: 'mediaInputSource', command: 'setInputSource', arguments: [value]}); + return this.sendCommands({ component: 'main', capability: 'mediaInputSource', command: 'setInputSource', arguments: [value] }); }); } + /** + * Sets the picture mode of the device. + * + * @param {string} value - The value representing the picture mode. + * @returns {Promise} A promise that resolves when the command is sent successfully. + */ setPictureMode(value) { - return this.sendCommands({component: 'main', capability: 'custom.picturemode', command: 'setPictureMode', arguments: [value]}); + return this.sendCommands({ component: 'main', capability: 'custom.picturemode', command: 'setPictureMode', arguments: [value] }); } + /** + * Sets the TV channel. + * + * @param {string} value - The channel value to set. + * @returns {Promise} A promise that resolves when the command is sent. + */ setTvChannel(value) { - return this.sendCommands({component: 'main', capability: 'tvChannel', command: 'setTvChannel', arguments: [value + '']}); + return this.sendCommands({ component: 'main', capability: 'tvChannel', command: 'setTvChannel', arguments: [value + ''] }); } + /** + * Sets the volume of the audio. + * + * @param {number} value - The volume value to set. + * @returns {Promise} A promise that resolves when the volume is set. + */ setVolume(value) { - return this.sendCommands({component: 'main', capability: 'audioVolume', command: 'setVolume', arguments: [parseInt(value)]}); + return this.sendCommands({ component: 'main', capability: 'audioVolume', command: 'setVolume', arguments: [parseInt(value)] }); } + /** + * Sends commands to the SmartThings API. + * @param {Array|Object} commands - The commands to send. Can be an array of commands or a single command object. + * @returns {Promise} A promise that resolves with the response from the API. + */ sendCommands(commands) { if (!Array.isArray(commands)) { commands = [commands]; @@ -95,8 +136,15 @@ module.exports = class SmartThings { return this._send(this.api_command, { commands: commands }, 'post'); - } + } + /** + * Sends a request to the specified endpoint with the provided data using the specified HTTP method. + * @param {string} endpoint - The URL endpoint to send the request to. + * @param {object} data - The data to send in the request body (optional). + * @param {string} method - The HTTP method to use for the request (optional, defaults to 'get'). + * @returns {Promise} - A promise that resolves with the response data or rejects with an error. + */ _send(endpoint, data, method) { this._validate(); @@ -131,6 +179,10 @@ module.exports = class SmartThings { }); } + /** + * Validates the API key and device ID are set. + * @throws {SmartThingsNotSet} If the API key or device ID is not set. + */ _validate() { if (this.api_key && this.device_id) return; From cb986b61ea79740b89cd2f422554e4852b626c71 Mon Sep 17 00:00:00 2001 From: Petar Hristov Date: Thu, 25 Apr 2024 23:31:21 +0200 Subject: [PATCH 03/14] Fix: Added some more documentation --- lib/methods/base.js | 51 ++++++++++++++++++++++++++------------------- lib/remote.js | 4 ++-- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/lib/methods/base.js b/lib/methods/base.js index a44722a..b97bf85 100644 --- a/lib/methods/base.js +++ b/lib/methods/base.js @@ -19,17 +19,26 @@ module.exports = class Base { this.cache = device.cache; } + /** + * Cleans up resources or state. + */ destroy() { this.destroyed = true; } + /** + * Simulates a pairing delay. + * @return {Promise} + */ pair() { return utils.delay(500); } /** - * Get state of TV with cache - * @return {Cache} + * Retrieves the TV's power state by first attempting a network ping. + * If the ping succeeds, it validates the power state via HTTP. + * If the ping fails, it assumes the TV is off, unless previously recorded as on. + * @return {Promise} Resolves with true if on, false if off. */ getState() { return this.cache.get('state', () => this.getStatePing().then(status => { @@ -44,8 +53,8 @@ module.exports = class Base { } /** - * Get state of TV by sending a Ping - * @return {Promise} + * Checks TV availability via network ping. + * @return {Promise} */ getStatePing() { return isPortReachable(8001, { @@ -55,9 +64,9 @@ module.exports = class Base { } /** - * Get state of TV from PowerState response - * @param {boolean} fallback - * @return {Promise} + * Fetches the TV's power state via HTTP if ping succeeds. + * @param {boolean} fallback - Fallback value if HTTP request fails. + * @return {Promise} */ getStateHttp(fallback = false) { return this.getInfo().then(data => { @@ -71,8 +80,8 @@ module.exports = class Base { } /** - * Turn the TV On - * @return {Promise} + * Powers on the TV. Uses Wake-on-LAN if necessary. + * @return {Promise} */ async setStateOn() { // If TV is in Sleep mode just send command @@ -90,16 +99,16 @@ module.exports = class Base { } /** - * Turn the TV Off - * @return {Promise} + * Powers off the TV. + * @return {Promise} */ setStateOff() { return this.click('KEY_POWER'); } /** - * Get TV informations - * @return {Promise} + * Retrieves device information from the TV's API. + * @return {Promise} */ getInfo() { return fetch(`http://${this.ip}:8001/api/v2/`, { @@ -110,9 +119,9 @@ module.exports = class Base { } /** - * Get Application Informations - * @param {Number} appId - * @return {Promise} + * Retrieves information about a specific application. + * @param {number} appId - The application ID. + * @return {Promise} */ getApplication(appId) { return fetch(`http://${this.ip}:8001/api/v2/applications/${appId}`, { @@ -122,9 +131,9 @@ module.exports = class Base { } /** - * Launch Application - * @param {Number} appId - * @return {Promise} + * Launches an application on the TV. + * @param {number} appId - The application ID to launch. + * @return {Promise} */ startApplication(appId) { return fetch(`http://${this.ip}:8001/api/v2/applications/${appId}`, { @@ -135,8 +144,8 @@ module.exports = class Base { } /** - * Encode TV name to base64 - * @return {string} + * Encodes the TV name to base64. + * @return {string} The encoded TV name. */ _encodeName() { return new Buffer.from(this.name).toString('base64'); diff --git a/lib/remote.js b/lib/remote.js index da0681c..e1ebef6 100644 --- a/lib/remote.js +++ b/lib/remote.js @@ -63,7 +63,7 @@ module.exports = class Remote { let method = MethodWSS; - switch(this.device.config.method) { + switch (this.device.config.method) { case 'ws': method = MethodWS; break; @@ -339,7 +339,7 @@ module.exports = class Remote { if (split[1]) { if (/^.*\*[0-9]*[.]?[0-9]+s$/.test(cmd)) { - return {key: split[0], time: parseFloat(split[1])}; + return { key: split[0], time: parseFloat(split[1]) }; } return Array(parseInt(split[1])).fill(split[0]); From dee262faf3babed1ba3f449e013d241853a5f78c Mon Sep 17 00:00:00 2001 From: Petar Hristov Date: Thu, 25 Apr 2024 23:54:07 +0200 Subject: [PATCH 04/14] New: Opt-in for checking power state over SmartThings API --- config.schema.json | 27 +++++++++++------- lib/device.js | 3 +- lib/remote.js | 7 +++++ lib/smartthings.js | 69 +++++++++++++++++++++++++++++++--------------- 4 files changed, 73 insertions(+), 33 deletions(-) diff --git a/config.schema.json b/config.schema.json index c74e2a6..fe295b3 100644 --- a/config.schema.json +++ b/config.schema.json @@ -82,7 +82,7 @@ "wait_time": {"type": "integer"}, "api_key": {"type": "string"}, "device_id": {"type": "string"}, - + "use_smartthings_for_power_state": {"type": "boolean"}, "name": { "type": "string", "required": true @@ -240,8 +240,8 @@ "default": "Bedroom TV" }, { - "type": "help", - "helpvalue": "

Make sure that your device have a static IP address. You can set it from your router admin interface!

" + "type": "help", + "helpvalue": "

Make sure that your device has a static IP address. You can set it from your router admin interface!

" }, { "type": "flex", @@ -264,8 +264,8 @@ "title": "SmartThings API", "items": [ { - "type": "help", - "helpvalue": "

SmartThings API is optional. You will need to set these parameters only if you want to use inputs! Please follow this tutorial.

" + "type": "help", + "helpvalue": "

SmartThings API is optional but highly recommended for more reliable power state (see \"Use SmartThings for Power State\") detection and input management. If provided, it will be used for enhanced functionality such as fetching the power state more effectively and managing inputs. Please follow this tutorial to set it up.

" }, { "key": "devices[].api_key", @@ -274,7 +274,14 @@ { "key": "devices[].device_id", "title": "Device ID" - } + }, + { + "key": "devices[].use_smartthings_for_power_state", + "type": "boolean", + "default": false, + "title": "Use SmartThings for Power State", + "description": "Enable to use the SmartThings API to check the power state of the TV. This can provide more accurate results. Ensure you have provided a valid API Key and Device ID." + } ] }, { @@ -285,7 +292,7 @@ "items": [ { "type": "help", - "helpvalue": "

By default no inputs are set. Use this section to set your own inputs. You can find more informations on our documentation.

" + "helpvalue": "

By default, no inputs are set. Use this section to configure your own inputs. For more information, please refer to our documentation.

" }, { "type": "array", @@ -357,7 +364,7 @@ "items": [ { "type": "help", - "helpvalue": "

This section give you the option to create custom switches (separated accessories) that make specific actions. You can find more informations on our documentation.

" + "helpvalue": "

This section allows you to create custom switches (separate accessories) that perform specific actions. For more information, please visit our documentation.

" }, { "type": "array", @@ -499,7 +506,7 @@ "key": "devices[].options", "title": "Options", "titleMap": { - "Switch.DeviceName.Disable": "Disable prepending device name on custom switches", + "Switch.DeviceName.Disable": "Disable prepending the device name on custom switches", "Frame.RealPowerMode": "Display and control Real Power with Main Acccessory (for Frame TVs)", "Frame.ArtSwitch.Disable": "Disable Art Switch (for Frame TVs)", "Frame.PowerSwitch.Disable": "Disable Power Switch (for Frame TVs)" @@ -515,7 +522,7 @@ "items": [ { "type": "help", - "helpvalue": "

You don't have to edit this section unless you want to change the default commands for Remote Control buttons. You can find more informations on our documentation.

", + "helpvalue": "

You do not need to edit this section unless you want to change the default commands for Remote Control buttons. For more information, please refer to our documentation.

", "flex": "1 1 100%" }, { diff --git a/lib/device.js b/lib/device.js index 946444a..b65db25 100644 --- a/lib/device.js +++ b/lib/device.js @@ -23,7 +23,8 @@ module.exports = class Device extends EventEmitter { wol : {}, options : [], api_key : null, - device_id : null + device_id : null, + use_smartthings_for_power_state : false }, Platform.config, config diff --git a/lib/remote.js b/lib/remote.js index e1ebef6..3ffdd75 100644 --- a/lib/remote.js +++ b/lib/remote.js @@ -119,6 +119,13 @@ module.exports = class Remote { if (this.turningOff !== null) { return false; } if (this.standbyMode !== null) { return false; } + // If SmartThings API is available, use it to get the power state reliably, otherwise fall back to default API method + if (this.smartthings.available && this.device.config.use_smartthings_for_power_state) { + const powerState = await this.smartthings.getSwitchState(); + if (powerState !== null) { return powerState; } + this.device.log.debug("SmartThings API call failed, falling back to default API method."); + } + return await this.api.getState(); } diff --git a/lib/smartthings.js b/lib/smartthings.js index 846b3ca..4c9e8d1 100644 --- a/lib/smartthings.js +++ b/lib/smartthings.js @@ -22,6 +22,7 @@ module.exports = class SmartThings { this.api_device = this.api_devices + this.device_id; this.api_status = this.api_device + '/states'; this.api_command = this.api_device + '/commands'; + this.api_switch = this.api_device + '/components/main/capabilities/switch/status'; this.api_headers = { 'Authorization': 'Bearer ' + this.api_key }; // Emit init event @@ -36,12 +37,12 @@ module.exports = class SmartThings { return this.device.cache.get(`st-status`, () => this.refresh() .then(() => this._send(this.api_status)) .catch(() => ({}) - ), 2500).then(response => ({ - pictureMode: response.main.pictureMode, - tvChannel: response.main.tvChannel, - tvChannelName: response.main.tvChannelName, - inputSource: response.main.inputSource - })); + ), 2500).then(response => ({ + pictureMode: response.main.pictureMode, + tvChannel: response.main.tvChannel, + tvChannelName: response.main.tvChannelName, + inputSource: response.main.inputSource + })); } getPictureMode() { @@ -69,6 +70,30 @@ module.exports = class SmartThings { }); } + /** + * Fetches the current state of the switch capability from the SmartThings API. + * Returns a boolean if successful, or null if an error occurs. + * @returns {Promise} + */ + getSwitchState() { + return this._send(this.api_switch) + .then(response => { + // Check the response for the switch state + if (response && response.switch && typeof response.switch.value === 'string') { + this.device.log.debug(`Switch state fetched: ${response.switch.value}`); + // Return true if 'on', otherwise false + return response.switch.value === 'on'; + } + // If response structure is not as expected, log and return null + throw new SmartThingsResponse('Unexpected response structure when fetching switch (power) state'); + }) + .catch(error => { + // Log any errors and return null + this.device.log.debug(`Error fetching switch state: ${error}`); + return new Promise(null); + }); + } + /** * Refreshes the main component's capability. * @returns {Promise} A promise that resolves when the refresh command is sent. @@ -136,7 +161,7 @@ module.exports = class SmartThings { return this._send(this.api_command, { commands: commands }, 'post'); - } + } /** * Sends a request to the specified endpoint with the provided data using the specified HTTP method. @@ -157,25 +182,25 @@ module.exports = class SmartThings { body: JSON.stringify(data), headers: this.api_headers }) - .then(response => { - const contentType = response.headers.get('content-type'); + .then(response => { + const contentType = response.headers.get('content-type'); - if (!contentType || !contentType.includes('application/json')) { - throw new SmartThingsResponse(`${response.status} - ${response.statusText}`); - } + if (!contentType || !contentType.includes('application/json')) { + throw new SmartThingsResponse(`${response.status} - ${response.statusText}`); + } - return response.json(); - }) - .then(response => { - this.device.log.debug(JSON.stringify(response)); + return response.json(); + }) + .then(response => { + this.device.log.debug(JSON.stringify(response)); - if (response.error) { - reject(response.error); - } + if (response.error) { + reject(response.error); + } - resolve(response); - }) - .catch(error => reject(error)); + resolve(response); + }) + .catch(error => reject(error)); }); } From 98963877e7043e805e1d34b2b76ff4faa163cbb8 Mon Sep 17 00:00:00 2001 From: Petar Hristov Date: Sat, 27 Apr 2024 21:21:01 +0200 Subject: [PATCH 05/14] New: Add caching to smart things API power state request --- lib/remote.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/remote.js b/lib/remote.js index 3ffdd75..2e7f090 100644 --- a/lib/remote.js +++ b/lib/remote.js @@ -121,11 +121,14 @@ module.exports = class Remote { // If SmartThings API is available, use it to get the power state reliably, otherwise fall back to default API method if (this.smartthings.available && this.device.config.use_smartthings_for_power_state) { - const powerState = await this.smartthings.getSwitchState(); - if (powerState !== null) { return powerState; } - this.device.log.debug("SmartThings API call failed, falling back to default API method."); + return this.cache.get('smartthings-power-state', () => this.smartthings.getSwitchState(), 2500) + .then(powerState => { + if (powerState !== null) { return powerState; } + this.device.log.debug("SmartThings API call failed, falling back to default API method."); + return this.api.getState(); + }); } - + return await this.api.getState(); } From 7b2631b9f968fee775b8fc9f5312bde433a4ab93 Mon Sep 17 00:00:00 2001 From: Petar Hristov Date: Sat, 27 Apr 2024 21:22:58 +0200 Subject: [PATCH 06/14] Make new alpha version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4a863f9..b10ee57 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "homebridge-samsung-tizen", "displayName": "Homebridge Samsung Tizen", - "version": "5.2.7", + "version": "5.4.0-alpha.1", "description": "Homebridge plugin for Samsung TV's with Tizen OS", "main": "index.js", "directories": { From 79dd07e696677406f34d1df6dc4d876d62920b38 Mon Sep 17 00:00:00 2001 From: Petar Hristov Date: Sat, 27 Apr 2024 21:50:06 +0200 Subject: [PATCH 07/14] Added more debug logs --- lib/methods/base.js | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/methods/base.js b/lib/methods/base.js index b97bf85..9855f49 100644 --- a/lib/methods/base.js +++ b/lib/methods/base.js @@ -42,7 +42,9 @@ module.exports = class Base { */ getState() { return this.cache.get('state', () => this.getStatePing().then(status => { + this.device.log.debug(`Ping status: ${status}`); if (status && this.device.storage.powerstate !== false) { + this.device.log.debug('Ping succeeded, fetching power state via HTTP...'); return this.cache.get('state-http', () => this.getStateHttp(status), 2500); } else { this.cache.forget('state-http'); @@ -57,6 +59,7 @@ module.exports = class Base { * @return {Promise} */ getStatePing() { + this.device.log.debug(`Pinging ${this.ip} with timeout ${this.timeout}ms.`); return isPortReachable(8001, { host: this.ip, timeout: this.timeout @@ -69,8 +72,10 @@ module.exports = class Base { * @return {Promise} */ getStateHttp(fallback = false) { + this.device.log.debug(`Fetching power state from ${this.ip} local TV API.`); return this.getInfo().then(data => { if (data.device && data.device.PowerState) { + this.device.log.debug(`Power state: ${data.device.PowerState}`); return data.device.PowerState == 'on'; } diff --git a/package.json b/package.json index b10ee57..e2a9c21 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "homebridge-samsung-tizen", "displayName": "Homebridge Samsung Tizen", - "version": "5.4.0-alpha.1", + "version": "5.4.0-alpha.2", "description": "Homebridge plugin for Samsung TV's with Tizen OS", "main": "index.js", "directories": { From 0b522519ba54ee2992e941c33ebcfacb3f3e2848 Mon Sep 17 00:00:00 2001 From: Petar Hristov Date: Sat, 27 Apr 2024 22:06:58 +0200 Subject: [PATCH 08/14] Doc --- lib/methods/base.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/methods/base.js b/lib/methods/base.js index 9855f49..a869f3d 100644 --- a/lib/methods/base.js +++ b/lib/methods/base.js @@ -67,7 +67,7 @@ module.exports = class Base { } /** - * Fetches the TV's power state via HTTP if ping succeeds. + * Fetches the TV's power state via the local TV's API. * @param {boolean} fallback - Fallback value if HTTP request fails. * @return {Promise} */ From 1b6e511d1c876743f828f344d4798dc38882b396 Mon Sep 17 00:00:00 2001 From: Petar Hristov Date: Sat, 27 Apr 2024 22:19:03 +0200 Subject: [PATCH 09/14] Fix: spelling --- config.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.schema.json b/config.schema.json index fe295b3..ce5b61c 100644 --- a/config.schema.json +++ b/config.schema.json @@ -507,7 +507,7 @@ "title": "Options", "titleMap": { "Switch.DeviceName.Disable": "Disable prepending the device name on custom switches", - "Frame.RealPowerMode": "Display and control Real Power with Main Acccessory (for Frame TVs)", + "Frame.RealPowerMode": "Display and control Real Power with Main Accessory (for Frame TVs)", "Frame.ArtSwitch.Disable": "Disable Art Switch (for Frame TVs)", "Frame.PowerSwitch.Disable": "Disable Power Switch (for Frame TVs)" } From 73f580b2effbc5543376d91f5da0202108fc6b69 Mon Sep 17 00:00:00 2001 From: Petar Hristov Date: Sat, 27 Apr 2024 22:20:59 +0200 Subject: [PATCH 10/14] Fix: Spelling --- lib/methods/base.js | 2 +- package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/methods/base.js b/lib/methods/base.js index a869f3d..5b6ed69 100644 --- a/lib/methods/base.js +++ b/lib/methods/base.js @@ -67,7 +67,7 @@ module.exports = class Base { } /** - * Fetches the TV's power state via the local TV's API. + * Fetches the TV's power state via its local API. * @param {boolean} fallback - Fallback value if HTTP request fails. * @return {Promise} */ diff --git a/package.json b/package.json index e2a9c21..81edcb2 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "homebridge-samsung-tizen", "displayName": "Homebridge Samsung Tizen", - "version": "5.4.0-alpha.2", - "description": "Homebridge plugin for Samsung TV's with Tizen OS", + "version": "5.2.7", + "description": "Homebridge plugin for Samsung TVs with Tizen OS", "main": "index.js", "directories": { "lib": "lib" From 9d17c22ac1cf2a1b6f594fbd994766c60b425ccb Mon Sep 17 00:00:00 2001 From: Petar Hristov Date: Sat, 27 Apr 2024 22:33:44 +0200 Subject: [PATCH 11/14] Refactored --- config.schema.json | 6 +++--- lib/device.js | 2 +- lib/remote.js | 2 +- lib/smartthings.js | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/config.schema.json b/config.schema.json index ce5b61c..84dd1bf 100644 --- a/config.schema.json +++ b/config.schema.json @@ -82,7 +82,7 @@ "wait_time": {"type": "integer"}, "api_key": {"type": "string"}, "device_id": {"type": "string"}, - "use_smartthings_for_power_state": {"type": "boolean"}, + "use_smartthings_power": {"type": "boolean"}, "name": { "type": "string", "required": true @@ -276,11 +276,11 @@ "title": "Device ID" }, { - "key": "devices[].use_smartthings_for_power_state", + "key": "devices[].use_smartthings_power", "type": "boolean", "default": false, "title": "Use SmartThings for Power State", - "description": "Enable to use the SmartThings API to check the power state of the TV. This can provide more accurate results. Ensure you have provided a valid API Key and Device ID." + "description": "Enable to use the SmartThings API to check the power state of the TV. This could provide more accurate power readings. Ensure you have provided a valid API Key and Device ID." } ] }, diff --git a/lib/device.js b/lib/device.js index b65db25..ab693ba 100644 --- a/lib/device.js +++ b/lib/device.js @@ -24,7 +24,7 @@ module.exports = class Device extends EventEmitter { options : [], api_key : null, device_id : null, - use_smartthings_for_power_state : false + use_smartthings_power : false }, Platform.config, config diff --git a/lib/remote.js b/lib/remote.js index 2e7f090..99ab8f7 100644 --- a/lib/remote.js +++ b/lib/remote.js @@ -120,7 +120,7 @@ module.exports = class Remote { if (this.standbyMode !== null) { return false; } // If SmartThings API is available, use it to get the power state reliably, otherwise fall back to default API method - if (this.smartthings.available && this.device.config.use_smartthings_for_power_state) { + if (this.smartthings.available && this.device.config.use_smartthings_power) { return this.cache.get('smartthings-power-state', () => this.smartthings.getSwitchState(), 2500) .then(powerState => { if (powerState !== null) { return powerState; } diff --git a/lib/smartthings.js b/lib/smartthings.js index 4c9e8d1..7acc3ee 100644 --- a/lib/smartthings.js +++ b/lib/smartthings.js @@ -71,11 +71,11 @@ module.exports = class SmartThings { } /** - * Fetches the current state of the switch capability from the SmartThings API. - * Returns a boolean if successful, or null if an error occurs. + * Fetches the current powers state of the device (switch capability) from the SmartThings API. + * Returns a boolean, representing the device's power state, if successful, or null if an error occurs. * @returns {Promise} */ - getSwitchState() { + getPowerState() { return this._send(this.api_switch) .then(response => { // Check the response for the switch state From 22f736d37aa0c753d91f3416c0466c0d7d4d9ca1 Mon Sep 17 00:00:00 2001 From: Petar Hristov Date: Sat, 27 Apr 2024 22:35:04 +0200 Subject: [PATCH 12/14] Revert "New: Added support for Samsung vendor specific media input sources" This reverts commit b01e27e9fe8e58f3411814d89adf9debc72f6e7e. --- config.schema.json | 10 +++------- lib/services/input.js | 2 +- lib/smartthings.js | 10 ++-------- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/config.schema.json b/config.schema.json index 84dd1bf..dc6b800 100644 --- a/config.schema.json +++ b/config.schema.json @@ -158,7 +158,7 @@ }, "input": { "type": "string", - "enum": ["digitalTv", "HDMI1", "HDMI2", "HDMI3", "HDMI4", "HDMI5", "HDMI6", "Display Port", "USB", "USB-C", "AM", "FM"] + "enum": ["digitalTv", "HDMI1", "HDMI2", "HDMI3", "HDMI4", "HDMI5", "HDMI6", "USB", "AM", "FM"] }, "sleep": { "type": "integer" @@ -254,7 +254,7 @@ }, { "key": "devices[].mac", - "title": "Network MAC Address", + "title": "MAC Address", "placeholder": "A0:B1:C2:D3:E4:F5" } ] @@ -334,7 +334,7 @@ "condition": { "functionBody": "return model.devices[arrayIndices[0]].inputs[arrayIndices[1]].type == 'input';" }, - "enum": ["digitalTv", "HDMI1", "HDMI2", "HDMI3", "HDMI4", "HDMI5", "HDMI6", "Display Port", "USB", "USB-C", "AM", "FM"], + "enum": ["digitalTv", "HDMI1", "HDMI2", "HDMI3", "HDMI4", "HDMI5", "HDMI6", "USB", "AM", "FM"], "titleMap": { "digitalTv": "Live TV", "HDMI1": "HDMI 1", @@ -343,9 +343,7 @@ "HDMI4": "HDMI 4", "HDMI5": "HDMI 5", "HDMI6": "HDMI 6", - "Display Port": "Display Port", "USB": "USB", - "USB-C": "USB-C", "AM": "AM", "FM": "FM" } @@ -400,8 +398,6 @@ "titleMap": { "digitalTv": "Live TV", "USB": "USB", - "USB-C": "USB-C", - "Display Port": "Display Port", "HDMI1": "HDMI 1", "HDMI2": "HDMI 2", "HDMI3": "HDMI 3", diff --git a/lib/services/input.js b/lib/services/input.js index f1797b5..bfbea9f 100644 --- a/lib/services/input.js +++ b/lib/services/input.js @@ -37,7 +37,7 @@ module.exports = class Input { return Hap.Characteristic.InputSourceType.TUNER; } - if (this.config.value && this.config.value.startsWith('USB')) { + if (this.config.value == 'USB') { return Hap.Characteristic.InputSourceType.USB; } diff --git a/lib/smartthings.js b/lib/smartthings.js index 7acc3ee..059f845 100644 --- a/lib/smartthings.js +++ b/lib/smartthings.js @@ -93,7 +93,7 @@ module.exports = class SmartThings { return new Promise(null); }); } - + /** * Refreshes the main component's capability. * @returns {Promise} A promise that resolves when the refresh command is sent. @@ -109,13 +109,7 @@ module.exports = class SmartThings { * @returns {Promise} A promise that resolves when the input source is set successfully. */ setInputSource(value) { - // Try to set input source with Samsung-specific vendor extended capability (used in e.g. smart monitors) - return this.sendCommands({ component: 'main', capability: 'samsungvd.mediaInputSource', command: 'setInputSource', arguments: [value] }) - .catch((error) => { - // Catch errors (422) potentially due to unsupported capability and fallback to standard capability (used in most Samsung TVs) - this.device.log.debug('Failed to set input source with Samsung-specific vendor extended capability, falling back to standard capability.', error); - return this.sendCommands({ component: 'main', capability: 'mediaInputSource', command: 'setInputSource', arguments: [value] }); - }); + return this.sendCommands({component: 'main', capability: 'mediaInputSource', command: 'setInputSource', arguments: [value]}); } /** From 34334d150c37259992f4e183c6cd81eb9963c984 Mon Sep 17 00:00:00 2001 From: Petar Hristov Date: Sat, 27 Apr 2024 22:40:41 +0200 Subject: [PATCH 13/14] Fix: Renamed method reference --- lib/remote.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/remote.js b/lib/remote.js index 99ab8f7..a0faba5 100644 --- a/lib/remote.js +++ b/lib/remote.js @@ -121,7 +121,7 @@ module.exports = class Remote { // If SmartThings API is available, use it to get the power state reliably, otherwise fall back to default API method if (this.smartthings.available && this.device.config.use_smartthings_power) { - return this.cache.get('smartthings-power-state', () => this.smartthings.getSwitchState(), 2500) + return this.cache.get('smartthings-power-state', () => this.smartthings.getPowerState(), 2500) .then(powerState => { if (powerState !== null) { return powerState; } this.device.log.debug("SmartThings API call failed, falling back to default API method."); From e9497f1a9b58b6476383c571f35bd0b629f10548 Mon Sep 17 00:00:00 2001 From: Petar Hristov Date: Sat, 27 Apr 2024 22:54:31 +0200 Subject: [PATCH 14/14] Did some more spellchecking --- .gitignore | 1 + config.schema.json | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 7abe7b4..3626bcb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ ehthumbs.db Thumbs.db Icon +.vscode node_modules dist \ No newline at end of file diff --git a/config.schema.json b/config.schema.json index dc6b800..ac72a9b 100644 --- a/config.schema.json +++ b/config.schema.json @@ -484,7 +484,7 @@ "wss": "Default", "frame": "Frame" }, - "description": "The plugin detects the type of TV automaticaly. Leave this option set to None unless you are having problems and the plugin don't detect your TV type correctly." + "description": "The plugin detects the type of TV automatically. Leave this option set to None unless you are having problems and the plugin don't detect your TV type correctly." }, { "key": "devices[].uuid", @@ -496,7 +496,7 @@ "key": "devices[].delay", "title": "Command Delay Interval", "placeholder": "400", - "description": "This is the delay between each command when you send multiple commands. By lowering the value you risk the commands not being executed. Value is in miliseconds." + "description": "This is the delay between each command when you send multiple commands. By lowering the value you risk the commands not being executed. Value is in milliseconds." }, { "key": "devices[].options",