From e01cef71eec4c5ff96874f855fc41fdc861f4bca Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Wed, 10 Jan 2024 16:59:52 +0100 Subject: [PATCH 1/2] Add new config options for Fan and Thermostat Signed-off-by: Michael Krug --- docs/USAGE.md | 22 +++++----- functions/devices/fan.js | 7 +-- functions/devices/thermostat.js | 2 + tests/devices/fan.test.js | 75 ++++++++++++++++++++++++++++++++ tests/devices/thermostat.test.js | 34 +++++++++++++++ 5 files changed, 126 insertions(+), 14 deletions(-) diff --git a/docs/USAGE.md b/docs/USAGE.md index 54929d97..375f15a7 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -17,7 +17,7 @@ With the Action you can voice control your openHAB items and it supports lights, Please be aware that the graphical user interface in the Google Home app or on Google Nest devices may not fully support interaction with some of the supported device types. We cannot influence this and rely on Google to implement user interfaces for more devices. Nevertheless, interaction via voice or in writing with Google Assistant should always work. ::: -If you have any issues, questions or an idea for additional features, please take a look at the [repository on GitHub](https://github.com/openhab/openhab-google-assistant). +If you have any issues, questions or an idea for additional features, please have a look at the [repository on GitHub](https://github.com/openhab/openhab-google-assistant). [[toc]] @@ -329,23 +329,23 @@ Player transportItem (tvGroup) { ga="tvTransport" } | **Device Type** | [Fan](https://developers.home.google.com/cloud-to-cloud/guides/fan), [Hood](https://developers.home.google.com/cloud-to-cloud/guides/hood), [AirPurifier](https://developers.home.google.com/cloud-to-cloud/guides/airpurifier) | | **Supported Traits** | [OnOff](https://developers.home.google.com/cloud-to-cloud/traits/OnOff), [FanSpeed](https://developers.home.google.com/cloud-to-cloud/traits/fanspeed) (depending on used item type) | | **Supported Items** | Switch (no speed control), Dimmer | -| **Configuration** | (optional) `checkState=true/false`
(optional) `speeds="0=away:zero,50=default:standard:one,100=high:two"`
(optional) `lang="en"`
(optional) `ordered=true/false` | +| **Configuration** | (optional) `checkState=true/false`
(optional) `fanSpeeds="0=away:zero,50=default:standard:one,100=high:two"`
(optional) `lang="en"`
(optional) `ordered=true/false` | Fans (and similar device types, like AirPurifier or Hood) support the `FanSpeed` trait. -If you do not specify the `speeds` option, Google will use and expect percentage values for the fan speed. +If you do not specify the `fanSpeeds` option, Google will use and expect percentage values for the fan speed. Otherwise, you will be able to set up and use human speakable modes, e.g. "fast" for 100% or "slow" for 25%. -`speeds` will be a comma-separated list of modes, where the mode value corresponds to the speed value to be passed to the device. The mode or value is followed by an equal sign to list different aliases separated by a colon sign. +`fanSpeeds` will be a comma-separated list of modes, where the mode value corresponds to the speed value to be passed to the device. The mode or value is followed by an equal sign to list different aliases separated by a colon sign. So in the example stated below both "high" and "two" would set the speed to 100%. -Some devices may expect a specific value instead of a percentage, like "1" or "2" as speed values. In this case, you can adjust the configuration and replace the percentage values with the values that the device expects. (e.g.: `speeds="0=away:zero,1=default:standard:one,2=high:two"`). +Some devices may expect a specific value instead of a percentage, like "1" or "2" as speed values. In this case, you can adjust the configuration and replace the percentage values with the values that the device expects. (e.g.: `fanSpeeds="0=away:zero,1=default:standard:one,2=high:two"`). You are also able to define the language of those aliases. The option `ordered` will tell the system that your list is ordered and you will then be able to also say "faster" or "slower" and Google will use the next or previous speed. ```shell -Dimmer { ga="Fan" [ speeds="0=away:zero,50=default:standard:one,100=high:two", lang="en", ordered=true ] } # Using specific percentage values for the speed +Dimmer { ga="Fan" [ fanSpeeds="0=away:zero,50=default:standard:one,100=high:two", lang="en", ordered=true ] } # Using specific percentage values for the speed Switch { ga="Hood" } # No speed control - only on/off Dimmer { ga="AirPurifier" } # Using percentage values for the speed -Dimmer { ga="AirPurifier" [ speeds="0=away:zero,1=low:one,2=medium:two,3=high:three,4=turbo:four", lang="en", ordered=true ] } # Using specific speed modes/values, which differ from percentage +Dimmer { ga="AirPurifier" [ fanSpeeds="0=away:zero,1=low:one,2=medium:two,3=high:three,4=turbo:four", lang="en", ordered=true ] } # Using specific speed modes/values, which differ from percentage Switch { ga="AirPurifier" } # No speed control - only on/off ``` @@ -447,7 +447,7 @@ Number humidityItem (sensorGroup) { ga="humidityAmbient" } | **Device Type** | [Thermostat](https://developers.home.google.com/cloud-to-cloud/guides/thermostat) | | **Supported Traits** | [TemperatureSetting](https://developers.home.google.com/cloud-to-cloud/traits/temperaturesetting) | | **Supported Items** | Group as `Thermostat` with the following members:
String or Number as `thermostatMode`
(optional) Number as `thermostatHumidityAmbient`
(optional) Number as `thermostatTemperatureAmbient`
(optional) Number as `thermostatTemperatureSetpoint`
(optional) Number as `thermostatTemperatureSetpointLow`
(optional) Number as `thermostatTemperatureSetpointHigh` | -| **Configuration** | (optional) `checkState=true/false`
(optional) `useFahrenheit=true/false`
(optional) `thermostatTemperatureRange="10,30"`
(optional) `modes="off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto"` | +| **Configuration** | (optional) `checkState=true/false`
(optional) `useFahrenheit=true/false`
(optional) `thermostatTemperatureRange="10,30"`
(optional) `thermostatModes="off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto"` | Thermostat requires a group of items to be properly configured to be used with Google Assistant. The default temperature unit is Celsius. To change the temperature unit to Fahrenheit, add the config option `useFahrenheit=true` to the thermostat group. @@ -456,15 +456,15 @@ If your thermostat supports a range for the setpoint you can use both `thermosta If your thermostat does not have a mode, you should create one and manually assign a value (e.g. heat, cool, on, etc.) to have proper functionality. -To map the [default thermostat modes of Google](https://developers.home.google.com/cloud-to-cloud/traits/temperaturesetting.html) (on, off, heat, cool, etc.) to custom ones for your specific setup, you can use the `modes` config option on the thermostat group. -E.g. `[ modes="off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto" ]` will enable the following five modes in Google Home `"off, heat, eco, on, auto"` that will be translated to `"OFF, COMFORT, ECO, ON, auto"`. You can specify alternative conversions using the colon sign, so that in the former example "BOOST" in openHAB would also be translated to "heat" in Google. For the translation of Google modes to openHAB always the first option after the equal sign is used. +To map the [default thermostat modes of Google](https://developers.home.google.com/cloud-to-cloud/traits/temperaturesetting.html) (on, off, heat, cool, etc.) to custom ones for your specific setup, you can use the `thermostatModes` config option on the thermostat group. +E.g. `[ thermostatModes="off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto" ]` will enable the following five modes in Google Home `"off, heat, eco, on, auto"` that will be translated to `"OFF, COMFORT, ECO, ON, auto"`. You can specify alternative conversions using the colon sign, so that in the former example "BOOST" in openHAB would also be translated to "heat" in Google. For the translation of Google modes to openHAB always the first option after the equal sign is used. By default the integration will provide `"off,heat,cool,on,heatcool,auto,eco"`. You can also set up a Thermostat for using it as a temperature sensor. To do so, create a Thermostat group and only add one item member as "thermostatTemperatureAmbient". However, it is recommended to prefer the `TemperatureSensor` type for simple temperature reports (but currently there is no UI support in Google Home). ```shell -Group thermostatGroup { ga="Thermostat" [ modes="off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto", thermostatTemperatureRange="10,30", useFahrenheit=false ] } +Group thermostatGroup { ga="Thermostat" [ thermostatModes="off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto", thermostatTemperatureRange="10,30", useFahrenheit=false ] } Number temperatureItem (thermostatGroup) { ga="thermostatTemperatureAmbient" } Number humidityItem (thermostatGroup) { ga="thermostatHumidityAmbient" } Number setpointItem (thermostatGroup) { ga="thermostatTemperatureSetpoint" } diff --git a/functions/devices/fan.js b/functions/devices/fan.js index 86f531e0..e00a3a06 100644 --- a/functions/devices/fan.js +++ b/functions/devices/fan.js @@ -11,7 +11,7 @@ class Fan extends DefaultDevice { static getAttributes(item) { const config = this.getConfig(item); - if (!config || !config.speeds) { + if (!config || !(config.speeds || config.fanSpeeds)) { return { supportsFanSpeedPercent: true }; @@ -23,7 +23,8 @@ class Fan extends DefaultDevice { }, reversible: false }; - config.speeds.split(',').forEach((speedEntry) => { + const fanSpeeds = config.speeds || config.fanSpeeds; + fanSpeeds.split(',').forEach((speedEntry) => { try { const [speedName, speedSynonyms] = speedEntry .trim() @@ -56,7 +57,7 @@ class Fan extends DefaultDevice { on: itemState > 0 }; const config = this.getConfig(item); - if (config && config.speeds) { + if (config && (config.speeds || config.fanSpeeds)) { state.currentFanSpeedSetting = itemState.toString(); } else { state.currentFanSpeedPercent = itemState; diff --git a/functions/devices/thermostat.js b/functions/devices/thermostat.js index a9aba5df..6be3b5bb 100644 --- a/functions/devices/thermostat.js +++ b/functions/devices/thermostat.js @@ -90,6 +90,8 @@ class Thermostat extends DefaultDevice { let modes = ['off', 'heat', 'cool', 'on', 'heatcool', 'auto', 'eco']; if ('modes' in config) { modes = config.modes.split(',').map((s) => s.trim()); + } else if ('thermostatModes' in config) { + modes = config.thermostatModes.split(',').map((s) => s.trim()); } const modeMap = {}; modes.forEach((pair) => { diff --git a/tests/devices/fan.test.js b/tests/devices/fan.test.js index 7b0f28dd..52dfb315 100644 --- a/tests/devices/fan.test.js +++ b/tests/devices/fan.test.js @@ -107,4 +107,79 @@ describe('Fan Device', () => { on: true }); }); + + describe('transition tests', () => { + test('getState', () => { + expect(Device.getState({ state: '50 %' })).toStrictEqual({ + currentFanSpeedPercent: 50, + on: true + }); + expect( + Device.getState({ + state: '50 upm', + metadata: { + ga: { + config: { + ordered: true, + fanSpeeds: '0=null:off,50=slow,100=full:fast', + lang: 'en' + } + } + } + }) + ).toStrictEqual({ + currentFanSpeedSetting: '50', + on: true + }); + }); + + test('getAttributes speeds', () => { + const item = { + metadata: { + ga: { + config: { + ordered: true, + fanSpeeds: '0=null:off,50=slow,100=full:fast', + lang: 'en' + } + } + } + }; + expect(Device.getAttributes(item)).toStrictEqual({ + availableFanSpeeds: { + speeds: [ + { + speed_name: '0', + speed_values: [ + { + speed_synonym: ['null', 'off'], + lang: 'en' + } + ] + }, + { + speed_name: '50', + speed_values: [ + { + speed_synonym: ['slow'], + lang: 'en' + } + ] + }, + { + speed_name: '100', + speed_values: [ + { + speed_synonym: ['full', 'fast'], + lang: 'en' + } + ] + } + ], + ordered: true + }, + reversible: false + }); + }); + }); }); diff --git a/tests/devices/thermostat.test.js b/tests/devices/thermostat.test.js index 7fc4a52e..70ce1afa 100644 --- a/tests/devices/thermostat.test.js +++ b/tests/devices/thermostat.test.js @@ -394,4 +394,38 @@ describe('Thermostat Device', () => { }); }); }); + + describe('transition tests', () => { + test('getModeMap', () => { + const item = { + metadata: { + ga: { + config: { + thermostatModes: 'on=ON:1,off=OFF:2,auto=3' + } + } + } + }; + expect(Device.getModeMap(item)).toStrictEqual({ + on: ['ON', '1'], + off: ['OFF', '2'], + auto: ['3'] + }); + }); + + test('translateModeToGoogle', () => { + const item = { + metadata: { + ga: { + config: { + thermostatModes: 'on=ON:1,off=OFF:2,auto=3' + } + } + } + }; + expect(Device.translateModeToGoogle(item, 'OFF')).toBe('off'); + expect(Device.translateModeToGoogle(item, '3')).toBe('auto'); + expect(Device.translateModeToGoogle(item, 'invalid')).toBe('on'); + }); + }); }); From 0a41023f6b22dd15f3283f3ffa0d91842580681a Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 11 Jan 2024 13:39:07 +0100 Subject: [PATCH 2/2] Fix markdown complain Signed-off-by: Michael Krug --- docs/USAGE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/USAGE.md b/docs/USAGE.md index 375f15a7..ec7f098d 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -17,7 +17,7 @@ With the Action you can voice control your openHAB items and it supports lights, Please be aware that the graphical user interface in the Google Home app or on Google Nest devices may not fully support interaction with some of the supported device types. We cannot influence this and rely on Google to implement user interfaces for more devices. Nevertheless, interaction via voice or in writing with Google Assistant should always work. ::: -If you have any issues, questions or an idea for additional features, please have a look at the [repository on GitHub](https://github.com/openhab/openhab-google-assistant). +If you have any issues, questions or an idea for additional features, please take a look at the [repository on GitHub](https://github.com/openhab/openhab-google-assistant). [[toc]] @@ -458,7 +458,7 @@ If your thermostat does not have a mode, you should create one and manually assi To map the [default thermostat modes of Google](https://developers.home.google.com/cloud-to-cloud/traits/temperaturesetting.html) (on, off, heat, cool, etc.) to custom ones for your specific setup, you can use the `thermostatModes` config option on the thermostat group. E.g. `[ thermostatModes="off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto" ]` will enable the following five modes in Google Home `"off, heat, eco, on, auto"` that will be translated to `"OFF, COMFORT, ECO, ON, auto"`. You can specify alternative conversions using the colon sign, so that in the former example "BOOST" in openHAB would also be translated to "heat" in Google. For the translation of Google modes to openHAB always the first option after the equal sign is used. -By default the integration will provide `"off,heat,cool,on,heatcool,auto,eco"`. +By default, the integration will provide `"off,heat,cool,on,heatcool,auto,eco"`. You can also set up a Thermostat for using it as a temperature sensor. To do so, create a Thermostat group and only add one item member as "thermostatTemperatureAmbient". However, it is recommended to prefer the `TemperatureSensor` type for simple temperature reports (but currently there is no UI support in Google Home).