2020-04-12 15:46:16 +00:00
|
|
|
import { createStore, applyMiddleware, compose } from "redux";
|
2020-04-07 12:49:49 +00:00
|
|
|
import thunk from "redux-thunk";
|
2020-04-09 11:59:34 +00:00
|
|
|
import update from "immutability-helper";
|
2020-04-12 15:46:16 +00:00
|
|
|
import reduxWebSocket, { connect } from "@giantmachines/redux-websocket";
|
|
|
|
import { socketURL } from "./endpoint";
|
2020-04-07 10:46:55 +00:00
|
|
|
|
|
|
|
function reducer(previousState, action) {
|
2020-04-11 16:29:32 +00:00
|
|
|
let newState, change;
|
|
|
|
|
2020-04-07 10:46:55 +00:00
|
|
|
const createOrUpdateRoom = (room) => {
|
2020-04-10 15:25:52 +00:00
|
|
|
if (!newState.rooms[room.id]) {
|
2020-04-09 11:59:34 +00:00
|
|
|
newState = update(newState, {
|
2020-04-10 15:25:52 +00:00
|
|
|
rooms: { [room.id]: { $set: { ...room, devices: new Set() } } },
|
2020-04-09 11:59:34 +00:00
|
|
|
});
|
2020-04-07 10:46:55 +00:00
|
|
|
} else {
|
2020-04-09 11:59:34 +00:00
|
|
|
newState = update(newState, {
|
|
|
|
rooms: {
|
|
|
|
[room.id]: {
|
|
|
|
name: { $set: room.name },
|
|
|
|
image: { $set: room.image },
|
|
|
|
icon: { $set: room.icon },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
2020-04-07 10:46:55 +00:00
|
|
|
}
|
2020-04-10 15:52:02 +00:00
|
|
|
|
|
|
|
if (newState.pendingJoins.rooms[room.id]) {
|
|
|
|
newState = update(newState, {
|
|
|
|
pendingJoins: { rooms: { $unset: [room.id] } },
|
|
|
|
rooms: {
|
|
|
|
[room.id]: {
|
|
|
|
devices: {
|
|
|
|
$add: [...newState.pendingJoins.rooms[room.id]],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2020-04-07 10:46:55 +00:00
|
|
|
};
|
|
|
|
|
2020-04-18 14:26:12 +00:00
|
|
|
const createOrUpdateScene = (scene) => {
|
|
|
|
if (!newState.scenes[scene.id]) {
|
|
|
|
newState = update(newState, {
|
2020-04-21 12:58:36 +00:00
|
|
|
scenes: { [scene.id]: { $set: { ...scene, sceneStates: new Set() } } },
|
2020-04-18 14:26:12 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
newState = update(newState, {
|
|
|
|
scenes: {
|
|
|
|
[scene.id]: {
|
|
|
|
name: { $set: scene.name },
|
2020-05-04 13:08:17 +00:00
|
|
|
icon: { $set: scene.icon },
|
2020-05-08 11:45:13 +00:00
|
|
|
guestAccessEnabled: { $set: scene.guestAccessEnabled },
|
2020-04-18 14:26:12 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newState.pendingJoins.scenes[scene.id]) {
|
|
|
|
newState = update(newState, {
|
|
|
|
pendingJoins: { scenes: { $unset: [scene.id] } },
|
|
|
|
scenes: {
|
|
|
|
[scene.id]: {
|
2020-04-21 12:58:36 +00:00
|
|
|
sceneStates: {
|
2020-04-18 14:26:12 +00:00
|
|
|
$add: [...newState.pendingJoins.scenes[scene.id]],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-04-11 16:29:32 +00:00
|
|
|
const updateDeviceProps = (device) => {
|
|
|
|
// In some updates the information regarding a device is incomplete
|
|
|
|
// due to a fault in the type system and JPA repository management
|
|
|
|
// in the backend. Therefore to solve this avoid to delete existing
|
|
|
|
// attributes of this device in the previous state, but just update
|
|
|
|
// the new ones.
|
|
|
|
change.devices[device.id] = {};
|
|
|
|
for (const key in device) {
|
|
|
|
change.devices[device.id][key] = { $set: device[key] };
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-05-06 19:36:32 +00:00
|
|
|
const createOrUpdateRoom = (room) => {
|
|
|
|
if (!newState.rooms[room.id]) {
|
|
|
|
newState = update(newState, {
|
|
|
|
rooms: { [room.id]: { $set: { ...room, devices: new Set() } } },
|
|
|
|
});
|
2020-04-21 12:58:36 +00:00
|
|
|
} else {
|
2020-05-06 19:36:32 +00:00
|
|
|
newState = update(newState, {
|
|
|
|
rooms: {
|
|
|
|
[room.id]: {
|
|
|
|
name: { $set: room.name },
|
|
|
|
image: { $set: room.image },
|
|
|
|
icon: { $set: room.icon },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
2020-04-21 12:58:36 +00:00
|
|
|
}
|
|
|
|
|
2020-05-06 19:36:32 +00:00
|
|
|
if (newState.pendingJoins.rooms[room.id]) {
|
|
|
|
newState = update(newState, {
|
|
|
|
pendingJoins: { rooms: { $unset: [room.id] } },
|
|
|
|
rooms: {
|
|
|
|
[room.id]: {
|
|
|
|
devices: {
|
|
|
|
$add: [...newState.pendingJoins.rooms[room.id]],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const createOrUpdateScene = (scene) => {
|
|
|
|
if (!newState.scenes[scene.id]) {
|
|
|
|
newState = update(newState, {
|
|
|
|
scenes: {
|
|
|
|
[scene.id]: { $set: { ...scene, sceneStates: new Set() } },
|
|
|
|
},
|
|
|
|
});
|
2020-04-21 12:58:36 +00:00
|
|
|
} else {
|
2020-05-06 19:36:32 +00:00
|
|
|
newState = update(newState, {
|
|
|
|
scenes: {
|
|
|
|
[scene.id]: {
|
|
|
|
name: { $set: scene.name },
|
|
|
|
icon: { $set: scene.icon },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
2020-04-21 12:58:36 +00:00
|
|
|
}
|
2020-05-06 19:36:32 +00:00
|
|
|
|
|
|
|
if (newState.pendingJoins.scenes[scene.id]) {
|
|
|
|
newState = update(newState, {
|
|
|
|
pendingJoins: { scenes: { $unset: [scene.id] } },
|
|
|
|
scenes: {
|
|
|
|
[scene.id]: {
|
|
|
|
sceneStates: {
|
|
|
|
$add: [...newState.pendingJoins.scenes[scene.id]],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
2020-04-07 10:46:55 +00:00
|
|
|
}
|
2020-05-06 19:36:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const updateDeviceProps = (device) => {
|
|
|
|
// In some updates the information regarding a device is incomplete
|
|
|
|
// due to a fault in the type system and JPA repository management
|
|
|
|
// in the backend. Therefore to solve this avoid to delete existing
|
|
|
|
// attributes of this device in the previous state, but just update
|
|
|
|
// the new ones.
|
|
|
|
change.devices[device.id] = {};
|
|
|
|
for (const key in device) {
|
|
|
|
change.devices[device.id][key] = { $set: device[key] };
|
2020-04-07 10:46:55 +00:00
|
|
|
}
|
2020-05-06 19:36:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const updateSceneStateProps = (state) => {
|
|
|
|
change.sceneStates[state.id] = {};
|
|
|
|
for (const key in state) {
|
|
|
|
change.sceneStates[state.id][key] = { $set: state[key] };
|
2020-04-07 10:46:55 +00:00
|
|
|
}
|
2020-05-06 19:36:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
switch (action.type) {
|
|
|
|
case "LOGIN_UPDATE":
|
|
|
|
newState = update(previousState, { login: { $set: action.login } });
|
|
|
|
break;
|
|
|
|
case "USER_INFO_UPDATE":
|
|
|
|
newState = update(previousState, {
|
|
|
|
userInfo: { $set: action.userInfo },
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case "ROOMS_UPDATE":
|
|
|
|
newState = previousState;
|
|
|
|
for (const room of action.rooms) {
|
|
|
|
createOrUpdateRoom(room);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "HOST_ROOMS_UPDATE":
|
|
|
|
change = {
|
|
|
|
hostRooms: {
|
|
|
|
[action.hostId]: { $set: {} },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
const rooms = change.hostRooms[action.hostId].$set;
|
|
|
|
|
|
|
|
for (const room of action.rooms) {
|
|
|
|
rooms[room.id] = room;
|
|
|
|
}
|
|
|
|
|
|
|
|
newState = update(previousState, change);
|
|
|
|
break;
|
|
|
|
case "SCENES_UPDATE":
|
|
|
|
newState = previousState;
|
|
|
|
for (const scene of action.scenes) {
|
|
|
|
createOrUpdateScene(scene);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "HOST_SCENES_UPDATE":
|
|
|
|
change = {
|
|
|
|
hostScenes: {
|
|
|
|
[action.hostId]: { $set: action.scenes }, // stored as array
|
|
|
|
},
|
|
|
|
};
|
2020-04-10 22:42:23 +00:00
|
|
|
|
2020-05-06 19:36:32 +00:00
|
|
|
newState = update(previousState, change);
|
|
|
|
break;
|
|
|
|
case "STATES_UPDATE":
|
|
|
|
//console.log(action.sceneStates);
|
|
|
|
change = null;
|
|
|
|
|
|
|
|
// if scene is given, delete all sceneStates in that scene
|
|
|
|
// and remove any join between that scene and deleted
|
|
|
|
// sceneStates
|
|
|
|
change = {
|
|
|
|
scenes: {
|
|
|
|
[action.sceneId]: { sceneStates: { $set: new Set() } },
|
|
|
|
},
|
|
|
|
sceneStates: { $unset: [] },
|
|
|
|
};
|
2020-04-07 10:46:55 +00:00
|
|
|
|
2020-05-06 19:36:32 +00:00
|
|
|
const scene = previousState.scenes[action.sceneId];
|
|
|
|
for (const stateId of scene.sceneStates) {
|
|
|
|
change.sceneStates.$unset.push(stateId);
|
|
|
|
}
|
2020-04-07 10:46:55 +00:00
|
|
|
|
2020-05-06 19:36:32 +00:00
|
|
|
newState = update(previousState, change);
|
2020-04-10 15:52:02 +00:00
|
|
|
|
2020-05-06 19:36:32 +00:00
|
|
|
change = {
|
|
|
|
sceneStates: {},
|
|
|
|
scenes: {},
|
|
|
|
pendingJoins: { scenes: {} },
|
2020-04-10 15:52:02 +00:00
|
|
|
};
|
2020-05-06 19:36:32 +00:00
|
|
|
|
|
|
|
for (const sceneState of action.sceneStates) {
|
|
|
|
if (!newState.sceneStates[sceneState.id]) {
|
|
|
|
change.sceneStates[sceneState.id] = {
|
|
|
|
$set: sceneState,
|
|
|
|
};
|
|
|
|
} 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.id);
|
|
|
|
} 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.id]),
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
change.pendingJoins.scenes[sceneState.sceneId].$set.add(
|
|
|
|
sceneState.id
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
newState = update(newState, change);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case "DEVICES_UPDATE":
|
|
|
|
change = null;
|
|
|
|
|
|
|
|
// if room is given, delete all devices in that room
|
|
|
|
// and remove any join between that room and deleted
|
|
|
|
// devices
|
|
|
|
if (action.roomId) {
|
|
|
|
change = {
|
|
|
|
rooms: {
|
|
|
|
[action.roomId]: { devices: { $set: new Set() } },
|
|
|
|
},
|
|
|
|
devices: { $unset: [] },
|
|
|
|
};
|
|
|
|
|
|
|
|
const room = newState.rooms[action.roomId];
|
|
|
|
for (const deviceId of room.devices) {
|
|
|
|
change.devices.$unset.push(deviceId);
|
|
|
|
}
|
|
|
|
} else if (action.partial) {
|
|
|
|
// if the update is partial and caused by an operation on an input
|
|
|
|
// device (like /switch/operate), iteratively remove deleted
|
|
|
|
// devices and their join with their corresponding room.
|
|
|
|
change = {
|
|
|
|
devices: { $unset: [] },
|
|
|
|
rooms: {},
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const device of action.devices) {
|
|
|
|
if (!previousState.devices[device.id]) continue;
|
|
|
|
change.devices.$unset.push(device.id);
|
|
|
|
const roomId = previousState.devices[device.id].roomId;
|
|
|
|
|
|
|
|
if (roomId in previousState.rooms) {
|
|
|
|
change.rooms[roomId] = change.rooms[roomId] || {
|
|
|
|
devices: { $remove: [] },
|
|
|
|
};
|
|
|
|
change.rooms[roomId].devices.$remove.push(device.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// otherwise, just delete all devices and all joins
|
|
|
|
// between rooms and devices
|
|
|
|
change = {
|
|
|
|
devices: { $set: {} },
|
|
|
|
rooms: {},
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const room of Object.values(previousState.rooms)) {
|
|
|
|
if (change.rooms[room.id]) {
|
|
|
|
change.rooms[room.id].devices = { $set: new Set() };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
newState = update(previousState, change);
|
|
|
|
|
|
|
|
change = {
|
|
|
|
devices: {},
|
|
|
|
rooms: {},
|
|
|
|
pendingJoins: { rooms: {} },
|
|
|
|
};
|
|
|
|
for (const device of action.devices) {
|
|
|
|
if (!newState.devices[device.id]) {
|
|
|
|
change.devices[device.id] = { $set: device };
|
|
|
|
} else {
|
|
|
|
updateDeviceProps(device);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (device.roomId in newState.rooms) {
|
|
|
|
change.rooms[device.roomId] =
|
|
|
|
change.rooms[device.roomId] || {};
|
|
|
|
change.rooms[device.roomId].devices =
|
|
|
|
change.rooms[device.roomId].devices || {};
|
|
|
|
const devices = change.rooms[device.roomId].devices;
|
|
|
|
devices.$add = devices.$add || [];
|
|
|
|
devices.$add.push(device.id);
|
|
|
|
} else {
|
|
|
|
// room does not exist yet, so add to the list of pending
|
|
|
|
// joins
|
|
|
|
|
|
|
|
if (!change.pendingJoins.rooms[device.roomId]) {
|
|
|
|
change.pendingJoins.rooms[device.roomId] = {
|
|
|
|
$set: new Set([device.id]),
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
change.pendingJoins.rooms[device.roomId].$set.add(
|
|
|
|
device.id
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
newState = update(newState, change);
|
|
|
|
break;
|
|
|
|
case "HOST_DEVICES_UPDATE":
|
|
|
|
newState = action.partial
|
|
|
|
? previousState
|
|
|
|
: update(previousState, {
|
|
|
|
hostDevices: { [action.hostId]: { $set: {} } },
|
|
|
|
});
|
|
|
|
newState.hostDevices[action.hostId] =
|
|
|
|
newState.hostDevices[action.hostId] || {};
|
|
|
|
change = {
|
|
|
|
hostDevices: {
|
|
|
|
[action.hostId]: {},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
const deviceMap = change.hostDevices[action.hostId];
|
|
|
|
|
|
|
|
for (const device of action.devices) {
|
|
|
|
deviceMap[device.id] = { $set: device };
|
|
|
|
}
|
|
|
|
|
|
|
|
newState = update(newState, change);
|
|
|
|
break;
|
|
|
|
case "AUTOMATION_UPDATE":
|
|
|
|
const automations = {};
|
|
|
|
for (const automation of action.automations) {
|
|
|
|
automations[automation.id] = automation;
|
|
|
|
}
|
|
|
|
|
|
|
|
change = {
|
|
|
|
automations: { $set: automations },
|
|
|
|
};
|
|
|
|
newState = update(previousState, change);
|
|
|
|
break;
|
|
|
|
case "ROOM_SAVE":
|
|
|
|
newState = previousState;
|
|
|
|
createOrUpdateRoom(action.room);
|
|
|
|
break;
|
|
|
|
case "SCENE_SAVE":
|
|
|
|
newState = previousState;
|
|
|
|
createOrUpdateScene(action.scene);
|
|
|
|
break;
|
|
|
|
case "DEVICE_SAVE":
|
|
|
|
change = {
|
|
|
|
devices: { [action.device.id]: { $set: action.device } },
|
|
|
|
};
|
|
|
|
|
|
|
|
if (previousState.rooms[action.device.roomId]) {
|
|
|
|
change.rooms = {
|
|
|
|
[action.device.roomId]: {
|
|
|
|
devices: {
|
|
|
|
$add: [action.device.id],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
change.pendingJoins = {
|
|
|
|
rooms: {
|
|
|
|
[action.device.roomId]: {
|
|
|
|
$add: [action.device.id],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
newState = update(previousState, change);
|
|
|
|
break;
|
|
|
|
case "HOST_DEVICE_SAVE":
|
|
|
|
change = {
|
|
|
|
hostDevices: {
|
|
|
|
[action.hostId]: {
|
|
|
|
[action.device.id]: {
|
|
|
|
$set: action.device,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
newState = update(previousState, change);
|
|
|
|
break;
|
|
|
|
case "HOST_DEVICES_DELETE":
|
|
|
|
change = {
|
|
|
|
hostDevices: {
|
|
|
|
[action.hostId]: {
|
|
|
|
$unset: [action.deviceIds],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
newState = update(previousState, change);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "AUTOMATION_SAVE":
|
|
|
|
change = {
|
|
|
|
automations: {
|
|
|
|
[action.automation.id]: { $set: action.automation },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
newState = update(previousState, change);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "STATE_SAVE":
|
|
|
|
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`);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This update does not ensure the consistent update of switchId/dimmerId properties
|
|
|
|
// on output devices connected to an input device in this room. Please manually request
|
|
|
|
// all devices again if consistent update is desired
|
|
|
|
change = { devices: { $unset: [] } };
|
|
|
|
|
|
|
|
for (const id of previousState.rooms[action.roomId].devices) {
|
|
|
|
change.devices.$unset.push(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
change.rooms = { $unset: [action.roomId] };
|
|
|
|
|
|
|
|
if (previousState.active.activeRoom === action.roomId) {
|
|
|
|
change.active = { activeRoom: { $set: -1 } };
|
|
|
|
}
|
|
|
|
|
|
|
|
newState = update(previousState, change);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "AUTOMATION_DELETE":
|
|
|
|
change = {
|
|
|
|
automations: { $unset: [action.id] },
|
|
|
|
};
|
|
|
|
|
|
|
|
newState = update(previousState, change);
|
|
|
|
break;
|
|
|
|
case "SCENE_DELETE":
|
|
|
|
if (!(action.sceneId in previousState.scenes)) {
|
|
|
|
console.warn(
|
|
|
|
`Scene to delete ${action.sceneId} does not exist`
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This update does not ensure the consistent update of switchId/dimmerId properties
|
|
|
|
// on output devices connected to an input device in this room. Please manually request
|
|
|
|
// all devices again if consistent update is desired
|
|
|
|
change = { sceneStates: { $unset: [] } };
|
|
|
|
|
|
|
|
for (const id of previousState.scenes[action.sceneId].sceneStates) {
|
|
|
|
change.sceneStates.$unset.push(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
change.scenes = { $unset: [action.sceneId] };
|
|
|
|
|
|
|
|
if (previousState.active.activeScene === action.sceneId) {
|
|
|
|
change.active = { activeScene: { $set: -1 } };
|
|
|
|
}
|
|
|
|
|
|
|
|
newState = update(previousState, change);
|
|
|
|
break;
|
|
|
|
case "STATE_DELETE":
|
|
|
|
if (!(action.stateId in previousState.sceneStates)) {
|
|
|
|
console.warn(
|
|
|
|
`State to delete ${action.stateId} does not exist`
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
change = {
|
|
|
|
sceneStates: { $unset: [action.stateId] },
|
|
|
|
};
|
|
|
|
|
|
|
|
if (
|
|
|
|
previousState.scenes[
|
|
|
|
previousState.sceneStates[action.stateId].sceneId
|
|
|
|
]
|
|
|
|
) {
|
|
|
|
change.scenes = {
|
|
|
|
[previousState.sceneStates[action.stateId].sceneId]: {
|
|
|
|
sceneStates: { $remove: [action.stateId] },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
newState = update(previousState, change);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "DEVICE_DELETE":
|
|
|
|
if (!(action.deviceId in previousState.devices)) {
|
|
|
|
console.warn(
|
|
|
|
`Device to delete ${action.deviceId} does not exist`
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
change = {
|
|
|
|
devices: { $unset: [action.deviceId] },
|
|
|
|
};
|
|
|
|
|
|
|
|
if (
|
|
|
|
previousState.rooms[
|
|
|
|
previousState.devices[action.deviceId].roomId
|
|
|
|
]
|
|
|
|
) {
|
|
|
|
change.rooms = {
|
|
|
|
[previousState.devices[action.deviceId].roomId]: {
|
|
|
|
devices: { $remove: [action.deviceId] },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
newState = update(previousState, change);
|
|
|
|
break;
|
|
|
|
case "LOGOUT":
|
|
|
|
newState = update(initState, {});
|
|
|
|
break;
|
|
|
|
case "SET_ACTIVE":
|
|
|
|
newState = update(previousState, {
|
|
|
|
active: {
|
|
|
|
[action.key]: {
|
|
|
|
$set: action.value,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case "REDUX_WEBSOCKET::MESSAGE":
|
|
|
|
const allDevices = JSON.parse(action.payload.message);
|
|
|
|
const devices = allDevices.filter(
|
|
|
|
(d) =>
|
|
|
|
(d.fromHostId === null || d.fromHostId === undefined) &&
|
|
|
|
!d.deleted
|
|
|
|
);
|
|
|
|
const hostDevicesMapByHostId = allDevices
|
|
|
|
.filter((d) => d.fromHostId)
|
|
|
|
.reduce((a, e) => {
|
|
|
|
const hostId = e.fromHostId;
|
|
|
|
//delete e.fromHostId;
|
|
|
|
a[hostId] = a[hostId] || { updated: [], deletedIds: [] };
|
|
|
|
if (e.deleted) {
|
|
|
|
a[hostId].deletedIds.push(e.id);
|
|
|
|
} else {
|
|
|
|
a[hostId].updated.push(e);
|
|
|
|
}
|
|
|
|
return a;
|
|
|
|
}, {});
|
|
|
|
newState = reducer(previousState, {
|
|
|
|
type: "DEVICES_UPDATE",
|
|
|
|
partial: true,
|
|
|
|
devices,
|
|
|
|
});
|
|
|
|
for (const hostId in hostDevicesMapByHostId) {
|
|
|
|
if (hostDevicesMapByHostId[hostId].updated.length > 0)
|
|
|
|
newState = reducer(newState, {
|
|
|
|
type: "HOST_DEVICES_UPDATE",
|
|
|
|
devices: hostDevicesMapByHostId[hostId].updated,
|
|
|
|
partial: true,
|
|
|
|
hostId,
|
|
|
|
});
|
|
|
|
if (hostDevicesMapByHostId[hostId].deletedIds.length > 0) {
|
|
|
|
newState = reducer(newState, {
|
|
|
|
type: "HOST_DEVICES_DELETE",
|
|
|
|
deviceIds: hostDevicesMapByHostId[hostId].deletedIds,
|
|
|
|
partial: true,
|
|
|
|
hostId,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "HG_UPDATE":
|
|
|
|
newState = update(previousState, {
|
|
|
|
[action.key]: { $set: action.value },
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
console.warn(`Action type ${action.type} unknown`, action);
|
|
|
|
return previousState;
|
|
|
|
}
|
|
|
|
return newState;
|
2020-04-07 10:46:55 +00:00
|
|
|
}
|
|
|
|
|
2020-04-09 15:24:30 +00:00
|
|
|
const initState = {
|
2020-05-06 19:36:32 +00:00
|
|
|
pendingJoins: {
|
|
|
|
rooms: {},
|
|
|
|
scenes: {},
|
|
|
|
automations: {},
|
|
|
|
},
|
|
|
|
active: {
|
|
|
|
activeRoom: -1,
|
|
|
|
activeTab: "Devices",
|
|
|
|
activeScene: -1,
|
|
|
|
activeAutomation: -1,
|
|
|
|
activeHost: -1,
|
|
|
|
},
|
|
|
|
login: {
|
|
|
|
loggedIn: false,
|
|
|
|
token: null,
|
|
|
|
},
|
|
|
|
userInfo: null,
|
|
|
|
/** @type {[integer]Room} */
|
2020-04-10 15:52:02 +00:00
|
|
|
rooms: {},
|
2020-05-06 19:36:32 +00:00
|
|
|
/** @type {[integer]Scene} */
|
2020-04-15 13:54:30 +00:00
|
|
|
scenes: {},
|
2020-05-06 19:36:32 +00:00
|
|
|
hostScenes: {},
|
|
|
|
/** @type {[integer]Automation} */
|
2020-04-15 13:54:30 +00:00
|
|
|
automations: {},
|
2020-05-06 19:36:32 +00:00
|
|
|
/** @type {[integer]Device} */
|
|
|
|
devices: {},
|
|
|
|
/** @type {[integer]SceneState} */
|
|
|
|
sceneStates: {},
|
|
|
|
/** @type {User[]} */
|
|
|
|
guests: [],
|
|
|
|
/** @type {User[]} */
|
|
|
|
hosts: [],
|
|
|
|
/** @type {[integer]Device} */
|
|
|
|
hostDevices: {},
|
|
|
|
/** @type {[integer]Eoom} */
|
|
|
|
hostRooms: {},
|
2020-04-09 15:24:30 +00:00
|
|
|
};
|
|
|
|
|
2020-04-07 12:49:49 +00:00
|
|
|
function createSmartHutStore() {
|
2020-05-06 19:36:32 +00:00
|
|
|
const token = localStorage.getItem("token");
|
|
|
|
const exp = localStorage.getItem("exp");
|
2020-04-07 12:49:49 +00:00
|
|
|
|
2020-05-06 19:36:32 +00:00
|
|
|
const initialState = update(initState, {
|
|
|
|
login: {
|
|
|
|
token: { $set: token },
|
|
|
|
loggedIn: { $set: !!(token && exp > new Date().getTime()) },
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!initialState.login.loggedIn) {
|
|
|
|
localStorage.removeItem("token");
|
|
|
|
localStorage.removeItem("exp");
|
|
|
|
initialState.login.token = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const store = createStore(
|
|
|
|
reducer,
|
|
|
|
initialState,
|
|
|
|
compose(applyMiddleware(thunk), applyMiddleware(reduxWebSocket()))
|
|
|
|
);
|
|
|
|
if (initialState.login.loggedIn) {
|
|
|
|
store.dispatch(connect(socketURL(token)));
|
|
|
|
}
|
|
|
|
return store;
|
2020-04-07 12:49:49 +00:00
|
|
|
}
|
2020-04-07 10:46:55 +00:00
|
|
|
|
2020-04-07 12:49:49 +00:00
|
|
|
const smartHutStore = createSmartHutStore();
|
2020-04-07 10:46:55 +00:00
|
|
|
export default smartHutStore;
|