zigbee-herdsman-converters 14.0.511 → 14.0.512

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -798,7 +798,7 @@ const converters = {
798
798
  const {message} = meta;
799
799
  const transition = utils.getTransition(entity, 'brightness', meta);
800
800
  const turnsOffAtBrightness1 = utils.getMetaValue(entity, meta.mapped, 'turnsOffAtBrightness1', 'allEqual', false);
801
- let state = message.hasOwnProperty('state') ? message.state.toLowerCase() : undefined;
801
+ let state = message.hasOwnProperty('state') ? (message.state === null ? null : message.state.toLowerCase()) : undefined;
802
802
  let brightness = undefined;
803
803
  if (message.hasOwnProperty('brightness')) {
804
804
  brightness = Number(message.brightness);
@@ -806,91 +806,106 @@ const converters = {
806
806
  brightness = utils.mapNumberRange(Number(message.brightness_percent), 0, 100, 0, 255);
807
807
  }
808
808
 
809
- if (brightness !== undefined && (isNaN(brightness) || brightness < 0 || brightness > 255)) {
810
- // Allow 255 value, changing this to 254 would be a breaking change.
809
+ if (brightness === 255) {
810
+ // Allow 255 for backwards compatibility.
811
+ brightness = 254;
812
+ }
813
+
814
+ if (brightness !== undefined && (isNaN(brightness) || brightness < 0 || brightness > 254)) {
811
815
  throw new Error(`Brightness value of message: '${JSON.stringify(message)}' invalid, must be a number >= 0 and =< 254`);
812
816
  }
813
817
 
814
- if (state !== undefined && ['on', 'off', 'toggle'].includes(state) === false) {
818
+ if (state !== undefined && state !== null && ['on', 'off', 'toggle'].includes(state) === false) {
815
819
  throw new Error(`State value of message: '${JSON.stringify(message)}' invalid, must be 'ON', 'OFF' or 'TOGGLE'`);
816
820
  }
817
821
 
818
- if (state === 'toggle' || state === 'off' || (brightness === undefined && state === 'on')) {
819
- if (transition.specified) {
820
- if (state === 'toggle') {
821
- state = meta.state.state === 'ON' ? 'off' : 'on';
822
- }
823
-
824
- if (state === 'off' && meta.state.brightness && meta.state.state === 'ON') {
825
- // https://github.com/Koenkk/zigbee2mqtt/issues/2850#issuecomment-580365633
826
- // We need to remember the state before turning the device off as we need to restore
827
- // it once we turn it on again.
828
- // We cannot rely on the meta.state as when reporting is enabled the bulb will reports
829
- // it brightness while decreasing the brightness.
830
- globalStore.putValue(entity, 'brightness', meta.state.brightness);
831
- globalStore.putValue(entity, 'turnedOffWithTransition', true);
832
- }
833
-
834
- const fallbackLevel = utils.getObjectProperty(meta.state, 'brightness', 254);
835
- let level = state === 'off' ? 0 : globalStore.getValue(entity, 'brightness', fallbackLevel);
836
- if (state === 'on' && level === 0) level = turnsOffAtBrightness1 ? 2 : 1;
837
-
838
- const payload = {level, transtime: transition.time};
839
- await entity.command('genLevelCtrl', 'moveToLevelWithOnOff', payload, utils.getOptions(meta.mapped, entity));
840
- const result = {state: {state: state.toUpperCase()}};
841
- if (state === 'on') result.state.brightness = level;
842
- return result;
843
- } else {
844
- if (state === 'on' && globalStore.getValue(entity, 'turnedOffWithTransition') === true) {
845
- /**
846
- * In case the bulb it turned OFF with a transition and turned ON WITHOUT
847
- * a transition, the brightness is not recovered as it turns on with brightness 1.
848
- * https://github.com/Koenkk/zigbee-herdsman-converters/issues/1073
849
- */
850
- globalStore.putValue(entity, 'turnedOffWithTransition', false);
851
- await entity.command(
852
- 'genLevelCtrl',
853
- 'moveToLevelWithOnOff',
854
- {level: globalStore.getValue(entity, 'brightness'), transtime: 0},
855
- utils.getOptions(meta.mapped, entity),
856
- );
857
- return {state: {state: 'ON'}, readAfterWriteTime: transition * 100};
858
- } else {
859
- // Store brightness where the bulb was turned off with as we need it when the bulb is turned on
860
- // with transition.
861
- if (meta.state.hasOwnProperty('brightness') && state === 'off') {
862
- globalStore.putValue(entity, 'brightness', meta.state.brightness);
863
- globalStore.putValue(entity, 'turnedOffWithTransition', false);
864
- }
865
-
866
- const result = await converters.on_off.convertSet(entity, 'state', state, meta);
867
- result.readAfterWriteTime = 0;
868
- if (result.state && result.state.state === 'ON' && meta.state.brightness === 0) {
869
- result.state.brightness = 1;
822
+ if ((state === undefined || state === null) && brightness === undefined) {
823
+ throw new Error(`At least one of "brightness" or "state" must have a value: '${JSON.stringify(message)}'`);
824
+ }
825
+
826
+ // Infer state from desired brightness if unset. Ideally we'd want to keep it as it is, but this code has always
827
+ // used 'MoveToLevelWithOnOff' so that'd break backwards compatibility. To keep the state, the user
828
+ // has to explicitly set it to null.
829
+ if (state === undefined) {
830
+ // Also write to `meta.message.state` in case we delegate to the `on_off` converter.
831
+ state = meta.message.state = brightness === 0 ? 'off' : 'on';
832
+ }
833
+
834
+ const targetState = state === 'toggle' ? (meta.state.state === 'ON' ? 'off' : 'on') : state;
835
+ if (targetState === 'off') {
836
+ // Simulate 'Off' with transition via 'MoveToLevelWithOnOff', otherwise just use 'Off'.
837
+ // TODO: if this is a group where some members don't support Level Control, turning them off
838
+ // with transition may have no effect. (Some devices, such as Envilar ZG302-BOX-RELAY, handle
839
+ // 'MoveToLevelWithOnOff' despite not supporting the cluster; others, like the LEDVANCE SMART+
840
+ // plug, do not.)
841
+ brightness = transition.specified || brightness === 0 ? 0 : undefined;
842
+ if (meta.state.hasOwnProperty('brightness') && meta.state.state === 'ON') {
843
+ // The light's current level gets clobbered in two cases:
844
+ // 1. when 'Off' has a transition, in which case it is really 'MoveToLevelWithOnOff'
845
+ // https://github.com/Koenkk/zigbee-herdsman-converters/issues/1073
846
+ // 2. when 'OnLevel' is set: "If OnLevel is not defined, set the CurrentLevel to the stored level."
847
+ // https://github.com/Koenkk/zigbee2mqtt/issues/2850#issuecomment-580365633
848
+ // We need to remember current brightness in case the next 'On' does not provide it. `meta` is not reliable
849
+ // here, as it will get clobbered too if reporting is configured.
850
+ globalStore.putValue(entity, 'brightness', meta.state.brightness);
851
+ globalStore.putValue(entity, 'turnedOffWithTransition', brightness !== undefined);
852
+ }
853
+ } else if (targetState === 'on' && brightness === undefined) {
854
+ // Simulate 'On' with transition via 'MoveToLevelWithOnOff', or restore the level from before
855
+ // it was clobbered by a previous transition to off; otherwise just use 'On'.
856
+ // TODO: same problem as above.
857
+ // TODO: if transition is not specified, should use device default (OnTransitionTime), not 0.
858
+ if (transition.specified || globalStore.getValue(entity, 'turnedOffWithTransition') === true) {
859
+ const current = utils.getObjectProperty(meta.state, 'brightness', 254);
860
+ brightness = globalStore.getValue(entity, 'brightness', current);
861
+ try {
862
+ const attributeRead = await entity.read('genLevelCtrl', ['onLevel']);
863
+ // TODO: for groups, `read` does not wait for responses. If it did, we could still issue a single
864
+ // command if all values of `OnLevel` are equal, or split into one command per device if not.
865
+ if (attributeRead !== undefined && attributeRead['onLevel'] != 255) {
866
+ brightness = attributeRead['onLevel'];
870
867
  }
871
-
872
- return result;
868
+ } catch (e) {
869
+ // OnLevel not supported
873
870
  }
874
871
  }
875
- } else {
876
- brightness = Math.min(254, brightness);
877
- if (brightness === 1 && turnsOffAtBrightness1) {
878
- brightness = 2;
872
+ }
873
+
874
+ if (brightness === undefined) {
875
+ const result = await converters.on_off.convertSet(entity, 'state', state, meta);
876
+ result.readAfterWriteTime = 0;
877
+ if (result.state && result.state.state === 'ON' && meta.state.brightness === 0) {
878
+ result.state.brightness = 1;
879
879
  }
880
+ return result;
881
+ }
882
+
883
+ if (brightness === 0 && (targetState === 'on' || state === null)) {
884
+ brightness = 1;
885
+ }
886
+ if (brightness === 1 && turnsOffAtBrightness1) {
887
+ brightness = 2;
888
+ }
880
889
 
890
+ if (targetState !== 'off') {
881
891
  globalStore.putValue(entity, 'brightness', brightness);
882
- await entity.command(
883
- 'genLevelCtrl',
884
- 'moveToLevelWithOnOff',
885
- {level: Number(brightness), transtime: transition.time},
886
- utils.getOptions(meta.mapped, entity),
887
- );
892
+ globalStore.clearValue(entity, 'turnedOffWithTransition');
893
+ }
894
+ await entity.command(
895
+ 'genLevelCtrl',
896
+ state === null ? 'moveToLevel' : 'moveToLevelWithOnOff',
897
+ {level: Number(brightness), transtime: transition.time},
898
+ utils.getOptions(meta.mapped, entity),
899
+ );
888
900
 
889
- return {
890
- state: {state: brightness === 0 ? 'OFF' : 'ON', brightness: Number(brightness)},
891
- readAfterWriteTime: transition.time * 100,
892
- };
901
+ const result = {state: {}, readAfterWriteTime: transition.time * 100};
902
+ if (brightness !== 0) {
903
+ result.state.brightness = Number(brightness);
893
904
  }
905
+ if (state !== null) {
906
+ result.state.state = brightness === 0 ? 'OFF' : 'ON';
907
+ }
908
+ return result;
894
909
  },
895
910
  convertGet: async (entity, key, meta) => {
896
911
  if (key === 'brightness') {
@@ -2645,6 +2645,15 @@ module.exports = [
2645
2645
  extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
2646
2646
  ota: ota.zigbeeOTA,
2647
2647
  },
2648
+ {
2649
+ zigbeeModel: ['929003045601_01', '929003045601_02'],
2650
+ model: '8719514338142',
2651
+ vendor: 'Philips',
2652
+ description: 'Hue White ambiance Runner double spotlight',
2653
+ meta: {turnsOffAtBrightness1: true},
2654
+ extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
2655
+ ota: ota.zigbeeOTA,
2656
+ },
2648
2657
  {
2649
2658
  zigbeeModel: ['5047230P6', '5047230P6'],
2650
2659
  model: '5047230P6',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.511",
3
+ "version": "14.0.512",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [