diff --git a/front/src/routes/scene/edit-scene/actions/TurnOnOffLightParams.jsx b/front/src/routes/scene/edit-scene/actions/TurnOnOffLightParams.jsx
index 51a97d818f..7d444ac7dc 100644
--- a/front/src/routes/scene/edit-scene/actions/TurnOnOffLightParams.jsx
+++ b/front/src/routes/scene/edit-scene/actions/TurnOnOffLightParams.jsx
@@ -4,6 +4,8 @@ import { Text } from 'preact-i18n';
import Select from 'react-select';
import { ACTIONS } from '../../../../../../server/utils/constants';
+import { getDeviceFeatureName } from '../../../../utils/device';
+import withIntlAsProp from '../../../../utils/withIntlAsProp';
class TurnOnOffLight extends Component {
getOptions = async () => {
@@ -12,13 +14,20 @@ class TurnOnOffLight extends Component {
device_feature_category: 'light',
device_feature_type: 'binary'
});
- const deviceOptions = devices.map(device => ({
- value: device.selector,
- label: device.name
- }));
- await this.setState({ deviceOptions });
+ // keep only write lights, not read only
+ const deviceFeatureOptions = [];
+ devices.forEach(device => {
+ device.features
+ .filter(deviceFeature => deviceFeature.read_only === false)
+ .map(deviceFeature => ({
+ value: deviceFeature.selector,
+ label: getDeviceFeatureName(this.props.intl.dictionary, device, deviceFeature)
+ }))
+ .forEach(deviceFeatureOption => deviceFeatureOptions.push(deviceFeatureOption));
+ });
+ await this.setState({ deviceFeatureOptions });
this.refreshSelectedOptions(this.props);
- return deviceOptions;
+ return deviceFeatureOptions;
} catch (e) {
console.error(e);
}
@@ -26,18 +35,20 @@ class TurnOnOffLight extends Component {
handleChange = selectedOptions => {
if (selectedOptions) {
const lights = selectedOptions.map(selectedOption => selectedOption.value);
- this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'devices', lights);
+ this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'device_features', lights);
} else {
- this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'devices', []);
+ this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'device_features', []);
}
};
refreshSelectedOptions = nextProps => {
const selectedOptions = [];
- if (nextProps.action.devices && this.state.deviceOptions) {
- nextProps.action.devices.forEach(light => {
- const deviceOption = this.state.deviceOptions.find(deviceOption => deviceOption.value === light);
- if (deviceOption) {
- selectedOptions.push(deviceOption);
+ if (nextProps.action.device_features && this.state.deviceFeatureOptions) {
+ nextProps.action.device_features.forEach(light => {
+ const deviceFeatureOption = this.state.deviceFeatureOptions.find(
+ deviceFeatureOption => deviceFeatureOption.value === light
+ );
+ if (deviceFeatureOption) {
+ selectedOptions.push(deviceFeatureOption);
}
});
}
@@ -46,7 +57,7 @@ class TurnOnOffLight extends Component {
constructor(props) {
super(props);
this.state = {
- deviceOptions: null,
+ deviceFeatureOptions: null,
selectedOptions: []
};
}
@@ -58,7 +69,7 @@ class TurnOnOffLight extends Component {
this.refreshSelectedOptions(nextProps);
}
- render(props, { selectedOptions, deviceOptions }) {
+ render(props, { selectedOptions, deviceFeatureOptions }) {
return (
);
}
}
-export default connect('httpClient', {})(TurnOnOffLight);
+export default withIntlAsProp(connect('httpClient', {})(TurnOnOffLight));
diff --git a/front/src/routes/scene/edit-scene/actions/TurnOnOffSwitchParams.jsx b/front/src/routes/scene/edit-scene/actions/TurnOnOffSwitchParams.jsx
index c485b41df1..ffb0d2ce53 100644
--- a/front/src/routes/scene/edit-scene/actions/TurnOnOffSwitchParams.jsx
+++ b/front/src/routes/scene/edit-scene/actions/TurnOnOffSwitchParams.jsx
@@ -4,6 +4,8 @@ import { Text } from 'preact-i18n';
import Select from 'react-select';
import { ACTIONS } from '../../../../../../server/utils/constants';
+import { getDeviceFeatureName } from '../../../../utils/device';
+import withIntlAsProp from '../../../../utils/withIntlAsProp';
class TurnOnOffSwitch extends Component {
getOptions = async () => {
@@ -13,17 +15,19 @@ class TurnOnOffSwitch extends Component {
device_feature_type: 'binary'
});
// keep only write switches, not read only
- const devicesFiltered = devices.filter(device => {
- const writeSwitch = device.features.find(f => f.read_only === false);
- return writeSwitch !== undefined;
+ const deviceFeatureOptions = [];
+ devices.forEach(device => {
+ device.features
+ .filter(deviceFeature => deviceFeature.read_only === false)
+ .map(deviceFeature => ({
+ value: deviceFeature.selector,
+ label: getDeviceFeatureName(this.props.intl.dictionary, device, deviceFeature)
+ }))
+ .forEach(deviceFeatureOption => deviceFeatureOptions.push(deviceFeatureOption));
});
- const deviceOptions = devicesFiltered.map(device => ({
- value: device.selector,
- label: device.name
- }));
- await this.setState({ deviceOptions });
+ await this.setState({ deviceFeatureOptions });
this.refreshSelectedOptions(this.props);
- return deviceOptions;
+ return deviceFeatureOptions;
} catch (e) {
console.error(e);
}
@@ -31,18 +35,20 @@ class TurnOnOffSwitch extends Component {
handleChange = selectedOptions => {
if (selectedOptions) {
const switches = selectedOptions.map(selectedOption => selectedOption.value);
- this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'devices', switches);
+ this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'device_features', switches);
} else {
- this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'devices', []);
+ this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'device_features', []);
}
};
refreshSelectedOptions = nextProps => {
const selectedOptions = [];
- if (nextProps.action.devices && this.state.deviceOptions) {
- nextProps.action.devices.forEach(switches => {
- const deviceOption = this.state.deviceOptions.find(deviceOption => deviceOption.value === switches);
- if (deviceOption) {
- selectedOptions.push(deviceOption);
+ if (nextProps.action.device_features && this.state.deviceFeatureOptions) {
+ nextProps.action.device_features.forEach(switches => {
+ const deviceFeatureOption = this.state.deviceFeatureOptions.find(
+ deviceFeatureOption => deviceFeatureOption.value === switches
+ );
+ if (deviceFeatureOption) {
+ selectedOptions.push(deviceFeatureOption);
}
});
}
@@ -51,7 +57,7 @@ class TurnOnOffSwitch extends Component {
constructor(props) {
super(props);
this.state = {
- deviceOptions: null,
+ deviceFeatureOptions: null,
selectedOptions: []
};
}
@@ -63,7 +69,7 @@ class TurnOnOffSwitch extends Component {
this.refreshSelectedOptions(nextProps);
}
- render(props, { selectedOptions, deviceOptions }) {
+ render(props, { selectedOptions, deviceFeatureOptions }) {
return (
);
}
}
-export default connect('httpClient', {})(TurnOnOffSwitch);
+export default withIntlAsProp(connect('httpClient', {})(TurnOnOffSwitch));
diff --git a/server/lib/scene/scene.actions.js b/server/lib/scene/scene.actions.js
index a7ade930f4..7825b161a9 100644
--- a/server/lib/scene/scene.actions.js
+++ b/server/lib/scene/scene.actions.js
@@ -18,7 +18,7 @@ const get = require('get-value');
const dayjs = require('dayjs');
const utc = require('dayjs/plugin/utc');
const timezone = require('dayjs/plugin/timezone');
-const { ACTIONS, DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_TYPES } = require('../../utils/constants');
+const { ACTIONS } = require('../../utils/constants');
const { getDeviceFeature } = require('../../utils/device');
const { AbortScene } = require('../../utils/coreErrors');
const { compare } = require('../../utils/compare');
@@ -64,90 +64,78 @@ const actionsFunc = {
return self.device.setValue(device, deviceFeature, value);
},
[ACTIONS.LIGHT.TURN_ON]: async (self, action, scope) => {
- await Promise.map(action.devices, async (deviceSelector) => {
+ await Promise.map(action.device_features, async (deviceFeatureSelector) => {
try {
- const device = self.stateManager.get('device', deviceSelector);
- const deviceFeature = getDeviceFeature(
- device,
- DEVICE_FEATURE_CATEGORIES.LIGHT,
- DEVICE_FEATURE_TYPES.LIGHT.BINARY,
- );
- await self.device.setValue(device, deviceFeature, 1);
+ const deviceFeature = self.stateManager.get('deviceFeature', deviceFeatureSelector);
+ if (deviceFeature) {
+ const device = self.stateManager.get('deviceById', deviceFeature.device_id);
+ await self.device.setValue(device, deviceFeature, 1);
+ }
} catch (e) {
logger.warn(e);
}
});
},
[ACTIONS.LIGHT.TURN_OFF]: async (self, action, scope) => {
- await Promise.map(action.devices, async (deviceSelector) => {
+ await Promise.map(action.device_features, async (deviceFeatureSelector) => {
try {
- const device = self.stateManager.get('device', deviceSelector);
- const deviceFeature = getDeviceFeature(
- device,
- DEVICE_FEATURE_CATEGORIES.LIGHT,
- DEVICE_FEATURE_TYPES.LIGHT.BINARY,
- );
- await self.device.setValue(device, deviceFeature, 0);
+ const deviceFeature = self.stateManager.get('deviceFeature', deviceFeatureSelector);
+ if (deviceFeature) {
+ const device = self.stateManager.get('deviceById', deviceFeature.device_id);
+ await self.device.setValue(device, deviceFeature, 0);
+ }
} catch (e) {
logger.warn(e);
}
});
},
[ACTIONS.LIGHT.TOGGLE]: async (self, action, scope) => {
- await Promise.map(action.devices, async (deviceSelector) => {
+ await Promise.map(action.device_features, async (deviceFeatureSelector) => {
try {
- const device = self.stateManager.get('device', deviceSelector);
- const deviceFeature = getDeviceFeature(
- device,
- DEVICE_FEATURE_CATEGORIES.LIGHT,
- DEVICE_FEATURE_TYPES.LIGHT.BINARY,
- );
- await self.device.setValue(device, deviceFeature, deviceFeature.last_value === 0 ? 1 : 0);
+ const deviceFeature = self.stateManager.get('deviceFeature', deviceFeatureSelector);
+ if (deviceFeature) {
+ const device = self.stateManager.get('deviceById', deviceFeature.device_id);
+ await self.device.setValue(device, deviceFeature, deviceFeature.last_value === 0 ? 1 : 0);
+ }
} catch (e) {
logger.warn(e);
}
});
},
[ACTIONS.SWITCH.TURN_ON]: async (self, action, scope) => {
- await Promise.map(action.devices, async (deviceSelector) => {
+ await Promise.map(action.device_features, async (deviceFeatureSelector) => {
try {
- const device = self.stateManager.get('device', deviceSelector);
- const deviceFeature = getDeviceFeature(
- device,
- DEVICE_FEATURE_CATEGORIES.SWITCH,
- DEVICE_FEATURE_TYPES.SWITCH.BINARY,
- );
- await self.device.setValue(device, deviceFeature, 1);
+ const deviceFeature = self.stateManager.get('deviceFeature', deviceFeatureSelector);
+ if (deviceFeature) {
+ const device = self.stateManager.get('deviceById', deviceFeature.device_id);
+ await self.device.setValue(device, deviceFeature, 1);
+ }
} catch (e) {
logger.warn(e);
}
});
},
[ACTIONS.SWITCH.TURN_OFF]: async (self, action, scope) => {
- await Promise.map(action.devices, async (deviceSelector) => {
+ await Promise.map(action.device_features, async (deviceFeatureSelector) => {
try {
- const device = self.stateManager.get('device', deviceSelector);
- const deviceFeature = getDeviceFeature(
- device,
- DEVICE_FEATURE_CATEGORIES.SWITCH,
- DEVICE_FEATURE_TYPES.SWITCH.BINARY,
- );
- await self.device.setValue(device, deviceFeature, 0);
+ const deviceFeature = self.stateManager.get('deviceFeature', deviceFeatureSelector);
+ if (deviceFeature) {
+ const device = self.stateManager.get('deviceById', deviceFeature.device_id);
+ await self.device.setValue(device, deviceFeature, 0);
+ }
} catch (e) {
logger.warn(e);
}
});
},
[ACTIONS.SWITCH.TOGGLE]: async (self, action, scope) => {
- await Promise.map(action.devices, async (deviceSelector) => {
+ await Promise.map(action.device_features, async (deviceFeatureSelector) => {
try {
- const device = self.stateManager.get('device', deviceSelector);
- const deviceFeature = getDeviceFeature(
- device,
- DEVICE_FEATURE_CATEGORIES.SWITCH,
- DEVICE_FEATURE_TYPES.SWITCH.BINARY,
- );
- await self.device.setValue(device, deviceFeature, deviceFeature.last_value === 0 ? 1 : 0);
+ const deviceFeature = self.stateManager.get('deviceFeature', deviceFeatureSelector);
+ if (deviceFeature) {
+ const device = self.stateManager.get('deviceById', deviceFeature.device_id);
+ await self.device.setValue(device, deviceFeature, deviceFeature.last_value === 0 ? 1 : 0);
+ }
} catch (e) {
logger.warn(e);
}
diff --git a/server/migrations/20230816115900-update-scene-actions-device-features.js b/server/migrations/20230816115900-update-scene-actions-device-features.js
new file mode 100644
index 0000000000..37bbdbfa4e
--- /dev/null
+++ b/server/migrations/20230816115900-update-scene-actions-device-features.js
@@ -0,0 +1,81 @@
+const Promise = require('bluebird');
+const db = require('../models');
+const logger = require('../utils/logger');
+const { DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_TYPES } = require('../utils/constants');
+
+module.exports = {
+ up: async (queryInterface, Sequelize) => {
+ const scenes = await db.Scene.findAll();
+ logger.info(`Scene migration: Found ${scenes.length} scene(s)`);
+
+ await Promise.each(scenes, async (scene) => {
+ let actionsModified = false;
+ const updatedActions = await Promise.all(
+ scene.actions.map(async (subactions) => {
+ return Promise.all(
+ subactions.map(async (action) => {
+ if (
+ action.type === 'switch.turn-on' ||
+ action.type === 'switch.turn-off' ||
+ action.type === 'switch.toggle' ||
+ action.type === 'light.turn-on' ||
+ action.type === 'light.turn-off' ||
+ action.type === 'light.toggle'
+ ) {
+ let category;
+ let type;
+ if (action.type.startsWith('switch')) {
+ category = DEVICE_FEATURE_CATEGORIES.SWITCH;
+ type = DEVICE_FEATURE_TYPES.SWITCH.BINARY;
+ } else if (action.type.startsWith('light')) {
+ category = DEVICE_FEATURE_CATEGORIES.LIGHT;
+ type = DEVICE_FEATURE_TYPES.LIGHT.BINARY;
+ }
+ if (action.devices) {
+ const devices = await Promise.all(
+ action.devices.map((deviceSelector) =>
+ db.Device.findOne({
+ where: {
+ selector: deviceSelector,
+ },
+ }),
+ ),
+ );
+
+ const deviceIds = devices.filter((device) => device).map((device) => device.id);
+ if (deviceIds && deviceIds.length > 0) {
+ const deviceFeatures = await Promise.all(
+ deviceIds.map(async (deviceId) =>
+ db.DeviceFeature.findOne({
+ where: {
+ device_id: deviceId,
+ category,
+ type,
+ },
+ }),
+ ),
+ );
+ action.device_features = deviceFeatures.map((deviceFeature) => deviceFeature.selector);
+ delete action.devices;
+ actionsModified = true;
+ }
+ }
+ }
+ return action;
+ }),
+ );
+ }),
+ );
+
+ logger.info(`Scene migration: Updating scene ${scene.id} with new actions`);
+ if (actionsModified) {
+ scene.set({
+ actions: updatedActions,
+ });
+ scene.changed('actions', true);
+ await scene.save();
+ }
+ });
+ },
+ down: async (queryInterface, Sequelize) => {},
+};
diff --git a/server/test/lib/device/device.update_scene_actions_device_features.test.js b/server/test/lib/device/device.update_scene_actions_device_features.test.js
new file mode 100644
index 0000000000..a1169d29d5
--- /dev/null
+++ b/server/test/lib/device/device.update_scene_actions_device_features.test.js
@@ -0,0 +1,41 @@
+const { expect } = require('chai');
+const { fake } = require('sinon');
+const { up } = require('../../../migrations/20230816115900-update-scene-actions-device-features');
+const StateManager = require('../../../lib/state');
+const SceneManager = require('../../../lib/scene');
+const Event = require('../../../lib/event');
+
+describe('Device', () => {
+ it('should create device alone', async () => {
+ const event = new Event();
+ const stateManager = new StateManager(event);
+ const sceneManager = new SceneManager(
+ stateManager,
+ event,
+ {},
+ {},
+ {},
+ {},
+ {},
+ {},
+ {},
+ {},
+ {
+ addNamedEntity: fake.returns(null),
+ },
+ );
+
+ const scene = await sceneManager.create({
+ id: 'device.update_scene_actions_device_features.test',
+ selector: 'device_update_scene_actions_device_features_test',
+ name: 'device.update_scene_actions_device_features.test',
+ icon: 'fe-scene',
+ triggers: [],
+ actions: [],
+ });
+
+ up(null, null);
+
+ expect(scene).to.have.property('name', 'Philips Hue 1');
+ });
+});
diff --git a/server/test/lib/scene/scene.checkTrigger.test.js b/server/test/lib/scene/scene.checkTrigger.test.js
index 458586be1e..2872807245 100644
--- a/server/test/lib/scene/scene.checkTrigger.test.js
+++ b/server/test/lib/scene/scene.checkTrigger.test.js
@@ -17,6 +17,10 @@ describe('scene.checkTrigger', () => {
setValue: fake.resolves(null),
};
+ const deviceFeature = {
+ device_id: 'light-1',
+ };
+
const brain = {};
beforeEach(() => {
@@ -38,6 +42,8 @@ describe('scene.checkTrigger', () => {
brain.removeNamedEntity = fake.returns(null);
const stateManager = new StateManager();
+ stateManager.setState('deviceById', 'light-1', device);
+ stateManager.setState('deviceFeature', 'light-1-binary', deviceFeature);
sceneManager = new SceneManager(stateManager, event, device, {}, {}, house, {}, {}, {}, scheduler, brain);
});
@@ -54,7 +60,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_ON,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -91,7 +97,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_ON,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -128,7 +134,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_ON,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -164,7 +170,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_ON,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -200,7 +206,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_OFF,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -236,7 +242,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_OFF,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -271,7 +277,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_OFF,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -306,7 +312,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_OFF,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -343,7 +349,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_OFF,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -380,7 +386,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_OFF,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -417,7 +423,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_OFF,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -454,7 +460,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_ON,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -491,7 +497,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_ON,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -530,7 +536,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_ON,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -569,7 +575,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_ON,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -615,7 +621,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_ON,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -654,7 +660,7 @@ describe('scene.checkTrigger', () => {
[
{
type: ACTIONS.LIGHT.TURN_ON,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
diff --git a/server/test/lib/scene/scene.execute.test.js b/server/test/lib/scene/scene.execute.test.js
index 64fc7455b1..0456a77b81 100644
--- a/server/test/lib/scene/scene.execute.test.js
+++ b/server/test/lib/scene/scene.execute.test.js
@@ -10,6 +10,9 @@ describe('scene.execute', () => {
const event = new EventEmitter();
const brain = {};
const device = {};
+ const deviceFeature = {
+ device_id: 'light-1',
+ };
let stateManager;
let sceneManager;
@@ -19,6 +22,8 @@ describe('scene.execute', () => {
brain.removeNamedEntity = fake.returns(null);
device.setValue = fake.resolves(null);
stateManager = new StateManager(event);
+ stateManager.setState('deviceById', 'light-1', device);
+ stateManager.setState('deviceFeature', 'light-1-binary', deviceFeature);
sceneManager = new SceneManager(stateManager, event, device, {}, {}, {}, {}, {}, {}, {}, brain);
});
@@ -34,7 +39,7 @@ describe('scene.execute', () => {
[
{
type: ACTIONS.LIGHT.TURN_ON,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
diff --git a/server/test/lib/scene/scene.executeActions.test.js b/server/test/lib/scene/scene.executeActions.test.js
index 79c8261815..2704263dc4 100644
--- a/server/test/lib/scene/scene.executeActions.test.js
+++ b/server/test/lib/scene/scene.executeActions.test.js
@@ -24,24 +24,24 @@ describe('scene.executeActions', () => {
const deviceFeature = {
category: DEVICE_FEATURE_CATEGORIES.LIGHT,
type: DEVICE_FEATURE_TYPES.LIGHT.BINARY,
+ device_id: 'light-1',
};
const device = {
setValue: fake.resolves(null),
features: {
category: DEVICE_FEATURE_CATEGORIES.LIGHT,
type: DEVICE_FEATURE_TYPES.LIGHT.BINARY,
- find: fake.returns(deviceFeature),
},
};
-
- stateManager.setState('device', 'light-1', device);
+ stateManager.setState('deviceById', 'light-1', device);
+ stateManager.setState('deviceFeature', 'light-1-binary', deviceFeature);
await executeActions(
{ stateManager, event, device },
[
[
{
type: ACTIONS.LIGHT.TURN_ON,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -53,24 +53,24 @@ describe('scene.executeActions', () => {
const deviceFeature = {
category: DEVICE_FEATURE_CATEGORIES.LIGHT,
type: DEVICE_FEATURE_TYPES.LIGHT.BINARY,
+ device_id: 'light-1',
};
const device = {
setValue: fake.resolves(null),
features: {
category: DEVICE_FEATURE_CATEGORIES.LIGHT,
type: DEVICE_FEATURE_TYPES.LIGHT.BINARY,
- find: fake.returns(deviceFeature),
},
};
-
- stateManager.setState('device', 'light-1', device);
+ stateManager.setState('deviceById', 'light-1', device);
+ stateManager.setState('deviceFeature', 'light-1-binary', deviceFeature);
await executeActions(
{ stateManager, event, device },
[
[
{
type: ACTIONS.LIGHT.TURN_OFF,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -82,6 +82,7 @@ describe('scene.executeActions', () => {
const deviceFeature = {
category: DEVICE_FEATURE_CATEGORIES.LIGHT,
type: DEVICE_FEATURE_TYPES.LIGHT.BINARY,
+ device_id: 'light-1',
last_value: 0,
};
const device = {
@@ -89,18 +90,17 @@ describe('scene.executeActions', () => {
features: {
category: DEVICE_FEATURE_CATEGORIES.LIGHT,
type: DEVICE_FEATURE_TYPES.LIGHT.BINARY,
- find: fake.returns(deviceFeature),
},
};
-
- stateManager.setState('device', 'light-1', device);
+ stateManager.setState('deviceById', 'light-1', device);
+ stateManager.setState('deviceFeature', 'light-1-binary', deviceFeature);
await executeActions(
{ stateManager, event, device },
[
[
{
type: ACTIONS.LIGHT.TOGGLE,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -112,6 +112,7 @@ describe('scene.executeActions', () => {
const deviceFeature = {
category: DEVICE_FEATURE_CATEGORIES.LIGHT,
type: DEVICE_FEATURE_TYPES.LIGHT.BINARY,
+ device_id: 'light-1',
last_value: 1,
};
const device = {
@@ -119,18 +120,17 @@ describe('scene.executeActions', () => {
features: {
category: DEVICE_FEATURE_CATEGORIES.LIGHT,
type: DEVICE_FEATURE_TYPES.LIGHT.BINARY,
- find: fake.returns(deviceFeature),
},
};
-
- stateManager.setState('device', 'light-1', device);
+ stateManager.setState('deviceById', 'light-1', device);
+ stateManager.setState('deviceFeature', 'light-1-binary', deviceFeature);
await executeActions(
{ stateManager, event, device },
[
[
{
type: ACTIONS.LIGHT.TOGGLE,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
],
@@ -138,53 +138,28 @@ describe('scene.executeActions', () => {
);
assert.calledOnceWithExactly(device.setValue, device, deviceFeature, 0);
});
- it('should execute light toggle error', async () => {
- const device = {
- setValue: fake.resolves(null),
- features: {
- category: DEVICE_FEATURE_CATEGORIES.LIGHT,
- type: DEVICE_FEATURE_TYPES.LIGHT.BINARY,
- find: fake.throws('An error occured'),
- },
- };
-
- stateManager.setState('device', 'light-1', device);
- await executeActions(
- { stateManager, event, device },
- [
- [
- {
- type: ACTIONS.LIGHT.TOGGLE,
- devices: ['light-1'],
- },
- ],
- ],
- {},
- );
- assert.notCalled(device.setValue);
- });
it('should execute switch turn on', async () => {
const deviceFeature = {
category: DEVICE_FEATURE_CATEGORIES.SWITCH,
type: DEVICE_FEATURE_TYPES.SWITCH.BINARY,
+ device_id: 'switch-1',
};
const device = {
setValue: fake.resolves(null),
features: {
category: DEVICE_FEATURE_CATEGORIES.SWITCH,
type: DEVICE_FEATURE_TYPES.SWITCH.BINARY,
- find: fake.returns(deviceFeature),
},
};
-
- stateManager.setState('device', 'switch-1', device);
+ stateManager.setState('deviceById', 'switch-1', device);
+ stateManager.setState('deviceFeature', 'switch-1-binary', deviceFeature);
await executeActions(
{ stateManager, event, device },
[
[
{
type: ACTIONS.SWITCH.TURN_ON,
- devices: ['switch-1'],
+ device_features: ['switch-1-binary'],
},
],
],
@@ -196,24 +171,24 @@ describe('scene.executeActions', () => {
const deviceFeature = {
category: DEVICE_FEATURE_CATEGORIES.SWITCH,
type: DEVICE_FEATURE_TYPES.SWITCH.BINARY,
+ device_id: 'switch-1',
};
const device = {
setValue: fake.resolves(null),
features: {
category: DEVICE_FEATURE_CATEGORIES.SWITCH,
type: DEVICE_FEATURE_TYPES.SWITCH.BINARY,
- find: fake.returns(deviceFeature),
},
};
-
- stateManager.setState('device', 'switch-1', device);
+ stateManager.setState('deviceById', 'switch-1', device);
+ stateManager.setState('deviceFeature', 'switch-1-binary', deviceFeature);
await executeActions(
{ stateManager, event, device },
[
[
{
type: ACTIONS.SWITCH.TURN_OFF,
- devices: ['switch-1'],
+ device_features: ['switch-1-binary'],
},
],
],
@@ -225,6 +200,7 @@ describe('scene.executeActions', () => {
const deviceFeature = {
category: DEVICE_FEATURE_CATEGORIES.SWITCH,
type: DEVICE_FEATURE_TYPES.SWITCH.BINARY,
+ device_id: 'switch-1',
last_value: 0,
};
const device = {
@@ -232,18 +208,17 @@ describe('scene.executeActions', () => {
features: {
category: DEVICE_FEATURE_CATEGORIES.SWITCH,
type: DEVICE_FEATURE_TYPES.SWITCH.BINARY,
- find: fake.returns(deviceFeature),
},
};
-
- stateManager.setState('device', 'switch-1', device);
+ stateManager.setState('deviceById', 'switch-1', device);
+ stateManager.setState('deviceFeature', 'switch-1-binary', deviceFeature);
await executeActions(
{ stateManager, event, device },
[
[
{
type: ACTIONS.SWITCH.TOGGLE,
- devices: ['switch-1'],
+ device_features: ['switch-1-binary'],
},
],
],
@@ -255,6 +230,7 @@ describe('scene.executeActions', () => {
const deviceFeature = {
category: DEVICE_FEATURE_CATEGORIES.SWITCH,
type: DEVICE_FEATURE_TYPES.SWITCH.BINARY,
+ device_id: 'switch-1',
last_value: 1,
};
const device = {
@@ -262,18 +238,17 @@ describe('scene.executeActions', () => {
features: {
category: DEVICE_FEATURE_CATEGORIES.SWITCH,
type: DEVICE_FEATURE_TYPES.SWITCH.BINARY,
- find: fake.returns(deviceFeature),
},
};
-
- stateManager.setState('device', 'switch-1', device);
+ stateManager.setState('deviceById', 'switch-1', device);
+ stateManager.setState('deviceFeature', 'switch-1-binary', deviceFeature);
await executeActions(
{ stateManager, event, device },
[
[
{
type: ACTIONS.SWITCH.TURN_OFF,
- devices: ['switch-1'],
+ device_features: ['switch-1-binary'],
},
],
],
@@ -364,32 +339,40 @@ describe('scene.executeActions', () => {
const device = {
setValue: fake.resolves(null),
};
-
+ const deviceFeatureLight = {
+ device_id: 'device',
+ };
+ const deviceFeatureSwitch = {
+ device_id: 'device',
+ };
+ stateManager.setState('deviceById', 'device', device);
+ stateManager.setState('deviceFeature', 'light-1-binary', deviceFeatureLight);
+ stateManager.setState('deviceFeature', 'switch-1-binary', deviceFeatureSwitch);
await executeActions(
{ stateManager, event, device },
[
[
{
type: ACTIONS.LIGHT.TURN_ON,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
[
{
type: ACTIONS.LIGHT.TURN_ON,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
},
],
[
{
type: ACTIONS.SWITCH.TURN_ON,
- devices: ['switch-1'],
+ device_features: ['switch-1-binary'],
},
],
[
{
type: ACTIONS.SWITCH.TURN_OFF,
- devices: ['switch-1'],
+ device_features: ['switch-1-binary'],
},
],
],
diff --git a/server/test/lib/scene/scene.executeSingleAction.test.js b/server/test/lib/scene/scene.executeSingleAction.test.js
index 7fb3da1675..6f8dcbb57d 100644
--- a/server/test/lib/scene/scene.executeSingleAction.test.js
+++ b/server/test/lib/scene/scene.executeSingleAction.test.js
@@ -9,6 +9,8 @@ const event = new EventEmitter();
const deviceFeatureLightBinary = {
category: DEVICE_FEATURE_CATEGORIES.LIGHT,
type: DEVICE_FEATURE_TYPES.LIGHT.BINARY,
+ device_id: 'light-1',
+ last_value: 0,
};
const lightDevice = {
@@ -18,6 +20,8 @@ const lightDevice = {
const deviceFeatureSwitchBinary = {
category: DEVICE_FEATURE_CATEGORIES.SWITCH,
type: DEVICE_FEATURE_TYPES.SWITCH.BINARY,
+ device_id: 'switch-1',
+ last_value: 0,
};
const switchDevice = {
@@ -30,11 +34,12 @@ describe('scene.executeSingleAction', () => {
setValue: fake.resolves(null),
};
const stateManager = new StateManager();
- stateManager.setState('device', 'light-1', lightDevice);
+ stateManager.setState('deviceById', 'light-1', lightDevice);
+ stateManager.setState('deviceFeature', 'light-1-binary', deviceFeatureLightBinary);
const sceneManager = new SceneManager(stateManager, event, device);
await sceneManager.executeSingleAction({
type: ACTIONS.LIGHT.TURN_ON,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
});
assert.calledWith(device.setValue, lightDevice, deviceFeatureLightBinary, 1);
});
@@ -43,11 +48,12 @@ describe('scene.executeSingleAction', () => {
setValue: fake.resolves(null),
};
const stateManager = new StateManager();
- stateManager.setState('device', 'light-1', lightDevice);
+ stateManager.setState('deviceById', 'light-1', lightDevice);
+ stateManager.setState('deviceFeature', 'light-1-binary', deviceFeatureLightBinary);
const sceneManager = new SceneManager(stateManager, event, device);
await sceneManager.executeSingleAction({
type: ACTIONS.LIGHT.TURN_OFF,
- devices: ['light-1'],
+ device_features: ['light-1-binary'],
});
assert.calledWith(device.setValue, lightDevice, deviceFeatureLightBinary, 0);
});
@@ -56,11 +62,26 @@ describe('scene.executeSingleAction', () => {
setValue: fake.resolves(null),
};
const stateManager = new StateManager();
- stateManager.setState('device', 'switch-1', switchDevice);
+ stateManager.setState('deviceById', 'light-1', lightDevice);
+ stateManager.setState('deviceFeature', 'light-1-binary', deviceFeatureLightBinary);
+ const sceneManager = new SceneManager(stateManager, event, device);
+ await sceneManager.executeSingleAction({
+ type: ACTIONS.LIGHT.TOGGLE,
+ device_features: ['light-1-binary'],
+ });
+ assert.calledWith(device.setValue, lightDevice, deviceFeatureLightBinary, 1);
+ });
+ it('should execute one action', async () => {
+ const device = {
+ setValue: fake.resolves(null),
+ };
+ const stateManager = new StateManager();
+ stateManager.setState('deviceById', 'switch-1', switchDevice);
+ stateManager.setState('deviceFeature', 'switch-1-binary', deviceFeatureSwitchBinary);
const sceneManager = new SceneManager(stateManager, event, device);
await sceneManager.executeSingleAction({
type: ACTIONS.SWITCH.TURN_ON,
- devices: ['switch-1'],
+ device_features: ['switch-1-binary'],
});
assert.calledWith(device.setValue, switchDevice, deviceFeatureSwitchBinary, 1);
});
@@ -69,11 +90,12 @@ describe('scene.executeSingleAction', () => {
setValue: fake.resolves(null),
};
const stateManager = new StateManager();
- stateManager.setState('device', 'switch-1', switchDevice);
+ stateManager.setState('deviceById', 'switch-1', switchDevice);
+ stateManager.setState('deviceFeature', 'switch-1-binary', deviceFeatureSwitchBinary);
const sceneManager = new SceneManager(stateManager, event, device);
await sceneManager.executeSingleAction({
type: ACTIONS.SWITCH.TURN_OFF,
- devices: ['switch-1'],
+ device_features: ['switch-1-binary'],
});
assert.calledWith(device.setValue, switchDevice, deviceFeatureSwitchBinary, 0);
});
@@ -82,11 +104,12 @@ describe('scene.executeSingleAction', () => {
setValue: fake.rejects(null),
};
const stateManager = new StateManager();
- stateManager.setState('device', 'switch-1', switchDevice);
+ stateManager.setState('deviceById', 'switch-1', switchDevice);
+ stateManager.setState('deviceFeature', 'switch-1-binary', deviceFeatureSwitchBinary);
const sceneManager = new SceneManager(stateManager, event, device);
await sceneManager.executeSingleAction({
type: ACTIONS.SWITCH.TURN_OFF,
- devices: ['switch-1'],
+ device_features: ['switch-1-binary'],
});
assert.calledWith(device.setValue, switchDevice, deviceFeatureSwitchBinary, 0);
});
@@ -95,11 +118,12 @@ describe('scene.executeSingleAction', () => {
setValue: fake.rejects(null),
};
const stateManager = new StateManager();
- stateManager.setState('device', 'switch-1', switchDevice);
+ stateManager.setState('deviceById', 'switch-1', switchDevice);
+ stateManager.setState('deviceFeature', 'switch-1-binary', deviceFeatureSwitchBinary);
const sceneManager = new SceneManager(stateManager, event, device);
await sceneManager.executeSingleAction({
type: ACTIONS.SWITCH.TURN_ON,
- devices: ['switch-1'],
+ device_features: ['switch-1-binary'],
});
assert.calledWith(device.setValue, switchDevice, deviceFeatureSwitchBinary, 1);
});