diff --git a/smart-hut/src/components/dashboard/AutomationCreationModal.js b/smart-hut/src/components/dashboard/AutomationCreationModal.js
index ced8261..6d13dd6 100644
--- a/smart-hut/src/components/dashboard/AutomationCreationModal.js
+++ b/smart-hut/src/components/dashboard/AutomationCreationModal.js
@@ -1,6 +1,6 @@
-import React, {Component, useState, useRef} from 'react';
-import {connect} from 'react-redux';
-import {RemoteService} from '../../remote';
+import React, { Component, useState, useRef } from 'react';
+import { connect } from 'react-redux';
+import { RemoteService } from '../../remote';
import update from 'immutability-helper';
import './Automations.css';
@@ -21,7 +21,7 @@ import {
} from 'semantic-ui-react';
export const operands = [
- {key: 'EQUAL', text: '=', value: 'EQUAL'},
+ { key: 'EQUAL', text: '=', value: 'EQUAL' },
{
key: 'GREATER_EQUAL',
text: '\u2265',
@@ -45,8 +45,8 @@ export const operands = [
];
const deviceStateOptions = [
- {key: 'off', text: 'off', value: false},
- {key: 'on', text: 'on', value: true},
+ { key: 'off', text: 'off', value: false },
+ { key: 'on', text: 'on', value: true },
];
const CreateTrigger = (props) => {
@@ -78,112 +78,112 @@ const CreateTrigger = (props) => {
};
return (
-
-
-
-
-
-
- {activeOperand ? (
- <>
-
- props.inputChange(val)}
- ref={operandsRef}
- name="operand"
- compact
- selection
- options={operands}
- />
-
-
- {
+
+
+
+
+
+
+ {activeOperand ? (
+ <>
+
+ props.inputChange(val)}
+ ref={operandsRef}
+ name="operand"
+ compact
+ selection
+ options={operands}
+ />
+
+
+ {
props.inputChange(val);
}}
- ref={valuesRef}
- name="value"
- type="number"
- placeholder="Value"
- />
-
- >
+ ref={valuesRef}
+ name="value"
+ type="number"
+ placeholder="Value"
+ />
+
+ >
) : (
-
- props.inputChange(val)}
- placeholder="State"
- name="on"
- compact
- selection
- options={deviceStateOptions}
- />
-
+
+ props.inputChange(val)}
+ placeholder="State"
+ name="on"
+ compact
+ selection
+ options={deviceStateOptions}
+ />
+
)}
-
-
-
-
+
+
+
+
);
};
const SceneItem = (props) => {
const position = props.order.indexOf(props.scene.id);
return (
-
-
-
-
-
- props.orderScenes(props.scene.id, val.checked)}
- checked={position + 1 > 0}
- />
-
-
- {props.scene.name}
-
-
- {position !== -1 ? `# ${position + 1}` : ''}
-
-
-
-
-
+
+
+
+
+
+ props.orderScenes(props.scene.id, val.checked)}
+ checked={position + 1 > 0}
+ />
+
+
+ {props.scene.name}
+
+
+ {position !== -1 ? `# ${position + 1}` : ''}
+
+
+
+
+
);
};
const Trigger = ({
deviceName, trigger, onRemove, index,
}) => {
- const {operand, value, on} = trigger;
+ const { operand, value, on } = trigger;
let symbol;
if (operand) {
symbol = operands.filter((opt) => opt.key === operand)[0].text;
}
return (
-
-
- onRemove(index)}
- className="remove-icon"
- name="remove"
- />
-
+
+
+ onRemove(index)}
+ className="remove-icon"
+ name="remove"
+ />
+
);
};
@@ -213,7 +213,7 @@ class AutomationSaveModal extends Component {
device: trigger.deviceId,
kind: trigger.kind,
...(trigger.kind === 'booleanTrigger'
- ? {on: trigger.on}
+ ? { on: trigger.on }
: {
operand: trigger.operator,
value: trigger.value,
@@ -235,19 +235,18 @@ class AutomationSaveModal extends Component {
this.orderScenes = this.orderScenes.bind(this);
this.onChangeName = this.onChangeName.bind(this);
- //Conditions
- this.setNewCondition = this._setter("newCondition");
+ // Conditions
+ this.setNewCondition = this._setter('newCondition');
this.addCondition = this.addCondition.bind(this);
this.removeCondition = this.removeCondition.bind(this);
-
}
openModal = (e) => {
- this.setState({openModal: true});
+ this.setState({ openModal: true });
};
closeModal = (e) => {
- this.setState({openModal: false});
+ this.setState({ openModal: false });
};
get deviceList() {
@@ -255,7 +254,7 @@ class AutomationSaveModal extends Component {
}
_setter(property) {
- return (value) => this.setState(update(this.state, {[property]: {$set: value}}));
+ return (value) => this.setState(update(this.state, { [property]: { $set: value } }));
}
triggerKind(trigger) {
@@ -269,6 +268,20 @@ class AutomationSaveModal extends Component {
// throw new Error("Trigger kind not handled");
}
+ conditionKind(condition) {
+ if ('operand' in condition && 'value' in condition) {
+ return 'rangeTrigger';
+ }
+ if ('on' in condition) {
+ return 'booleanTrigger';
+ }
+
+ if ('operand' in condition && 'mode' in condition) {
+ return 'thermostatCondition';
+ }
+ return false;
+ }
+
_checkNewTrigger(trigger, isCondition = false) {
const error = {
result: false,
@@ -322,31 +335,31 @@ class AutomationSaveModal extends Component {
let isNotDuplicate = null;
- if(isCondition === true){
+ if (isCondition === true) {
isNotDuplicate = !this.state.conditionsList.some(
(t) => t.device === trigger.device && t.operand === trigger.operand,
);
- }else{
+ } else {
isNotDuplicate = !this.state.triggerList.some(
(t) => t.device === trigger.device && t.operand === trigger.operand,
);
}
- const type = isCondition ? "condition" : "trigger"
+ const type = isCondition ? 'condition' : 'trigger';
const duplicationMessage = `You have already created a ${type} for this device with the same conditions`;
return {
result: isNotDuplicate,
message: isNotDuplicate
? null
- : duplicationMessage
+ : duplicationMessage,
};
}
addTrigger() {
- const {result, message} = this._checkNewTrigger(this.state.newTrigger);
+ const { result, message } = this._checkNewTrigger(this.state.newTrigger);
if (result) {
this.setState(
update(this.state, {
- triggerList: {$push: [this.state.newTrigger]},
+ triggerList: { $push: [this.state.newTrigger] },
}),
);
} else {
@@ -356,14 +369,14 @@ class AutomationSaveModal extends Component {
removeTrigger(index) {
this.setState(
- update(this.state, {triggerList: {$splice: [[index, 1]]}}),
+ update(this.state, { triggerList: { $splice: [[index, 1]] } }),
);
}
// This gets triggered when the devices dropdown changes the value.
onInputChange(val) {
if (val.name === 'device') {
- this.setNewTrigger({[val.name]: val.value});
+ this.setNewTrigger({ [val.name]: val.value });
} else {
this.setNewTrigger({
...this.state.newTrigger,
@@ -378,7 +391,7 @@ class AutomationSaveModal extends Component {
orderScenes = (id, checked) => {
if (checked) {
- this.setState(update(this.state, {order: {$push: [id]}}));
+ this.setState(update(this.state, { order: { $push: [id] } }));
} else {
this.setState(
update(this.state, {
@@ -388,8 +401,8 @@ class AutomationSaveModal extends Component {
}
};
- searchScenes(_, {value}) {
- this.setState(update(this.state, {scenesFilter: {$set: value}}));
+ searchScenes(_, { value }) {
+ this.setState(update(this.state, { scenesFilter: { $set: value } }));
this.forceUpdate();
}
@@ -452,7 +465,7 @@ class AutomationSaveModal extends Component {
deviceId: trigger.device,
kind,
...(kind === 'booleanTrigger'
- ? {on: trigger.on}
+ ? { on: trigger.on }
: {
operator: trigger.operand,
range: parseInt(trigger.value),
@@ -461,6 +474,25 @@ class AutomationSaveModal extends Component {
);
}
+ for (const condition of this.state.conditionsList) {
+ const kind = condition.kind || this.conditionKind(condition);
+ const loSpagnolo = (kind === 'thermostatCondition' ? { operator: condition.operand, mode: condition.mode }
+ : {
+ operator: condition.operand,
+ range: parseInt(condition.value),
+ });
+ automation.conditions.push(
+ {
+ deviceId: condition.device,
+ kind,
+ ...(kind === 'booleanTrigger'
+ ? { on: condition.on }
+ : loSpagnolo
+ ),
+ },
+ );
+ }
+
this.props
.fastUpdateAutomation(automation)
.then(this.closeModal)
@@ -471,6 +503,7 @@ class AutomationSaveModal extends Component {
automation,
triggerList: this.state.triggerList,
order: this.state.order,
+ conditionList: this.state.conditionsList,
})
.then(this.closeModal)
.catch(console.error);
@@ -480,18 +513,18 @@ class AutomationSaveModal extends Component {
get trigger() {
return this.props.id ? (
-
+
) : (
-
);
}
@@ -499,11 +532,11 @@ class AutomationSaveModal extends Component {
addCondition() {
// Same method used to check triggers and conditions, not a mistake
- const {result, message} = this._checkNewTrigger(this.state.newCondition, true);
+ const { result, message } = this._checkNewTrigger(this.state.newCondition, true);
if (result) {
this.setState(
update(this.state, {
- conditionsList: {$push: [this.state.newCondition]},
+ conditionsList: { $push: [this.state.newCondition] },
}),
);
} else {
@@ -513,13 +546,13 @@ class AutomationSaveModal extends Component {
removeCondition(index) {
this.setState(
- update(this.state, {conditionsList: {$splice: [[index, 1]]}}),
+ update(this.state, { conditionsList: { $splice: [[index, 1]] } }),
);
}
onInputChangeCondition = (val) => {
if (val.name === 'device') {
- this.setNewCondition({[val.name]: val.value});
+ this.setNewCondition({ [val.name]: val.value });
} else {
this.setNewCondition({
...this.state.newCondition,
@@ -530,150 +563,150 @@ class AutomationSaveModal extends Component {
render() {
return (
-
-
-
+ this.setEditName((prev) => !prev)}
+ style={{ display: 'inline' }}
+ circular
+ size="small"
+ icon={this.state.editName ? 'save' : 'edit'}
+ />
-
-
-
-
-
-
-
- {this.state.triggerList.length > 0
+
+
+
+
+
+
+
+ {this.state.triggerList.length > 0
&& this.state.triggerList.map((trigger, i) => {
const deviceName = this.deviceList.filter(
(d) => d.id === trigger.device,
)[0].name;
const key = this._generateKey(trigger);
return (
-
+
);
})}
-
-
-
-
-
- {this.props.scenes.length > 0 ? (
- <>
-
-
-
-
- {this.sceneList.map((scene) => (
-
+
+
+
+
+
+ {this.props.scenes.length > 0 ? (
+ <>
+
+
+
+
+ {this.sceneList.map((scene) => (
+
))}
-
- >
+
+ >
) : (
- <>
-
- Create Scene
- >
+ <>
+
+ Create Scene
+ >
)}
-
-
-
-
-
-
-
-
-
- {this.state.conditionsList.length > 0
+
+
+
+
+
+
+
+
+
+ {this.state.conditionsList.length > 0
&& this.state.conditionsList.map((condition, i) => {
const deviceName = this.deviceList.filter(
(d) => d.id === condition.device,
)[0].name;
const key = this._generateKey(condition);
return (
-
+
);
})}
-
-
-
-
-
-
-
-
-
- this.saveAutomation()} color="green">
- {this.props.id ? 'Save' : 'Create'}
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ this.saveAutomation()} color="green">
+ {this.props.id ? 'Save' : 'Create'}
+
+
+
+
+
+
);
}
}
diff --git a/smart-hut/src/components/dashboard/AutomationsPanel.js b/smart-hut/src/components/dashboard/AutomationsPanel.js
index 81163d0..4d9e231 100644
--- a/smart-hut/src/components/dashboard/AutomationsPanel.js
+++ b/smart-hut/src/components/dashboard/AutomationsPanel.js
@@ -17,7 +17,7 @@ import CreateAutomation, { operands } from './AutomationCreationModal';
const Automation = ({
automation, devices, scenes, removeAutomation,
}) => {
- const { triggers } = automation;
+ const { triggers, conditions } = automation;
const scenePriorities = automation.scenes;
const getOperator = (operand) => operands.filter((o) => o.key === operand)[0].text;
@@ -85,6 +85,36 @@ const Automation = ({
+
+
+
+
+
+ {conditions !== undefined
+ && conditions.map((condition) => {
+ const device = devices.filter(
+ (d) => d.id === condition.deviceId,
+ )[0];
+ return (
+
+ );
+ })}
+
+
+
+
>
);
};
diff --git a/smart-hut/src/remote.js b/smart-hut/src/remote.js
index e190f32..7ca4588 100644
--- a/smart-hut/src/remote.js
+++ b/smart-hut/src/remote.js
@@ -556,19 +556,30 @@ export const RemoteService = {
* with user-fiendly errors as a RemoteError
*/
saveAutomation: (data) => {
- const { automation, triggerList, order } = data;
+ const {
+ automation, triggerList, order, conditionList,
+} = data;
automation.triggers = [];
automation.scenes = [];
+ automation.condition = [];
return (dispatch) => {
const urlAutomation = '/automation';
const urlBooleanTrigger = '/booleanTrigger';
const urlRangeTrigger = '/rangeTrigger';
const urlScenePriority = '/scenePriority';
+ // conditions
+ const urlRangeCondition = '/rangeCondition';
+ const urlBooleanCondition = '/booleanCondition';
+ const urlThermostatCondition = '/thermostatCondition';
const rangeTriggerList = triggerList.filter((trigger) => 'operand' in trigger);
const booleanTriggerList = triggerList.filter(
(trigger) => !('operand' in trigger),
);
+ const rangeConditionList = conditionList.filter((condition) => 'operand' in condition && 'value' in condition);
+ const booleanConditionList = conditionList.filter((condition) => 'on' in condition);
+ const thermostatConditionList = conditionList.filter((condition) => 'operand' in condition && 'mode' in condition);
+
return Endpoint.post(urlAutomation, {}, automation).then(
async (automationRes) => {
@@ -591,7 +602,7 @@ export const RemoteService = {
const trigger = {
automationId: id,
deviceId: t.device,
- on: t.value,
+ on: t.on,
};
resBoolTriggers.push(Endpoint.post(
urlBooleanTrigger,
@@ -601,6 +612,51 @@ export const RemoteService = {
}
automation.triggers.push(...((await Promise.all(resBoolTriggers)).map((v) => v.data)));
+ // Conditions
+ const resRangeConditions = [];
+ for (const t of rangeConditionList) {
+ const condition = {
+ automationId: id,
+ deviceId: t.device,
+ operator: t.operand,
+ range: t.value,
+ };
+ resRangeConditions.push(Endpoint.post(urlRangeCondition, {}, condition));
+ }
+ automation.conditions = (await Promise.all(resRangeConditions)).map((v) => v.data);
+
+ const resBoolConditions = [];
+ for (const t of booleanConditionList) {
+ console.log('HERE', t);
+ const condition = {
+ automationId: id,
+ deviceId: t.device,
+ on: t.on,
+ };
+ resBoolConditions.push(Endpoint.post(
+ urlBooleanCondition,
+ {},
+ condition,
+ ));
+ }
+ automation.conditions.push(...((await Promise.all(resBoolConditions)).map((v) => v.data)));
+
+ const resThermoConditions = [];
+ for (const t of thermostatConditionList) {
+ const condition = {
+ automationId: id,
+ deviceId: t.device,
+ mode: t.mode,
+ operator: t.operand,
+ };
+ resThermoConditions.push(Endpoint.post(
+ urlThermostatCondition,
+ {},
+ condition,
+ ));
+ }
+ automation.conditions.push(...((await Promise.all(resThermoConditions)).map((v) => v.data)));
+
const resScenePriorities = [];
for (const [priority, sceneId] of order.entries()) {
const scenePriority = {