Merge branch '79-addscene-getscenestates' into 'dev'
Able to add scenes and get scenes in a right way Closes #79 See merge request sa4-2020/the-sanmarinoes/frontend!89
This commit is contained in:
commit
77f7abed92
13 changed files with 236 additions and 52 deletions
|
@ -15,10 +15,12 @@ class DevicePanel extends Component {
|
|||
}
|
||||
|
||||
getDevices() {
|
||||
if (this.props.tab === "Devices") {
|
||||
this.props
|
||||
.fetchDevices()
|
||||
.catch((err) => console.error(`error fetching devices:`, err));
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
|
|
@ -30,12 +30,13 @@ class NewSceneDevice extends Component {
|
|||
|
||||
this.state = {
|
||||
openModal: false,
|
||||
sceneDevices: this.props.scene ? this.props.scene.devices : {},
|
||||
sceneDevices: this.props.scene ? this.props.scene.sceneStates : {},
|
||||
deviceName: "",
|
||||
};
|
||||
this.getDevices();
|
||||
|
||||
this.setSceneDevice = this.setSceneDevice.bind(this);
|
||||
this.setSceneState = this.setSceneState.bind(this);
|
||||
this.createState = this.createState.bind(this);
|
||||
}
|
||||
|
||||
getDevices() {
|
||||
|
@ -56,10 +57,25 @@ class NewSceneDevice extends Component {
|
|||
this.handleClose();
|
||||
};
|
||||
|
||||
setSceneDevice(e, d) {
|
||||
setSceneState(e, d) {
|
||||
this.setState({ devicesAttached: d.value });
|
||||
}
|
||||
|
||||
createState() {
|
||||
const device = this.props.devices.filter(
|
||||
(e) => this.state.devicesAttached[0] === e.id
|
||||
);
|
||||
let data = {
|
||||
sceneId: this.props.activeScene,
|
||||
id: device[0].id,
|
||||
kind: device[0].kind,
|
||||
};
|
||||
this.props
|
||||
.saveState(data)
|
||||
.catch((err) => console.error("error in creating state", err));
|
||||
this.resetState();
|
||||
}
|
||||
|
||||
render() {
|
||||
const availableDevices = [];
|
||||
this.props.devices.forEach((e) => {
|
||||
|
@ -83,7 +99,7 @@ class NewSceneDevice extends Component {
|
|||
}
|
||||
centered={true}
|
||||
>
|
||||
<Modal.Header>Add a New Scene Device</Modal.Header>
|
||||
<Modal.Header>Add a New Scene State</Modal.Header>
|
||||
<Modal.Content>
|
||||
<Form>
|
||||
<Form.Field style={{ marginTop: "1rem" }}>
|
||||
|
@ -93,7 +109,7 @@ class NewSceneDevice extends Component {
|
|||
placeholder="Select Devices"
|
||||
fluid
|
||||
multiple
|
||||
onChange={this.setSceneDevice}
|
||||
onChange={this.setSceneState}
|
||||
options={availableDevices}
|
||||
/>
|
||||
</Form.Field>
|
||||
|
@ -101,7 +117,7 @@ class NewSceneDevice extends Component {
|
|||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button
|
||||
onClick={this.createDevice}
|
||||
onClick={this.createState}
|
||||
color="blue"
|
||||
icon
|
||||
labelPosition="right"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, { Component } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { RemoteService } from "../../remote";
|
||||
import Device from "./devices/Device";
|
||||
import NewSceneDevice from "./NewSceneDevice";
|
||||
import { Grid } from "semantic-ui-react";
|
||||
|
||||
|
@ -12,20 +13,38 @@ class ScenesPanel extends Component {
|
|||
render() {
|
||||
return (
|
||||
<Grid doubling columns={2} divided="vertically">
|
||||
{
|
||||
//TODO DISPLAY DEVICES IN SCENE
|
||||
}
|
||||
{this.props.isActiveDefaultScene ? (
|
||||
{!this.props.isActiveDefaultScene
|
||||
? this.props.sceneStates.map((e, i) => {
|
||||
return (
|
||||
<Grid.Column key={i}>
|
||||
<Device tab={this.props.tab} id={e} />
|
||||
</Grid.Column>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
{!this.props.isActiveDefaultScene ? (
|
||||
<Grid.Column>
|
||||
<NewSceneDevice />
|
||||
</Grid.Column>
|
||||
) : null}
|
||||
) : (
|
||||
<Grid.Column>Welcome to the Scene View, you add a Scene</Grid.Column>
|
||||
)}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, _) => ({
|
||||
get sceneStates() {
|
||||
if (state.active.activeScene !== -1) {
|
||||
const stateArray = [
|
||||
...state.scenes[state.active.activeScene].sceneStates,
|
||||
].sort();
|
||||
return stateArray.map((id) => state.sceneStates[id]);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
get isActiveDefaultScene() {
|
||||
return state.active.activeScene === -1;
|
||||
},
|
||||
|
|
|
@ -30,7 +30,7 @@ class Device extends React.Component {
|
|||
}
|
||||
|
||||
renderDeviceComponent() {
|
||||
switch (this.props.device.kind) {
|
||||
switch (this.props.stateOrDevice.kind) {
|
||||
case "regularLight":
|
||||
return <Light tab={this.props.tab} id={this.props.id} />;
|
||||
case "sensor":
|
||||
|
@ -57,13 +57,19 @@ class Device extends React.Component {
|
|||
<Segment>
|
||||
<Grid columns={2}>
|
||||
<Grid.Column>{this.renderDeviceComponent()}</Grid.Column>
|
||||
{this.props.tab === "Devices" ? (
|
||||
<Grid.Column textAlign="center">
|
||||
<Header as="h3">{this.props.device.name}</Header>
|
||||
<Button color="blue" icon onClick={this.edit} labelPosition="left">
|
||||
<Header as="h3">{this.props.stateOrDevice.name}</Header>
|
||||
<Button
|
||||
color="blue"
|
||||
icon
|
||||
onClick={this.edit}
|
||||
labelPosition="left"
|
||||
>
|
||||
<Icon name="pencil" />
|
||||
Edit
|
||||
</Button>
|
||||
{this.props.device.kind === "smartPlug" ? (
|
||||
{this.props.stateOrDevice.kind === "smartPlug" ? (
|
||||
<Button
|
||||
color="orange"
|
||||
icon
|
||||
|
@ -75,6 +81,12 @@ class Device extends React.Component {
|
|||
</Button>
|
||||
) : null}
|
||||
</Grid.Column>
|
||||
) : (
|
||||
<Grid.Column textAlign="center">
|
||||
<Header as="h3">{this.props.stateOrDevice.name}</Header>
|
||||
</Grid.Column>
|
||||
)}
|
||||
|
||||
<DeviceSettingsModal ref={this.modalRef} id={this.props.id} />
|
||||
</Grid>
|
||||
</Segment>
|
||||
|
@ -83,7 +95,14 @@ class Device extends React.Component {
|
|||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
device: state.devices[ownProps.id],
|
||||
get stateOrDevice() {
|
||||
if (state.active.activeTab === "Devices") {
|
||||
return state.devices[ownProps.id];
|
||||
} else {
|
||||
const sceneState = state.sceneStates[ownProps.id];
|
||||
return state.devices[sceneState];
|
||||
}
|
||||
},
|
||||
});
|
||||
const DeviceContainer = connect(mapStateToProps, RemoteService)(Device);
|
||||
export default DeviceContainer;
|
||||
|
|
|
@ -37,7 +37,6 @@ class Light extends Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { intensity: this.props.device.intensity, timeout: null };
|
||||
console.log(this.props);
|
||||
|
||||
this.iconOn = "/img/lightOn.svg";
|
||||
this.iconOff = "/img/lightOff.svg";
|
||||
|
|
|
@ -405,6 +405,35 @@ export const RemoteService = {
|
|||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates/Updates a state with the given data. If
|
||||
* data.id is truthy, then a update call is performed,
|
||||
* otherwise a create call is performed. The update URL
|
||||
* is computed based on data.kind when data.flowType =
|
||||
* 'OUTPUT', otherwise the PUT "/device" endpoint
|
||||
* is used for updates and the POST "/<device.kind>"
|
||||
* endpoints are used for creation.
|
||||
* @param {State} data the device to update.
|
||||
* @returns {Promise<Device, RemoteError>} promise that resolves to the saved device and rejects
|
||||
* with user-fiendly errors as a RemoteError
|
||||
*/
|
||||
saveState: (data) => {
|
||||
return (dispatch) => {
|
||||
let url =
|
||||
"/" + data.kind + "/" + data.id + "/state?sceneId=" + data.sceneId;
|
||||
|
||||
return Endpoint["post"](url, {}, data)
|
||||
.then((res) => {
|
||||
dispatch(actions.stateSave(res.data));
|
||||
return res.data;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn("Update device: ", data, "error: ", err);
|
||||
throw new RemoteError(["Network error"]);
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Connetcs a series of output devices to an input device.
|
||||
* Output devices for Switch input can be: Normal Light, Dimmable Light, Smart Plug.
|
||||
|
|
|
@ -41,7 +41,7 @@ function reducer(previousState, action) {
|
|||
const createOrUpdateScene = (scene) => {
|
||||
if (!newState.scenes[scene.id]) {
|
||||
newState = update(newState, {
|
||||
scenes: { [scene.id]: { $set: { ...scene, states: new Set() } } },
|
||||
scenes: { [scene.id]: { $set: { ...scene, sceneStates: new Set() } } },
|
||||
});
|
||||
} else {
|
||||
newState = update(newState, {
|
||||
|
@ -58,7 +58,7 @@ function reducer(previousState, action) {
|
|||
pendingJoins: { scenes: { $unset: [scene.id] } },
|
||||
scenes: {
|
||||
[scene.id]: {
|
||||
states: {
|
||||
sceneStates: {
|
||||
$add: [...newState.pendingJoins.scenes[scene.id]],
|
||||
},
|
||||
},
|
||||
|
@ -79,6 +79,11 @@ function reducer(previousState, action) {
|
|||
}
|
||||
};
|
||||
|
||||
const updateSceneStateProps = (state) => {
|
||||
change.sceneStates[state.deviceId] = {};
|
||||
change.sceneStates[state.deviceId] = { $set: state.deviceId };
|
||||
};
|
||||
|
||||
switch (action.type) {
|
||||
case "LOGIN_UPDATE":
|
||||
newState = update(previousState, { login: { $set: action.login } });
|
||||
|
@ -94,10 +99,70 @@ function reducer(previousState, action) {
|
|||
break;
|
||||
case "SCENES_UPDATE":
|
||||
newState = previousState;
|
||||
console.log(action.scenes);
|
||||
for (const scene of action.scenes) {
|
||||
createOrUpdateScene(scene);
|
||||
}
|
||||
break;
|
||||
case "STATE_UPDATE":
|
||||
newState = previousState;
|
||||
change = null;
|
||||
|
||||
// if room is given, delete all devices in that room
|
||||
// and remove any join between that room and deleted
|
||||
// devices
|
||||
change = {
|
||||
scenes: { [action.sceneId]: { sceneStates: { $set: new Set() } } },
|
||||
sceneStates: { $unset: [] },
|
||||
};
|
||||
|
||||
const scene = newState.scenes[action.sceneId];
|
||||
for (const stateId of scene.sceneStates) {
|
||||
change.sceneStates.$unset.push(stateId);
|
||||
}
|
||||
|
||||
newState = update(previousState, change);
|
||||
|
||||
change = {
|
||||
sceneStates: {},
|
||||
scenes: {},
|
||||
pendingJoins: { scenes: {} },
|
||||
};
|
||||
|
||||
for (const sceneState of action.sceneStates) {
|
||||
if (!newState.sceneStates[sceneState.deviceId]) {
|
||||
change.sceneStates[sceneState.deviceId] = {
|
||||
$set: sceneState.deviceId,
|
||||
};
|
||||
} else {
|
||||
updateSceneStateProps(sceneState);
|
||||
}
|
||||
|
||||
if (sceneState.sceneId in newState.scenes) {
|
||||
change.scenes[sceneState.sceneId] =
|
||||
change.scenes[sceneState.sceneId] || {};
|
||||
change.scenes[sceneState.sceneId].sceneStates =
|
||||
change.scenes[sceneState.sceneId].sceneStates || {};
|
||||
const sceneStates = change.scenes[sceneState.sceneId].sceneStates;
|
||||
sceneStates.$add = sceneStates.$add || [];
|
||||
sceneStates.$add.push(sceneState.deviceId);
|
||||
} else {
|
||||
// room does not exist yet, so add to the list of pending
|
||||
// joins
|
||||
|
||||
if (!change.pendingJoins.scenes[sceneState.sceneId]) {
|
||||
change.pendingJoins.scenes[sceneState.sceneId] = {
|
||||
$set: new Set([sceneState.deviceId]),
|
||||
};
|
||||
} else {
|
||||
change.pendingJoins.scenes[sceneState.sceneId].$set.add(
|
||||
sceneState.deviceId
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newState = update(newState, change);
|
||||
|
||||
break;
|
||||
case "DEVICES_UPDATE":
|
||||
change = null;
|
||||
|
@ -220,6 +285,31 @@ function reducer(previousState, action) {
|
|||
}
|
||||
newState = update(previousState, change);
|
||||
break;
|
||||
case "STATE_SAVE":
|
||||
console.log("Store", action.sceneState);
|
||||
change = {
|
||||
sceneStates: { [action.sceneState.id]: { $set: action.sceneState } },
|
||||
};
|
||||
|
||||
if (previousState.scenes[action.sceneState.sceneId]) {
|
||||
change.scenes = {
|
||||
[action.sceneState.sceneId]: {
|
||||
sceneStates: {
|
||||
$add: [action.sceneState.id],
|
||||
},
|
||||
},
|
||||
};
|
||||
} else {
|
||||
change.pendingJoins = {
|
||||
scenes: {
|
||||
[action.sceneState.sceneId]: {
|
||||
$add: [action.sceneState.id],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
newState = update(previousState, change);
|
||||
break;
|
||||
case "ROOM_DELETE":
|
||||
if (!(action.roomId in previousState.rooms)) {
|
||||
console.warn(`Room to delete ${action.roomId} does not exist`);
|
||||
|
@ -255,8 +345,8 @@ function reducer(previousState, action) {
|
|||
// all devices again if consistent update is desired
|
||||
change = { states: { $unset: [] } };
|
||||
|
||||
for (const id of previousState.scenes[action.sceneId].states) {
|
||||
change.states.$unset.push(id);
|
||||
for (const id of previousState.scenes[action.sceneId].sceneStates) {
|
||||
change.sceneStates.$unset.push(id);
|
||||
}
|
||||
|
||||
change.scenes = { $unset: [action.sceneId] };
|
||||
|
@ -368,6 +458,8 @@ const initState = {
|
|||
automations: {},
|
||||
/** @type {[integer]Device} */
|
||||
devices: {},
|
||||
/** @type {[integer]SceneState} */
|
||||
sceneStates: {},
|
||||
};
|
||||
|
||||
function createSmartHutStore() {
|
||||
|
|
|
@ -25,6 +25,15 @@ const actions = {
|
|||
type: "DEVICE_SAVE",
|
||||
device,
|
||||
}),
|
||||
stateSave: (sceneState) => ({
|
||||
type: "STATE_SAVE",
|
||||
sceneState,
|
||||
}),
|
||||
statesUpdate: (sceneId, sceneStates) => ({
|
||||
type: "STATE_UPDATE",
|
||||
sceneId,
|
||||
sceneStates,
|
||||
}),
|
||||
devicesUpdate: (roomId, devices, partial = false) => ({
|
||||
type: "DEVICES_UPDATE",
|
||||
roomId,
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import React, { Component } from "react";
|
||||
import HomeNavbar from "./../components/HomeNavbar";
|
||||
import {
|
||||
Image,
|
||||
Divider,
|
||||
Message,
|
||||
Grid,
|
||||
Button,
|
||||
Icon,
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import React, { Component } from "react";
|
||||
import HomeNavbar from "./../components/HomeNavbar";
|
||||
import {
|
||||
Image,
|
||||
Divider,
|
||||
Message,
|
||||
Grid,
|
||||
Button,
|
||||
Icon,
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import React, { Component } from "react";
|
||||
import HomeNavbar from "./../components/HomeNavbar";
|
||||
import {
|
||||
Image,
|
||||
Divider,
|
||||
Message,
|
||||
Grid,
|
||||
Button,
|
||||
Icon,
|
||||
|
|
|
@ -53,7 +53,7 @@ class Dashboard extends Component {
|
|||
case "Devices":
|
||||
return <DevicePanel tab={this.state.activeTab} />;
|
||||
case "Scenes":
|
||||
return <ScenesPanel />;
|
||||
return <ScenesPanel tab={this.state.activeTab} />;
|
||||
case "Automations":
|
||||
return <AutomationsPanel />;
|
||||
default:
|
||||
|
|
|
@ -19,7 +19,6 @@ class ScenesNavbar extends Component {
|
|||
this.state = {
|
||||
editMode: false,
|
||||
};
|
||||
console.log(this.props.scenes);
|
||||
this.toggleEditMode = this.toggleEditMode.bind(this);
|
||||
this.openCurrentModalMobile = this.openCurrentModalMobile.bind(this);
|
||||
this.selectScene = this.selectScene.bind(this);
|
||||
|
@ -41,14 +40,11 @@ class ScenesNavbar extends Component {
|
|||
}
|
||||
|
||||
getScenes() {
|
||||
this.props
|
||||
.fetchAllScenes()
|
||||
.then(() => console.log(this.props.scenes))
|
||||
.catch(console.error);
|
||||
this.props.fetchAllScenes().catch(console.error);
|
||||
}
|
||||
|
||||
openCurrentModalMobile() {
|
||||
console.log(this.activeItemScene, this.props.sceneModalRefs);
|
||||
//console.log(this.activeItemScene, this.props.sceneModalRefs);
|
||||
const currentModal = this.props.sceneModalRefs[this.activeItemScene]
|
||||
.current;
|
||||
currentModal.openModal();
|
||||
|
@ -60,6 +56,15 @@ class ScenesNavbar extends Component {
|
|||
|
||||
selectScene(e, { id }) {
|
||||
this.activeItemScene = id || -1;
|
||||
this.getStates(id);
|
||||
}
|
||||
|
||||
getStates(sceneId) {
|
||||
if (sceneId) {
|
||||
this.props
|
||||
.fetchStates(sceneId)
|
||||
.catch((err) => console.error(`error fetching states:`, err));
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -194,11 +199,14 @@ const setActiveScene = (activeScene) => {
|
|||
|
||||
const mapStateToProps = (state, _) => ({
|
||||
scenes: state.scenes,
|
||||
activeScene: state.active.activeScene,
|
||||
sceneModalRefs: Object.keys(state.scenes).reduce(
|
||||
(acc, key) => ({ ...acc, [key]: React.createRef() }),
|
||||
{}
|
||||
),
|
||||
get isActiveDefaultScene() {
|
||||
return state.active.activeScene === -1;
|
||||
},
|
||||
activeScene: state.active.activeScene,
|
||||
});
|
||||
const ScenesNavbarContainer = connect(mapStateToProps, {
|
||||
...RemoteService,
|
||||
|
|
Loading…
Reference in a new issue