Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use device feature for scene action of type turn on/off light/switch #1768

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d125248
Initial commit
rpochet May 1, 2023
4f31c6c
Test
rpochet May 1, 2023
ba6d252
Tests
rpochet May 2, 2023
5354341
Prettier
rpochet May 2, 2023
ec2d327
Tests
rpochet May 3, 2023
0aed666
Tests
rpochet May 3, 2023
fc4ed22
Tests
rpochet May 3, 2023
c95d833
Tests
rpochet May 3, 2023
a74193b
Comment Pierre-gilles
rpochet May 5, 2023
775813c
ESLint
rpochet May 5, 2023
e29417c
Merge branch 'master' into scene_device_feature
rpochet May 5, 2023
cd208ff
DB migration
rpochet May 12, 2023
44eb242
Merge branch 'scene_device_feature' of https://github.com/rpochet/Gla…
rpochet May 12, 2023
d605d02
DB Migration
rpochet May 12, 2023
feefc8b
Merge branch 'master' into scene_device_feature
rpochet May 12, 2023
24b4b8e
Add check
rpochet May 12, 2023
63746db
Merge branch 'scene_device_feature' of https://github.com/rpochet/Gla…
rpochet May 12, 2023
5d79ae4
DB Migration
rpochet May 13, 2023
3ec5cf6
Update 20230505164900-update-scene-actions-device-features.js
rpochet May 13, 2023
6e5ec2b
Update 20230505164900-update-scene-actions-device-features.js
rpochet May 13, 2023
e84a707
DB Migration
rpochet May 13, 2023
ee8b55e
Merge branch 'scene_device_feature' of https://github.com/rpochet/Gla…
rpochet May 13, 2023
0043547
DB Migration
rpochet May 13, 2023
8f0de63
Merge branch 'master' into scene_device_feature
rpochet Aug 16, 2023
f980c74
Update DB migration scriptdate
rpochet Aug 16, 2023
9e88a52
Migration test
rpochet Aug 17, 2023
b3bd6f5
Merge branch 'master' into scene_device_feature
rpochet Sep 12, 2023
4293dd1
Merge branch 'master' into scene_device_feature
rpochet Sep 20, 2023
7ba4b26
Merge branch 'GladysAssistant:master' into scene_device_feature
rpochet Oct 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
45 changes: 28 additions & 17 deletions front/src/routes/scene/edit-scene/actions/TurnOnOffLightParams.jsx
Expand Up @@ -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 () => {
Expand All @@ -12,32 +14,41 @@ 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);
}
};
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);
}
});
}
Expand All @@ -46,7 +57,7 @@ class TurnOnOffLight extends Component {
constructor(props) {
super(props);
this.state = {
deviceOptions: null,
deviceFeatureOptions: null,
selectedOptions: []
};
}
Expand All @@ -58,7 +69,7 @@ class TurnOnOffLight extends Component {
this.refreshSelectedOptions(nextProps);
}

render(props, { selectedOptions, deviceOptions }) {
render(props, { selectedOptions, deviceFeatureOptions }) {
return (
<div class="form-group">
<label class="form-label">
Expand All @@ -76,11 +87,11 @@ class TurnOnOffLight extends Component {
isMulti
value={selectedOptions}
onChange={this.handleChange}
options={deviceOptions}
options={deviceFeatureOptions}
/>
</div>
);
}
}

export default connect('httpClient', {})(TurnOnOffLight);
export default withIntlAsProp(connect('httpClient', {})(TurnOnOffLight));
46 changes: 26 additions & 20 deletions front/src/routes/scene/edit-scene/actions/TurnOnOffSwitchParams.jsx
Expand Up @@ -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 () => {
Expand All @@ -13,36 +15,40 @@ 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);
}
};
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);
}
});
}
Expand All @@ -51,7 +57,7 @@ class TurnOnOffSwitch extends Component {
constructor(props) {
super(props);
this.state = {
deviceOptions: null,
deviceFeatureOptions: null,
selectedOptions: []
};
}
Expand All @@ -63,7 +69,7 @@ class TurnOnOffSwitch extends Component {
this.refreshSelectedOptions(nextProps);
}

render(props, { selectedOptions, deviceOptions }) {
render(props, { selectedOptions, deviceFeatureOptions }) {
return (
<div class="form-group">
<label class="form-label">
Expand All @@ -81,11 +87,11 @@ class TurnOnOffSwitch extends Component {
isMulti
value={selectedOptions}
onChange={this.handleChange}
options={deviceOptions}
options={deviceFeatureOptions}
/>
</div>
);
}
}

export default connect('httpClient', {})(TurnOnOffSwitch);
export default withIntlAsProp(connect('httpClient', {})(TurnOnOffSwitch));
86 changes: 37 additions & 49 deletions server/lib/scene/scene.actions.js
Expand Up @@ -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');
Expand Down Expand Up @@ -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) {
rpochet marked this conversation as resolved.
Show resolved Hide resolved
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);
}
Expand Down