diff --git a/docs/USAGE.md b/docs/USAGE.md
index 45af0fd3..82586e49 100644
--- a/docs/USAGE.md
+++ b/docs/USAGE.md
@@ -32,7 +32,7 @@ This documentation refers to release [v3.9.0](https://github.com/openhab/openhab
- Prepared available configuration options for the upcoming rollout of v4
- Users can start adjusting their setup to use the new configuration options:
- [`Thermostat`](#thermostat): `modes` has been renamed to `thermostatModes`
- - [`Fan`](#fan-hood-airpurifier): `speeds` has been renamed to `fanSpeeds`
+ - [`Fan`](#fan-hood-airpurifier-only-onoff-or-fan-speed-control): `speeds` has been renamed to `fanSpeeds`
### v3.8.0
@@ -40,7 +40,7 @@ This documentation refers to release [v3.9.0](https://github.com/openhab/openhab
### v3.7.0
-- Adjusted [`Fan`](#fan-hood-airpurifier) to use `supportsFanSpeedPercent` option
+- Adjusted [`Fan`](#fan-hood-airpurifier-only-onoff-or-fan-speed-control) to use `supportsFanSpeedPercent` option
- Inverted `lightColorTemperature` percentage range when using `colorUnit="percent"` with [`SpecialColorLight`](#light-as-group-with-separate-controls)
### v3.6.0
@@ -194,7 +194,7 @@ Switch { ga="Fireplace" }
Switch { ga="Valve" [ inverted=true ] }
```
-### Sprinkler, Vacuum
+### Sprinkler, Vacuum, Washer, Dishwasher
| | |
|---|---|
@@ -206,6 +206,8 @@ Switch { ga="Valve" [ inverted=true ] }
```shell
Switch { ga="Sprinkler" [ inverted=true ] }
Switch { ga="Vacuum" [ inverted=false ] }
+Switch { ga="Washer" [ inverted=false ] }
+Switch { ga="Dishwasher" [ inverted=false ] }
```
### Lock
@@ -315,7 +317,7 @@ Dimmer { ga="Speaker" [ volumeDefaultPercentage="50", levelStepSize="10", volume
|---|---|
| **Device Type** | [TV](https://developers.home.google.com/cloud-to-cloud/guides/tv) |
| **Supported Traits** | [OnOff](https://developers.home.google.com/cloud-to-cloud/traits/onoff), [Volume](https://developers.home.google.com/cloud-to-cloud/traits/volume), [TransportControl](https://developers.home.google.com/cloud-to-cloud/traits/transportcontrol), [InputSelector](https://developers.home.google.com/cloud-to-cloud/traits/inputselector), [AppSelector](https://developers.home.google.com/cloud-to-cloud/traits/appselector), [Channel](https://developers.home.google.com/cloud-to-cloud/traits/channel) (depending on used members) |
-| **Supported Items** | Group as `TV` with the following members:
(optional) Switch as `tvPower`
(optional) Switch as `tvMute`
(optional) Dimmer as `tvVolume`
(optional) String as `tvChannel`
(optional) String as `tvInput`
(optional) String as `tvApplication`
(optional) Player as `tvTransport` |
+| **Supported Items** | Group as `TV` with the following members:
(optional) Switch as `tvPower`
(optional) Switch as `tvMute`
(optional) Dimmer or Number as `tvVolume`
(optional) String or Number as `tvChannel`
(optional) String or Number as `tvInput`
(optional) String or Number as `tvApplication`
(optional) Player as `tvTransport` |
| **Configuration** | (optional) `checkState=true/false`
(optional) `volumeDefaultPercentage="20"`
(optional) `levelStepSize="5"`
(optional) `volumeMaxLevel="100"`
(optional) `transportControlSupportedCommands="NEXT,PREVIOUS,PAUSE,RESUME"`
(optional) `availableChannels="channelNumber=channelId=channelName:channelSynonym:...,..."`
(optional) `availableInputs="inputKey=inputName:inputSynonym:...,..."`
(optional) `availableApplications="applicationKey=applicationName:applicationSynonym:...,..."`
(optional) `lang="en"` |
```shell
@@ -329,13 +331,13 @@ String applicationItem (tvGroup) { ga="tvApplication" }
Player transportItem (tvGroup) { ga="tvTransport" }
```
-### Fan, Hood, AirPurifier
+### Fan, Hood, AirPurifier (only on/off or fan speed control)
| | |
|---|---|
| **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 |
+| **Supported Items** | Switch (no speed control), Dimmer, Number |
| **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.
@@ -356,6 +358,59 @@ Dimmer { ga="AirPurifier" [ fanSpeeds="0=away:zero,1=low:one,2=medium:two,3=high
Switch { ga="AirPurifier" } # No speed control - only on/off
```
+### Fan, Hood, AirPurifier (extended control options)
+
+| | |
+|---|---|
+| **Device Type** | [Fan](https://developers.google.com/assistant/smarthome/guides/fan), [Hood](https://developers.google.com/assistant/smarthome/guides/hood), [AirPurifier](https://developers.google.com/assistant/smarthome/guides/airpurifier) |
+| **Supported Traits** | [OnOff](https://developers.google.com/assistant/smarthome/traits/OnOff), [FanSpeed](https://developers.google.com/assistant/smarthome/traits/fanspeed), [Modes](https://developers.google.com/assistant/smarthome/traits/modes), [SensorState](https://developers.google.com/assistant/smarthome/traits/sensorstate) |
+| **Supported Items** | Group as `Fan`, `Hood` or `AirPurifier` with the following members:
(optional) Switch as `fanPower`
(optional) Dimmer or Number as `fanSpeed`
(optional) Number or String as `fanMode`
(optional) Number as `fanFilterLifeTime`
(optional) Number as `fanPM25` |
+| **Configuration** | (optional) `checkState=true/false`
(optional) `fanSpeeds="0=away:zero,50=default:standard:one,100=high:two"`
(optional) `fanModeName="OperationMode,Modus"`
(optional) `fanModeSettings="1=Low:Silent,2=Normal,3=High:Night"`
(optional) `lang="en"`
(optional) `ordered=true/false` |
+
+When configuring a Fan (or similar device) as a group with the above listed members, you will gain more options to control the device.
+In addition to control power and speeds, you will also be able to set modes and query sensor information (if supported by Google).
+
+For more information on the `fanSpeeds` configuration option, please take a look at the simple `Fan` device type.
+
+With the `fanModeName` and `fanModeSettings` you can control specific modes. Currently, one mode type per device is supported that you can configure with a name and a list of settings. The first entry in the names field is used internally while any other following separated by comma will be a synonym to be used in commands. The settings list is a comma-separated list of `value=name` pairs. The name can also contain synonyms separated by a colon. In the listed example you could then say "Set OperationMode to Normal".
+
+_Hint:_ At the moment, sensor values will only be queriable by voice and will not show up anywhere in the Google Home app.
+
+```shell
+Group fanGroup { ga="Fan" [ fanSpeeds="0=away:zero,50=default:standard:one,100=high:two", fanModeName="OperationMode,Modus", fanModeSettings="1=Silent,2=Normal,3=Night", lang="en", ordered=true ] }
+Switch powerItem (fanGroup) { ga="fanPower" }
+Dimmer speedItem (fanGroup) { ga="fanSpeed" }
+String modeItem (fanGroup) { ga="fanMode" }
+Number lifetimeItem (fanGroup) { ga="fanFilterLifeTime" }
+Number pm25Item (fanGroup) { ga="fanPM25" }
+```
+
+### AC_Unit
+
+| | |
+|---|---|
+| **Device Type** | [AC_Unit](https://developers.google.com/assistant/smarthome/guides/acunit) |
+| **Supported Traits** | [OnOff](https://developers.google.com/assistant/smarthome/traits/OnOff), [FanSpeed](https://developers.google.com/assistant/smarthome/traits/fanspeed), [TemperatureSetting](https://developers.google.com/assistant/smarthome/traits/temperaturesetting), [Modes](https://developers.google.com/assistant/smarthome/traits/modes), [SensorState](https://developers.google.com/assistant/smarthome/traits/sensorstate) |
+| **Supported Items** | Group as `AC_Unit` with the following members:
(optional) Switch as `fanPower`
(optional) Dimmer or Number as `fanSpeed`
(optional) Number or String as `fanMode`
(optional) Number as `fanFilterLifeTime`
(optional) Number as `fanPM25`
(optional) Number as `thermostatTemperatureAmbient`
(optional) Number as `thermostatTemperatureSetpoint`
(optional) Number as `thermostatTemperatureSetpointLow`
(optional) Number as `thermostatTemperatureSetpointHigh`
(optional) Number as `thermostatHumidityAmbient`
(optional) String or Number or Switch as `thermostatMode` |
+| **Configuration** | (optional) `checkState=true/false`
(optional) `fanSpeeds="0=away:zero,50=default:standard:one,100=high:two"`
(optional) `fanModeName="OperationMode,Modus"`
(optional) `fanModeSettings="1=Low:Silent,2=Normal,3=High:Night"`
(optional) `useFahrenheit=true/false`
(optional) `maxHumidity=1-100`
(optional) `thermostatTemperatureRange="10,30"`
(optional) `thermostatModes="off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto"`
(optional) `lang="en"`
(optional) `ordered=true/false` |
+
+The AC_Unit device is basically the combination of the Fan and the Thermostat device. For explanation on configuration options please take a look at both of them.
+
+```shell
+Group acunitGroup { ga="AC_Unit" [ fanSpeeds="0=null:off,50=slow,100=full:fast", fanModeName="OperationMode,Modus", fanModeSettings="1=Silent,2=Normal,3=Night", thermostatModes="off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto", thermostatTemperatureRange="10,30", useFahrenheit=false, lang="en", ordered=true ] }
+Switch powerItem (acunitGroup) { ga="fanPower" }
+Dimmer speedItem (acunitGroup) { ga="fanSpeed" }
+String modeItem (acunitGroup) { ga="fanMode" }
+Number lifetimeItem (acunitGroup) { ga="fanFilterLifeTime" }
+Number pm25Item (acunitGroup) { ga="fanPM25" }
+Number ambientItem (acunitGroup) { ga="thermostatTemperatureAmbient" }
+Number humidityItem (acunitGroup) { ga="thermostatHumidityAmbient" }
+Number setpointItem (acunitGroup) { ga="thermostatTemperatureSetpoint" }
+Number setpointItemLow (acunitGroup) { ga="thermostatTemperatureSetpointLow" }
+Number setpointItemHigh (acunitGroup) { ga="thermostatTemperatureSetpointHigh" }
+String modeItem (acunitGroup) { ga="thermostatMode" }
+```
+
### Awning, Blinds, Curtain, Door, Garage, Gate, Pergola, Shutter, Window
| | |
@@ -414,10 +469,16 @@ Number capacityFullItem (chargerGroup) { ga="chargerCapacityUntilFull" }
| **Device Type** | [Sensor](https://developers.home.google.com/cloud-to-cloud/guides/sensor) |
| **Supported Traits** | [TemperatureControl](https://developers.home.google.com/cloud-to-cloud/traits/temperaturecontrol), [TemperatureSetting](https://developers.home.google.com/cloud-to-cloud/traits/temperaturesetting) |
| **Supported Items** | Number |
-| **Configuration** | (optional) `useFahrenheit=true/false` |
+| **Configuration** | (optional) `useFahrenheit=true/false`
(optional) `temperatureRange="-10,50"` |
+
+By default, the temperature range of a temperature sensor is set to -100 °C to 100 °C.
+The reported state values have to fall into that range!
+If you need to adjust the range, please add the config option `temperatureRange="-20,40"` to the item. Keep in mind that those values always have to be provided in Celsius!
+
+_Hint:_ At the moment, sensor values will only be queriable by voice and will not show up anywhere in the Google Home app.
```shell
-Number { ga="TemperatureSensor" [ useFahrenheit=true ] }
+Number { ga="TemperatureSensor" [ useFahrenheit=true, temperatureRange="-20,40" ] }
```
### HumiditySensor
@@ -427,6 +488,7 @@ Number { ga="TemperatureSensor" [ useFahrenheit=true ] }
| **Device Type** | [Sensor](https://developers.home.google.com/cloud-to-cloud/guides/sensor) |
| **Supported Traits** | [HumiditySetting](https://developers.home.google.com/cloud-to-cloud/traits/humiditysetting) |
| **Supported Items** | Number |
+| **Configuration** | (optional) `maxHumidity=1-100` |
```shell
Number { ga="HumiditySensor" }
@@ -439,10 +501,14 @@ Number { ga="HumiditySensor" }
| **Device Type** | [Sensor](https://developers.home.google.com/cloud-to-cloud/guides/sensor) |
| **Supported Traits** | [HumiditySetting](https://developers.home.google.com/cloud-to-cloud/traits/humiditysetting), [TemperatureControl](https://developers.home.google.com/cloud-to-cloud/traits/temperaturecontrol), [TemperatureSetting](https://developers.home.google.com/cloud-to-cloud/traits/temperaturesetting) |
| **Supported Items** | Group as `ClimateSensor` with the following members:
(optional) Number as `humidityAmbient`
(optional) Number as `temperatureAmbient` |
-| **Configuration** | (optional) `useFahrenheit=true/false` |
+| **Configuration** | (optional) `useFahrenheit=true/false`
(optional) `maxHumidity=1-100`
(optional) `temperatureRange="-10,50"` |
+
+By default, the temperature range of a climate sensor is set to -100 °C to 100 °C.
+The reported state values have to fall into that range!
+If you need to adjust the range, please add the config option `temperatureRange="-20,40"` to the item. Keep in mind that those values always have to be provided in Celsius!
```shell
-Group sensorGroup { ga="ClimateSensor" [ useFahrenheit=true ] }
+Group sensorGroup { ga="ClimateSensor" [ useFahrenheit=true, temperatureRange="0,40" ] }
Number temperatureItem (sensorGroup) { ga="temperatureAmbient" }
Number humidityItem (sensorGroup) { ga="humidityAmbient" }
```
@@ -453,8 +519,8 @@ 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) `thermostatModes="off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto"` |
+| **Supported Items** | Group as `Thermostat` with the following members
(optional) Number as `thermostatTemperatureAmbient`
(optional) Number as `thermostatTemperatureSetpoint`
(optional) Number as `thermostatTemperatureSetpointLow`
(optional) Number as `thermostatTemperatureSetpointHigh`
(optional) Number as `thermostatHumidityAmbient`
(optional) String or Number or Switch as `thermostatMode` |
+| **Configuration** | (optional) `checkState=true/false`
(optional) `useFahrenheit=true/false`
(optional) `maxHumidity=1-100`
(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.
@@ -472,7 +538,7 @@ However, it is recommended to prefer the `TemperatureSensor` type for simple tem
```shell
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 ambientItem (thermostatGroup) { ga="thermostatTemperatureAmbient" }
Number humidityItem (thermostatGroup) { ga="thermostatHumidityAmbient" }
Number setpointItem (thermostatGroup) { ga="thermostatTemperatureSetpoint" }
Number setpointItemLow (thermostatGroup) { ga="thermostatTemperatureSetpointLow" }
diff --git a/functions/apihandler.js b/functions/apihandler.js
index 79393421..c36a9e3f 100644
--- a/functions/apihandler.js
+++ b/functions/apihandler.js
@@ -27,7 +27,7 @@ class ApiHandler {
*/
constructor(config = { host: '', path: '/rest/items/', port: 80 }) {
if (!config.path.startsWith('/')) {
- config.path = '/' + config.path;
+ config.path = `/${config.path}`;
}
if (!config.path.endsWith('/')) {
config.path += '/';
@@ -51,13 +51,13 @@ class ApiHandler {
getOptions(method = 'GET', itemName = '', length = 0) {
const queryString =
method === 'GET'
- ? '?metadata=ga,synonyms' + (itemName ? '' : '&fields=groupNames,groupType,name,label,metadata,type')
+ ? `?metadata=ga,synonyms${itemName ? '' : '&fields=groupNames,groupType,name,label,metadata,type,state'}`
: '';
const options = {
hostname: this._config.host,
port: this._config.port,
- path: this._config.path + (itemName ? itemName : '') + queryString,
method: method,
+ path: this._config.path + (itemName || '') + queryString,
headers: {
Accept: 'application/json'
}
@@ -66,7 +66,7 @@ class ApiHandler {
if (this._config.userpass) {
options.auth = this._config.userpass;
} else if (this._authToken) {
- options.headers['Authorization'] = 'Bearer ' + this._authToken;
+ options.headers.Authorization = `Bearer ${this._authToken}`;
}
if (method === 'POST') {
@@ -85,8 +85,8 @@ class ApiHandler {
return new Promise((resolve, reject) => {
const protocol = options.port === 443 ? https : http;
const req = protocol.request(options, (response) => {
- if (200 !== response.statusCode) {
- reject({ statusCode: response.statusCode, message: 'getItem - failed for path: ' + options.path });
+ if (response.statusCode !== 200) {
+ reject({ statusCode: response.statusCode, message: `getItem - failed for path: ${options.path}` });
return;
}
@@ -103,7 +103,7 @@ class ApiHandler {
} catch (e) {
reject({
statusCode: 415,
- message: 'getItem - JSON parse failed for path: ' + options.path + ' - ' + e.toString()
+ message: `getItem - JSON parse failed for path: ${options.path} - ${e.toString()}`
});
}
});
@@ -130,10 +130,10 @@ class ApiHandler {
const protocol = options.port === 443 ? https : http;
const req = protocol.request(options, (response) => {
if (!response.statusCode || ![200, 201].includes(response.statusCode)) {
- reject({ statusCode: response.statusCode, message: 'sendCommand - failed for path: ' + options.path });
+ reject({ statusCode: response.statusCode, message: `sendCommand - failed for path: ${options.path}` });
return;
}
- resolve(null);
+ resolve(true);
});
req.on('error', (error) => {
console.error(`openhabGoogleAssistant - sendCommand: ERROR ${JSON.stringify(error)}`);
diff --git a/functions/commands/appselect.js b/functions/commands/appselect.js
index 19b97029..e0e6efdc 100644
--- a/functions/commands/appselect.js
+++ b/functions/commands/appselect.js
@@ -17,10 +17,10 @@ class AppSelect extends DefaultCommand {
return true;
}
- static getItemName(item) {
- const members = TV.getMembers(item);
+ static getItemName(device) {
+ const members = this.getMembers(device);
if ('tvApplication' in members) {
- return members.tvApplication.name;
+ return members.tvApplication;
}
throw { statusCode: 400 };
}
diff --git a/functions/commands/armdisarm.js b/functions/commands/armdisarm.js
index 5bcae2c4..649a2421 100644
--- a/functions/commands/armdisarm.js
+++ b/functions/commands/armdisarm.js
@@ -21,21 +21,21 @@ class ArmDisarm extends DefaultCommand {
return arm ? 'ON' : 'OFF';
}
- static getItemName(item, device, params) {
+ static getItemName(device, params) {
if (this.getDeviceType(device) === 'SecuritySystem') {
- const members = SecuritySystem.getMembers(item);
+ const members = this.getMembers(device);
if (params.armLevel) {
if (SecuritySystem.armLevelMemberName in members) {
- return members[SecuritySystem.armLevelMemberName].name;
+ return members[SecuritySystem.armLevelMemberName];
}
throw { statusCode: 400 };
}
if (SecuritySystem.armedMemberName in members) {
- return members[SecuritySystem.armedMemberName].name;
+ return members[SecuritySystem.armedMemberName];
}
throw { statusCode: 400 };
}
- return item.name;
+ return device.id;
}
static requiresItem() {
@@ -84,7 +84,7 @@ class ArmDisarm extends DefaultCommand {
return {
ids: [device.id],
status: 'EXCEPTIONS',
- states: Object.assign({ online: true, currentStatusReport: report }, SecuritySystem.getState(item))
+ states: { online: true, currentStatusReport: report, ...SecuritySystem.getState(item) }
};
}
throw { errorCode: 'armFailure' };
diff --git a/functions/commands/brightnessabsolute.js b/functions/commands/brightnessabsolute.js
index fa0da7d4..0c558310 100644
--- a/functions/commands/brightnessabsolute.js
+++ b/functions/commands/brightnessabsolute.js
@@ -1,5 +1,4 @@
const DefaultCommand = require('./default.js');
-const SpecialColorLight = require('../devices/specialcolorlight.js');
class BrightnessAbsolute extends DefaultCommand {
static get type() {
@@ -10,19 +9,15 @@ class BrightnessAbsolute extends DefaultCommand {
return 'brightness' in params && typeof params.brightness === 'number';
}
- static requiresItem(device) {
- return this.getDeviceType(device) === 'SpecialColorLight';
- }
-
- static getItemName(item, device) {
+ static getItemName(device) {
if (this.getDeviceType(device) === 'SpecialColorLight') {
- const members = SpecialColorLight.getMembers(item);
+ const members = this.getMembers(device);
if ('lightBrightness' in members) {
- return members.lightBrightness.name;
+ return members.lightBrightness;
}
throw { statusCode: 400 };
}
- return item.name;
+ return device.id;
}
static convertParamsToValue(params) {
diff --git a/functions/commands/charge.js b/functions/commands/charge.js
index 12477009..231b9330 100644
--- a/functions/commands/charge.js
+++ b/functions/commands/charge.js
@@ -10,14 +10,10 @@ class Charge extends DefaultCommand {
return 'charge' in params && typeof params.charge === 'boolean';
}
- static requiresItem() {
- return true;
- }
-
- static getItemName(item) {
- const members = Charger.getMembers(item);
+ static getItemName(device) {
+ const members = this.getMembers(device);
if ('chargerCharging' in members) {
- return members.chargerCharging.name;
+ return members.chargerCharging;
}
throw { statusCode: 400 };
}
diff --git a/functions/commands/colorabsolute.js b/functions/commands/colorabsolute.js
index a07c79bd..79987774 100644
--- a/functions/commands/colorabsolute.js
+++ b/functions/commands/colorabsolute.js
@@ -1,5 +1,4 @@
const DefaultCommand = require('./default.js');
-const SpecialColorLight = require('../devices/specialcolorlight.js');
class ColorAbsolute extends DefaultCommand {
static get type() {
@@ -15,19 +14,15 @@ class ColorAbsolute extends DefaultCommand {
);
}
- static requiresItem(device) {
- return this.getDeviceType(device) === 'SpecialColorLight';
- }
-
- static getItemName(item, device) {
+ static getItemName(device) {
if (this.getDeviceType(device) === 'SpecialColorLight') {
- const members = SpecialColorLight.getMembers(item);
+ const members = this.getMembers(device);
if ('lightColor' in members) {
- return members.lightColor.name;
+ return members.lightColor;
}
throw { statusCode: 400 };
}
- return item.name;
+ return device.id;
}
static convertParamsToValue(params, _, device) {
diff --git a/functions/commands/colorabsolutetemperature.js b/functions/commands/colorabsolutetemperature.js
index db63e6ef..0063dfc0 100644
--- a/functions/commands/colorabsolutetemperature.js
+++ b/functions/commands/colorabsolutetemperature.js
@@ -1,5 +1,4 @@
const DefaultCommand = require('./default.js');
-const SpecialColorLight = require('../devices/specialcolorlight.js');
const { convertMired, convertRgbToHsv, convertKelvinToRgb } = require('../utilities.js');
class ColorAbsoluteTemperature extends DefaultCommand {
@@ -16,34 +15,35 @@ class ColorAbsoluteTemperature extends DefaultCommand {
);
}
- static requiresItem() {
- return true;
+ static requiresItem(device) {
+ return this.getDeviceType(device) !== 'SpecialColorLight';
}
- static getItemName(item, device) {
+ static getItemName(device) {
if (this.getDeviceType(device) === 'SpecialColorLight') {
- const members = SpecialColorLight.getMembers(item);
+ const members = this.getMembers(device);
if ('lightColorTemperature' in members) {
- return members.lightColorTemperature.name;
+ return members.lightColorTemperature;
}
throw { statusCode: 400 };
}
- return item.name;
+ return device.id;
}
static convertParamsToValue(params, item, device) {
if (this.getDeviceType(device) === 'SpecialColorLight') {
try {
- const colorUnit = SpecialColorLight.getColorUnit(item);
+ const customData = device.customData || {};
+ const colorUnit = customData.colorUnit;
if (colorUnit === 'kelvin') {
return params.color.temperature.toString();
}
if (colorUnit === 'mired') {
return convertMired(params.color.temperature).toString();
}
- const { temperatureMinK, temperatureMaxK } = SpecialColorLight.getAttributes(item).colorTemperatureRange;
+ const { temperatureMinK, temperatureMaxK } = customData.colorTemperatureRange;
let percent = ((params.color.temperature - temperatureMinK) / (temperatureMaxK - temperatureMinK)) * 100;
- if (SpecialColorLight.getColorTemperatureInverted(item)) {
+ if (customData.colorTemperatureInverted) {
percent = 100 - percent;
}
return percent.toString();
diff --git a/functions/commands/default.js b/functions/commands/default.js
index 9344e821..847935ed 100644
--- a/functions/commands/default.js
+++ b/functions/commands/default.js
@@ -79,12 +79,11 @@ class DefaultCommand {
}
/**
- * @param {object} item
* @param {object} device
* @param {object} params
*/
- static getItemName(item, device, params) {
- return item.name;
+ static getItemName(device, params) {
+ return device.id;
}
/**
@@ -101,6 +100,13 @@ class DefaultCommand {
return (device.customData && device.customData.itemType) || '';
}
+ /**
+ * @param {object} device
+ */
+ static getMembers(device) {
+ return (device.customData && device.customData.members) || {};
+ }
+
/**
* @param {object} device
*/
@@ -179,7 +185,7 @@ class DefaultCommand {
console.log(`openhabGoogleAssistant - ${this.type}: Waiting ${secondsToWait} second(s) for state to update`);
setTimeout(() => {
console.log(`openhabGoogleAssistant - ${this.type}: Finished Waiting`);
- resolve(null);
+ resolve(true);
}, secondsToWait * 1000);
});
}
@@ -236,12 +242,11 @@ class DefaultCommand {
return getItemPromise
.then((item) => {
- const targetItem = this.getItemName(item, device, params);
+ const targetItem = this.getItemName(device, params);
const targetValue = this.convertParamsToValue(params, item, device);
if (shouldCheckState) {
let currentState = this.getNormalizedState(item);
if (targetItem !== device.id && item.members && item.members.length) {
- // @ts-ignore
const member = item.members.find((m) => m.name === targetItem);
currentState = member ? this.getNormalizedState(member) : currentState;
}
diff --git a/functions/commands/medianext.js b/functions/commands/medianext.js
index 8d66943b..0abe75c0 100644
--- a/functions/commands/medianext.js
+++ b/functions/commands/medianext.js
@@ -1,19 +1,14 @@
const DefaultCommand = require('./default.js');
-const TV = require('../devices/tv.js');
class MediaNext extends DefaultCommand {
static get type() {
return 'action.devices.commands.mediaNext';
}
- static requiresItem() {
- return true;
- }
-
- static getItemName(item) {
- const members = TV.getMembers(item);
+ static getItemName(device) {
+ const members = this.getMembers(device);
if ('tvTransport' in members) {
- return members.tvTransport.name;
+ return members.tvTransport;
}
throw { statusCode: 400 };
}
diff --git a/functions/commands/mediapause.js b/functions/commands/mediapause.js
index b97b5dce..2c91e9fb 100644
--- a/functions/commands/mediapause.js
+++ b/functions/commands/mediapause.js
@@ -1,19 +1,14 @@
const DefaultCommand = require('./default.js');
-const TV = require('../devices/tv.js');
class MediaPause extends DefaultCommand {
static get type() {
return 'action.devices.commands.mediaPause';
}
- static requiresItem() {
- return true;
- }
-
- static getItemName(item) {
- const members = TV.getMembers(item);
+ static getItemName(device) {
+ const members = this.getMembers(device);
if ('tvTransport' in members) {
- return members.tvTransport.name;
+ return members.tvTransport;
}
throw { statusCode: 400 };
}
diff --git a/functions/commands/mediaprevious.js b/functions/commands/mediaprevious.js
index edf54cbd..295648f2 100644
--- a/functions/commands/mediaprevious.js
+++ b/functions/commands/mediaprevious.js
@@ -1,19 +1,14 @@
const DefaultCommand = require('./default.js');
-const TV = require('../devices/tv.js');
class MediaPrevious extends DefaultCommand {
static get type() {
return 'action.devices.commands.mediaPrevious';
}
- static requiresItem() {
- return true;
- }
-
- static getItemName(item) {
- const members = TV.getMembers(item);
+ static getItemName(device) {
+ const members = this.getMembers(device);
if ('tvTransport' in members) {
- return members.tvTransport.name;
+ return members.tvTransport;
}
throw { statusCode: 400 };
}
diff --git a/functions/commands/mediaresume.js b/functions/commands/mediaresume.js
index 74ad0a6b..fc490aee 100644
--- a/functions/commands/mediaresume.js
+++ b/functions/commands/mediaresume.js
@@ -1,19 +1,14 @@
const DefaultCommand = require('./default.js');
-const TV = require('../devices/tv.js');
class MediaResume extends DefaultCommand {
static get type() {
return 'action.devices.commands.mediaResume';
}
- static requiresItem() {
- return true;
- }
-
- static getItemName(item) {
- const members = TV.getMembers(item);
+ static getItemName(device) {
+ const members = this.getMembers(device);
if ('tvTransport' in members) {
- return members.tvTransport.name;
+ return members.tvTransport;
}
throw { statusCode: 400 };
}
diff --git a/functions/commands/mute.js b/functions/commands/mute.js
index 7f1ea0f1..077f6191 100644
--- a/functions/commands/mute.js
+++ b/functions/commands/mute.js
@@ -1,5 +1,4 @@
const DefaultCommand = require('./default.js');
-const TV = require('../devices/tv.js');
class Mute extends DefaultCommand {
static get type() {
@@ -10,32 +9,26 @@ class Mute extends DefaultCommand {
return 'mute' in params && typeof params.mute === 'boolean';
}
- static requiresItem(device) {
- return this.getDeviceType(device) === 'TV';
- }
-
- static getItemName(item, device) {
+ static getItemName(device) {
if (this.getDeviceType(device) === 'TV') {
- const members = TV.getMembers(item);
+ const members = this.getMembers(device);
if ('tvMute' in members) {
- return members.tvMute.name;
+ return members.tvMute;
}
if ('tvVolume' in members) {
- return members.tvVolume.name;
+ return members.tvVolume;
}
throw { statusCode: 400 };
}
- return item.name;
+ return device.id;
}
static convertParamsToValue(params, item, device) {
let itemType = this.getItemType(device);
if (this.getDeviceType(device) === 'TV') {
- const members = TV.getMembers(item);
+ const members = this.getMembers(device);
if ('tvMute' in members) {
itemType = 'Switch';
- } else if ('tvVolume' in members) {
- itemType = 'Dimmer';
}
}
let mute = params.mute;
diff --git a/functions/commands/onoff.js b/functions/commands/onoff.js
index 135b6aba..0cb54e8e 100644
--- a/functions/commands/onoff.js
+++ b/functions/commands/onoff.js
@@ -1,6 +1,4 @@
const DefaultCommand = require('./default.js');
-const SpecialColorLight = require('../devices/specialcolorlight.js');
-const TV = require('../devices/tv.js');
class OnOff extends DefaultCommand {
static get type() {
@@ -11,30 +9,34 @@ class OnOff extends DefaultCommand {
return 'on' in params && typeof params.on === 'boolean';
}
- static requiresItem(device) {
- return ['SpecialColorLight', 'TV'].includes(this.getDeviceType(device));
- }
-
- static getItemName(item, device) {
+ static getItemName(device) {
const deviceType = this.getDeviceType(device);
+ if (deviceType.startsWith('DynamicModes')) {
+ throw { statusCode: 400 };
+ }
+ const members = this.getMembers(device);
if (deviceType === 'SpecialColorLight') {
- const members = SpecialColorLight.getMembers(item);
if ('lightPower' in members) {
- return members.lightPower.name;
+ return members.lightPower;
}
if ('lightBrightness' in members) {
- return members.lightBrightness.name;
+ return members.lightBrightness;
}
throw { statusCode: 400 };
}
if (deviceType === 'TV') {
- const members = TV.getMembers(item);
if ('tvPower' in members) {
- return members.tvPower.name;
+ return members.tvPower;
+ }
+ throw { statusCode: 400 };
+ }
+ if (['AirPurifier', 'Fan', 'Hood', 'ACUnit'].includes(deviceType) && this.getItemType(device) === 'Group') {
+ if ('fanPower' in members) {
+ return members.fanPower;
}
throw { statusCode: 400 };
}
- return item.name;
+ return device.id;
}
static convertParamsToValue(params, _, device) {
diff --git a/functions/commands/selectchannel.js b/functions/commands/selectchannel.js
index ae1bf0df..363b2144 100644
--- a/functions/commands/selectchannel.js
+++ b/functions/commands/selectchannel.js
@@ -18,10 +18,10 @@ class SelectChannel extends DefaultCommand {
return true;
}
- static getItemName(item) {
- const members = TV.getMembers(item);
+ static getItemName(device) {
+ const members = this.getMembers(device);
if ('tvChannel' in members) {
- return members.tvChannel.name;
+ return members.tvChannel;
}
throw { statusCode: 400 };
}
@@ -33,7 +33,7 @@ class SelectChannel extends DefaultCommand {
}
const search = params.channelName || params.channelCode;
for (const number in channelMap) {
- if (channelMap[number].includes(search)) {
+ if (channelMap[number].some((name) => name.toLowerCase() === search.toLowerCase())) {
return number;
}
}
diff --git a/functions/commands/setfanspeed.js b/functions/commands/setfanspeed.js
index b45df871..5f1dfc50 100644
--- a/functions/commands/setfanspeed.js
+++ b/functions/commands/setfanspeed.js
@@ -6,17 +6,36 @@ class SetFanSpeed extends DefaultCommand {
}
static validateParams(params) {
- return 'fanSpeed' in params && typeof params.fanSpeed === 'string';
+ return (
+ ('fanSpeed' in params && typeof params.fanSpeed === 'string') ||
+ ('fanSpeedPercent' in params && typeof params.fanSpeedPercent === 'number')
+ );
+ }
+
+ static getItemName(device) {
+ const deviceType = this.getDeviceType(device);
+ if (['AirPurifier', 'Fan', 'Hood', 'ACUnit'].includes(deviceType) && this.getItemType(device) === 'Group') {
+ const members = this.getMembers(device);
+ if ('fanSpeed' in members) {
+ return members.fanSpeed;
+ }
+ throw { statusCode: 400 };
+ }
+ return device.id;
}
static convertParamsToValue(params) {
- return params.fanSpeed.toString();
+ return (params.fanSpeed || params.fanSpeedPercent).toString();
}
static getResponseStates(params) {
- return {
- currentFanSpeedSetting: params.fanSpeed
+ const states = {
+ currentFanSpeedPercent: params.fanSpeedPercent || Number(params.fanSpeed)
};
+ if ('fanSpeed' in params) {
+ states.currentFanSpeedSetting = params.fanSpeed;
+ }
+ return states;
}
}
diff --git a/functions/commands/setfanspeedpercent.js b/functions/commands/setfanspeedpercent.js
deleted file mode 100644
index f100ed7e..00000000
--- a/functions/commands/setfanspeedpercent.js
+++ /dev/null
@@ -1,23 +0,0 @@
-const DefaultCommand = require('./default.js');
-
-class SetFanSpeedPercent extends DefaultCommand {
- static get type() {
- return 'action.devices.commands.SetFanSpeed';
- }
-
- static validateParams(params) {
- return 'fanSpeedPercent' in params && typeof params.fanSpeedPercent === 'number';
- }
-
- static convertParamsToValue(params) {
- return params.fanSpeedPercent.toString();
- }
-
- static getResponseStates(params) {
- return {
- currentFanSpeedPercent: params.fanSpeedPercent
- };
- }
-}
-
-module.exports = SetFanSpeedPercent;
diff --git a/functions/commands/setinput.js b/functions/commands/setinput.js
index 7459dd3f..db9f2a62 100644
--- a/functions/commands/setinput.js
+++ b/functions/commands/setinput.js
@@ -1,5 +1,4 @@
const DefaultCommand = require('./default.js');
-const TV = require('../devices/tv.js');
class SetInput extends DefaultCommand {
static get type() {
@@ -10,14 +9,10 @@ class SetInput extends DefaultCommand {
return 'newInput' in params && typeof params.newInput === 'string';
}
- static requiresItem() {
- return true;
- }
-
- static getItemName(item) {
- const members = TV.getMembers(item);
+ static getItemName(device) {
+ const members = this.getMembers(device);
if ('tvInput' in members) {
- return members.tvInput.name;
+ return members.tvInput;
}
throw { statusCode: 400 };
}
diff --git a/functions/commands/setmodes.js b/functions/commands/setmodes.js
new file mode 100644
index 00000000..79a57932
--- /dev/null
+++ b/functions/commands/setmodes.js
@@ -0,0 +1,45 @@
+const DefaultCommand = require('./default.js');
+
+class SetModes extends DefaultCommand {
+ static get type() {
+ return 'action.devices.commands.SetModes';
+ }
+
+ static validateParams(params) {
+ return 'updateModeSettings' in params && typeof params.updateModeSettings === 'object';
+ }
+
+ static getItemName(device) {
+ const deviceType = this.getDeviceType(device);
+ const members = this.getMembers(device);
+ if (deviceType.startsWith('DynamicModes')) {
+ if ('modesCurrentMode' in members) {
+ return members.modesCurrentMode;
+ }
+ throw { statusCode: 400 };
+ }
+ if (['AirPurifier', 'Fan', 'Hood', 'ACUnit'].includes(deviceType)) {
+ if ('fanMode' in members) {
+ return members.fanMode;
+ }
+ throw { statusCode: 400 };
+ }
+ return device.id;
+ }
+
+ static convertParamsToValue(params) {
+ const mode = Object.keys(params.updateModeSettings)[0];
+ return params.updateModeSettings[mode].toString();
+ }
+
+ static getResponseStates(params) {
+ const mode = Object.keys(params.updateModeSettings)[0];
+ return {
+ currentModeSettings: {
+ [mode]: params.updateModeSettings[mode]
+ }
+ };
+ }
+}
+
+module.exports = SetModes;
diff --git a/functions/commands/setvolume.js b/functions/commands/setvolume.js
index bc95ce29..a01daf95 100644
--- a/functions/commands/setvolume.js
+++ b/functions/commands/setvolume.js
@@ -1,5 +1,4 @@
const DefaultCommand = require('./default.js');
-const TV = require('../devices/tv.js');
class SetVolume extends DefaultCommand {
static get type() {
@@ -10,19 +9,15 @@ class SetVolume extends DefaultCommand {
return 'volumeLevel' in params && typeof params.volumeLevel === 'number';
}
- static requiresItem(device) {
- return this.getDeviceType(device) === 'TV';
- }
-
- static getItemName(item, device) {
+ static getItemName(device) {
if (this.getDeviceType(device) === 'TV') {
- const members = TV.getMembers(item);
+ const members = this.getMembers(device);
if ('tvVolume' in members) {
- return members.tvVolume.name;
+ return members.tvVolume;
}
throw { statusCode: 400 };
}
- return item.name;
+ return device.id;
}
static convertParamsToValue(params) {
diff --git a/functions/commands/thermostatsetmode.js b/functions/commands/thermostatsetmode.js
index 13675617..6f429973 100644
--- a/functions/commands/thermostatsetmode.js
+++ b/functions/commands/thermostatsetmode.js
@@ -14,10 +14,10 @@ class ThermostatSetMode extends DefaultCommand {
return true;
}
- static getItemName(item) {
- const members = Thermostat.getMembers(item);
+ static getItemName(device) {
+ const members = this.getMembers(device);
if ('thermostatMode' in members) {
- return members.thermostatMode.name;
+ return members.thermostatMode;
}
throw { statusCode: 400 };
}
diff --git a/functions/commands/thermostattemperaturesetpoint.js b/functions/commands/thermostattemperaturesetpoint.js
index ffff3131..22ea70d0 100644
--- a/functions/commands/thermostattemperaturesetpoint.js
+++ b/functions/commands/thermostattemperaturesetpoint.js
@@ -15,10 +15,10 @@ class ThermostatTemperatureSetpoint extends DefaultCommand {
return true;
}
- static getItemName(item) {
- const members = Thermostat.getMembers(item);
+ static getItemName(device) {
+ const members = this.getMembers(device);
if ('thermostatTemperatureSetpoint' in members) {
- return members.thermostatTemperatureSetpoint.name;
+ return members.thermostatTemperatureSetpoint;
}
throw { statusCode: 400 };
}
diff --git a/functions/commands/thermostattemperaturesetpointhigh.js b/functions/commands/thermostattemperaturesetpointhigh.js
index 610563e8..4f35769d 100644
--- a/functions/commands/thermostattemperaturesetpointhigh.js
+++ b/functions/commands/thermostattemperaturesetpointhigh.js
@@ -17,10 +17,10 @@ class ThermostatTemperatureSetpointHigh extends DefaultCommand {
return true;
}
- static getItemName(item) {
- const members = Thermostat.getMembers(item);
+ static getItemName(device) {
+ const members = this.getMembers(device);
if ('thermostatTemperatureSetpointHigh' in members) {
- return members.thermostatTemperatureSetpointHigh.name;
+ return members.thermostatTemperatureSetpointHigh;
}
throw { statusCode: 400 };
}
diff --git a/functions/commands/thermostattemperaturesetpointlow.js b/functions/commands/thermostattemperaturesetpointlow.js
index cb36a7a7..86ee08f6 100644
--- a/functions/commands/thermostattemperaturesetpointlow.js
+++ b/functions/commands/thermostattemperaturesetpointlow.js
@@ -15,10 +15,10 @@ class ThermostatTemperatureSetpointLow extends DefaultCommand {
return true;
}
- static getItemName(item) {
- const members = Thermostat.getMembers(item);
+ static getItemName(device) {
+ const members = this.getMembers(device);
if ('thermostatTemperatureSetpointLow' in members) {
- return members.thermostatTemperatureSetpointLow.name;
+ return members.thermostatTemperatureSetpointLow;
}
throw { statusCode: 400 };
}
diff --git a/functions/commands/volumerelative.js b/functions/commands/volumerelative.js
index 57a61b9c..f3cb2b72 100644
--- a/functions/commands/volumerelative.js
+++ b/functions/commands/volumerelative.js
@@ -14,15 +14,15 @@ class VolumeRelative extends DefaultCommand {
return true;
}
- static getItemName(item, device) {
+ static getItemName(device) {
if (this.getDeviceType(device) === 'TV') {
- const members = TV.getMembers(item);
+ const members = this.getMembers(device);
if ('tvVolume' in members) {
- return members.tvVolume.name;
+ return members.tvVolume;
}
throw { statusCode: 400 };
}
- return item.name;
+ return device.id;
}
static convertParamsToValue(params, item, device) {
@@ -35,7 +35,7 @@ class VolumeRelative extends DefaultCommand {
throw { statusCode: 400 };
}
}
- let level = parseInt(state) + params.relativeSteps;
+ const level = parseInt(state) + params.relativeSteps;
return (level < 0 ? 0 : level > 100 ? 100 : level).toString();
}
diff --git a/functions/config.js b/functions/config.js
index 580cb983..57d5f2db 100644
--- a/functions/config.js
+++ b/functions/config.js
@@ -31,9 +31,9 @@
* path
* Base URL path for openHAB items
*
- **/
+ * */
module.exports = {
- //userpass: process.env.OH_USERPASS || 'user@foo.com:Password1',
+ // userpass: process.env.OH_USERPASS || 'user@foo.com:Password1',
host: process.env.OH_HOST || '',
port: parseInt(process.env.OH_PORT) || 443,
path: process.env.OH_PATH || '/YOUR/REST/ENDPOINT'
diff --git a/functions/devices/acunit.js b/functions/devices/acunit.js
new file mode 100644
index 00000000..8fb1b572
--- /dev/null
+++ b/functions/devices/acunit.js
@@ -0,0 +1,41 @@
+const DefaultDevice = require('./default.js');
+const Fan = require('./fan');
+const Thermostat = require('./thermostat');
+
+class ACUnit extends DefaultDevice {
+ static get type() {
+ return 'action.devices.types.AC_UNIT';
+ }
+
+ static getTraits(item) {
+ return [...Fan.getTraits(item), ...Thermostat.getTraits()];
+ }
+
+ static get requiredItemTypes() {
+ return ['Group'];
+ }
+
+ static matchesDeviceType(item) {
+ return super.matchesDeviceType(item) && Object.keys(this.getMembers(item)).length > 0;
+ }
+
+ static getAttributes(item) {
+ return {
+ ...Fan.getAttributes(item),
+ ...Thermostat.getAttributes(item)
+ };
+ }
+
+ static getState(item) {
+ return {
+ ...Fan.getState(item),
+ ...Thermostat.getState(item)
+ };
+ }
+
+ static get supportedMembers() {
+ return [...Fan.supportedMembers, ...Thermostat.supportedMembers];
+ }
+}
+
+module.exports = ACUnit;
diff --git a/functions/devices/camera.js b/functions/devices/camera.js
index 7932384b..2f6429e5 100644
--- a/functions/devices/camera.js
+++ b/functions/devices/camera.js
@@ -15,7 +15,7 @@ class Camera extends DefaultDevice {
cameraStreamSupportedProtocols: (config.protocols || 'hls,dash,smooth_stream,progressive_mp4')
.split(',')
.map((s) => s.trim()),
- cameraStreamNeedAuthToken: config.token ? true : false,
+ cameraStreamNeedAuthToken: !!config.token,
cameraStreamNeedDrmEncryption: false
};
}
diff --git a/functions/devices/charger.js b/functions/devices/charger.js
index c2f00111..9db3480a 100644
--- a/functions/devices/charger.js
+++ b/functions/devices/charger.js
@@ -9,8 +9,12 @@ class Charger extends DefaultDevice {
return ['action.devices.traits.EnergyStorage'];
}
- static matchesItemType(item) {
- return item.type === 'Group' && Object.keys(this.getMembers(item)).length > 0;
+ static get requiredItemTypes() {
+ return ['Group'];
+ }
+
+ static matchesDeviceType(item) {
+ return super.matchesDeviceType(item) && Object.keys(this.getMembers(item)).length > 0;
}
static getAttributes(item) {
@@ -74,25 +78,13 @@ class Charger extends DefaultDevice {
return state;
}
- static getMembers(item) {
- const supportedMembers = [
- 'chargerCharging',
- 'chargerPluggedIn',
- 'chargerCapacityRemaining',
- 'chargerCapacityUntilFull'
+ static get supportedMembers() {
+ return [
+ { name: 'chargerCharging', types: ['Switch'] },
+ { name: 'chargerPluggedIn', types: ['Switch'] },
+ { name: 'chargerCapacityRemaining', types: ['Number', 'Dimmer'] },
+ { name: 'chargerCapacityUntilFull', types: ['Number', 'Dimmer'] }
];
- const members = {};
- if (item.members && item.members.length) {
- item.members.forEach((member) => {
- if (member.metadata && member.metadata.ga) {
- const memberType = supportedMembers.find((m) => member.metadata.ga.value.toLowerCase() === m.toLowerCase());
- if (memberType) {
- members[memberType] = { name: member.name, state: member.state };
- }
- }
- });
- }
- return members;
}
}
diff --git a/functions/devices/climatesensor.js b/functions/devices/climatesensor.js
index c6cf7c64..444954d6 100644
--- a/functions/devices/climatesensor.js
+++ b/functions/devices/climatesensor.js
@@ -23,6 +23,21 @@ class ClimateSensor extends DefaultDevice {
attributes.temperatureUnitForUX = this.useFahrenheit(item) ? 'F' : 'C';
attributes.queryOnlyTemperatureSetting = true;
attributes.thermostatTemperatureUnit = this.useFahrenheit(item) === true ? 'F' : 'C';
+ attributes.temperatureRange = {
+ minThresholdCelsius: -100,
+ maxThresholdCelsius: 100
+ };
+
+ const config = this.getConfig(item);
+ if ('temperatureRange' in config) {
+ const [min, max] = config.temperatureRange.split(',').map((s) => parseFloat(s.trim()));
+ if (!isNaN(min) && !isNaN(max)) {
+ attributes.temperatureRange = {
+ minThresholdCelsius: min,
+ maxThresholdCelsius: max
+ };
+ }
+ }
}
if ('humidityAmbient' in members) {
attributes.queryOnlyHumiditySetting = true;
@@ -30,15 +45,15 @@ class ClimateSensor extends DefaultDevice {
return attributes;
}
- static matchesItemType(item) {
- return item.type === 'Group' && Object.keys(this.getMembers(item)).length > 0;
+ static get requiredItemTypes() {
+ return ['Group'];
}
- static isCompatible(item = {}) {
+ static matchesDeviceType(item) {
return (
item.metadata &&
item.metadata.ga &&
- item.metadata.ga.value.toLowerCase() == 'climatesensor' &&
+ item.metadata.ga.value.toLowerCase() === 'climatesensor' &&
Object.keys(this.getMembers(item)).length > 0
);
}
@@ -56,30 +71,20 @@ class ClimateSensor extends DefaultDevice {
state.temperatureSetpointCelsius = temperature;
}
if ('humidityAmbient' in members) {
- const humidity = Math.round(parseFloat(members.humidityAmbient.state));
+ const config = this.getConfig(item);
+ const maxHumidity = (config.maxHumidity && parseInt(config.maxHumidity)) || 100;
+ const humidity = Math.round(parseFloat(members.humidityAmbient.state) * (100 / maxHumidity));
state.humidityAmbientPercent = humidity;
state.humiditySetpointPercent = humidity;
}
return state;
}
- /**
- * @returns {object}
- */
- static getMembers(item) {
- const supportedMembers = ['temperatureAmbient', 'humidityAmbient'];
- const members = {};
- if (item.members && item.members.length) {
- item.members.forEach((member) => {
- if (member.metadata && member.metadata.ga) {
- const memberType = supportedMembers.find((m) => member.metadata.ga.value.toLowerCase() === m.toLowerCase());
- if (memberType) {
- members[memberType] = { name: member.name, state: member.state };
- }
- }
- });
- }
- return members;
+ static get supportedMembers() {
+ return [
+ { name: 'temperatureAmbient', types: ['Number'] },
+ { name: 'humidityAmbient', types: ['Number'] }
+ ];
}
static useFahrenheit(item) {
diff --git a/functions/devices/default.js b/functions/devices/default.js
index 96de4001..f0ac0b35 100644
--- a/functions/devices/default.js
+++ b/functions/devices/default.js
@@ -25,8 +25,8 @@ class DefaultDevice {
* @param {object} item
* @returns {boolean}
*/
- static isCompatible(item) {
- return (
+ static matchesDeviceType(item) {
+ return !!(
item.metadata &&
item.metadata.ga &&
this.type.toLowerCase() === `action.devices.types.${item.metadata.ga.value}`.toLowerCase()
@@ -37,7 +37,7 @@ class DefaultDevice {
* @param {object} item
*/
static matchesItemType(item) {
- return (
+ return !!(
!this.requiredItemTypes.length ||
this.requiredItemTypes.includes((item.groupType || item.type || '').split(':')[0])
);
@@ -111,6 +111,13 @@ class DefaultDevice {
if (config.waitForStateChange) {
metadata.customData.waitForStateChange = parseInt(config.waitForStateChange);
}
+ if (this.supportedMembers.length) {
+ const members = this.getMembers(item);
+ metadata.customData.members = {};
+ for (const member in members) {
+ metadata.customData.members[member] = members[member].name;
+ }
+ }
return metadata;
}
@@ -120,6 +127,35 @@ class DefaultDevice {
static getState(item) {
return {};
}
+
+ /**
+ * @returns {Array