863 lines
27 KiB
JavaScript
863 lines
27 KiB
JavaScript
import smartHutStore from "./store";
|
|
import actions from "./storeActions";
|
|
import axios from "axios";
|
|
import { endpointURL, socketURL } from "./endpoint";
|
|
import { connect, disconnect } from "@giantmachines/redux-websocket";
|
|
|
|
/**
|
|
* An object returned by promise rejections in remoteservice
|
|
* @typedef {Error} RemoteError
|
|
* @property {String[]} messages a list of user-friendly error messages to show;
|
|
*/
|
|
class RemoteError extends Error {
|
|
messages;
|
|
|
|
constructor(messages) {
|
|
super(messages.join(" - "));
|
|
this.messages = messages;
|
|
}
|
|
}
|
|
|
|
const Endpoint = {
|
|
axiosInstance: axios.create({
|
|
baseURL: endpointURL(),
|
|
validateStatus: (status) => status >= 200 && status < 300,
|
|
}),
|
|
|
|
/**
|
|
* Returns token for current session, null if logged out
|
|
* @returns {String|null} the token
|
|
*/
|
|
get token() {
|
|
return smartHutStore.getState().login.token;
|
|
},
|
|
|
|
/**
|
|
* Performs an authenticated request
|
|
* @param {get|post|put|delete} the desired method
|
|
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
|
* @param {[String]String} query query ('?') parameters (no params by default)
|
|
* @param {any} body the JSON request body
|
|
*/
|
|
send: (method, route, query = {}, body = null) => {
|
|
if (!Endpoint.token) {
|
|
throw new Error("No token while performing authenticated request");
|
|
}
|
|
|
|
return Endpoint.axiosInstance(route, {
|
|
method: method,
|
|
params: query,
|
|
data: ["put", "post"].indexOf(method) !== -1 ? body : null,
|
|
headers: {
|
|
Authorization: `Bearer ${Endpoint.token}`,
|
|
},
|
|
}).then((res) => {
|
|
if (!res.data && method !== "delete") {
|
|
console.error("Response body is empty");
|
|
return null;
|
|
} else {
|
|
return res;
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Performs a non-authenticated post and put request for registration, reset-password
|
|
* @param {post} the desired method
|
|
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
|
* @param {[String]String} query query ('?') parameters (no params by default)
|
|
* @param {any} body the JSON request body
|
|
*/
|
|
sendNA: (method, route, query = {}, body = null) => {
|
|
return Endpoint.axiosInstance(route, {
|
|
method: method,
|
|
params: query,
|
|
data: ["put", "post"].indexOf(method) !== -1 ? body : null,
|
|
}).then((res) => {
|
|
if (!res.data) {
|
|
console.error("Response body is empty");
|
|
return null;
|
|
} else {
|
|
return res;
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Performs login
|
|
* @param {String} usernameOrEmail
|
|
* @param {String} password
|
|
* @returns {Promise<String, *>} promise that resolves to the token string
|
|
* and rejects to the axios error.
|
|
*/
|
|
login: (usernameOrEmail, password) => {
|
|
return Endpoint.axiosInstance
|
|
.post(`/auth/login`, {
|
|
usernameOrEmail,
|
|
password,
|
|
})
|
|
.then((res) => {
|
|
localStorage.setItem("token", res.data.jwttoken);
|
|
localStorage.setItem("exp", new Date().getTime() + 5 * 60 * 60 * 1000);
|
|
return res.data.jwttoken;
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Returns an immediately resolved promise for the socket logouts
|
|
* @return {Promise<Undefined, _>} An always-resolved promise
|
|
*/
|
|
logout: () => {
|
|
localStorage.removeItem("token");
|
|
localStorage.removeItem("exp");
|
|
return Promise.resolve(void 0);
|
|
},
|
|
|
|
/**
|
|
* Performs an authenticated GET request
|
|
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
|
* @param {[String]String} query query ('?') parameters (no params by default)
|
|
* @returns {Promise<*, *>} The Axios-generated promise
|
|
*/
|
|
get(route, query = {}) {
|
|
return this.send("get", route, query);
|
|
},
|
|
|
|
/**
|
|
* Performs an authenticated POST request
|
|
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
|
* @param {[String]String} query query ('?') parameters (no params by default)
|
|
* @param {any} body the JSON request body
|
|
* @returns {Promise<*, *>} The Axios-generated promise
|
|
*/
|
|
post(route, query, body) {
|
|
return this.send("post", route, query, body);
|
|
},
|
|
|
|
/**
|
|
* Performs a non-authenticated POST request
|
|
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
|
* @param {[String]String} query query ('?') parameters (no params by default)
|
|
* @param {any} body the JSON request body
|
|
* @returns {Promise<*, *>} The Axios-generated promise
|
|
*/
|
|
postNA(route, query, body) {
|
|
return this.sendNA("post", route, query, body);
|
|
},
|
|
|
|
/**
|
|
* Performs an authenticated PUT request
|
|
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
|
* @param {[String]String} query query ('?') parameters (no params by default)
|
|
* @param {any} body the JSON request body
|
|
* @returns {Promise<*, *>} The Axios-generated promise
|
|
*/
|
|
put(route, query = {}, body = {}) {
|
|
return this.send("put", route, query, body);
|
|
},
|
|
|
|
/**
|
|
* Performs a non-authenticated PUT request
|
|
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
|
* @param {[String]String} query query ('?') parameters (no params by default)
|
|
* @param {any} body the JSON request body
|
|
* @returns {Promise<*, *>} The Axios-generated promise
|
|
*/
|
|
putNA(route, query = {}, body = {}) {
|
|
return this.sendNA("put", route, query, body);
|
|
},
|
|
|
|
/**
|
|
* Performs an authenticated DELETE request
|
|
* @param {get|post|put|delete} the desired method
|
|
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
|
* @param {[String]String} query query ('?') parameters (no params by default)
|
|
* @returns {Promise<*, *>} The Axios-generated promise
|
|
*/
|
|
delete(route, query = {}) {
|
|
return this.send("delete", route, query);
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Given an error response, returns an array of user
|
|
* friendly messages to display to the user
|
|
* @param {*} err the Axios error reponse object
|
|
* @returns {RemoteError} user friendly error messages
|
|
*/
|
|
function parseValidationErrors(err) {
|
|
if (
|
|
err.response &&
|
|
err.response.status === 400 &&
|
|
err.response.data &&
|
|
Array.isArray(err.response.data.errors)
|
|
) {
|
|
throw new RemoteError([
|
|
...new Set(err.response.data.errors.map((e) => e.defaultMessage)),
|
|
]);
|
|
} else {
|
|
console.warn("Non validation error", err);
|
|
throw new RemoteError(["Network error"]);
|
|
}
|
|
}
|
|
|
|
export const RemoteService = {
|
|
/**
|
|
* Performs login
|
|
* @param {String} usernameOrEmail
|
|
* @param {String} password
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
login: (usernameOrEmail, password) => {
|
|
return (dispatch) => {
|
|
return Endpoint.login(usernameOrEmail, password)
|
|
.then((token) => {
|
|
dispatch(actions.loginSuccess(token));
|
|
dispatch(connect(socketURL(token)));
|
|
})
|
|
.catch((err) => {
|
|
console.warn("login error", err);
|
|
throw new RemoteError([
|
|
err.response && err.response.status === 401
|
|
? "Wrong credentials"
|
|
: "An error occurred while logging in",
|
|
]);
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Performs logout
|
|
*/
|
|
logout: () => {
|
|
return (dispatch) =>
|
|
Endpoint.logout().then(() => {
|
|
dispatch(disconnect());
|
|
dispatch(actions.logout());
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Fetches user information via REST calls, if it is logged in
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
fetchUserInfo: () => {
|
|
return (dispatch) => {
|
|
return Endpoint.get("/auth/profile")
|
|
.then((res) => void dispatch(actions.userInfoUpdate(res.data)))
|
|
.catch((err) => {
|
|
console.warn("Fetch user info error", err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Fetches all rooms that belong to this user. This call does not
|
|
* populate the devices attribute in rooms.
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
fetchAllRooms: () => {
|
|
return (dispatch) => {
|
|
return Endpoint.get("/room")
|
|
.then((res) => void dispatch(actions.roomsUpdate(res.data)))
|
|
.catch((err) => {
|
|
console.error("Fetch all rooms error", err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Fetches all scenes that belong to this user. This call does not
|
|
* populate the devices attribute in scenes.
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
fetchAllScenes: () => {
|
|
return (dispatch) => {
|
|
return Endpoint.get("/scene")
|
|
.then((res) => void dispatch(actions.scenesUpdate(res.data)))
|
|
.catch((err) => {
|
|
console.error("Fetch all scenes error", err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Fetches all devices in a particular room, or fetches all devices.
|
|
* This also updates the devices attribute on values in the map rooms.
|
|
* @param {Number|null} roomId the rsoom to which fetch devices
|
|
* from, null to fetch from all rooms
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
fetchDevices: (roomId = null) => {
|
|
return (dispatch) => {
|
|
return Endpoint.get(roomId ? `/room/${roomId}/device` : "/device")
|
|
.then((res) => void dispatch(actions.devicesUpdate(roomId, res.data)))
|
|
.catch((err) => {
|
|
console.error(`Fetch devices roomId=${roomId} error`, err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Fetches all the automations
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
fetchAutomations: () => {
|
|
return (dispatch) => {
|
|
return Endpoint.get("/automation/")
|
|
.then((res) => void dispatch(actions.automationsUpdate(res.data)))
|
|
.catch((err) => {
|
|
console.error(`Fetch automations error`, err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Fetches all devices in a particular scene, or fetches all devices.
|
|
* This also updates the devices attribute on values in the map scenes.
|
|
* @param {Number} sceneId the scene to which fetch devices
|
|
* from, null to fetch from all scenes
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
fetchStates: (sceneId) => {
|
|
return (dispatch) => {
|
|
return Endpoint.get(`/scene/${sceneId}/states`)
|
|
.then((res) => void dispatch(actions.statesUpdate(sceneId, res.data)))
|
|
.catch((err) => {
|
|
console.error(`Fetch devices sceneId=${sceneId} error`, err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Creates/Updates a room with the given data
|
|
* @param {String} data.name the room's name,
|
|
* @param {String} data.icon the room's icon name in SemanticUI icons
|
|
* @param {String} data.image ths room's image, as base64
|
|
* @param {Number|null} roomId the room's id if update, null for creation
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
saveRoom: (data, roomId = null) => {
|
|
return (dispatch) => {
|
|
data = {
|
|
name: data.name,
|
|
icon: data.icon,
|
|
image: data.image,
|
|
};
|
|
|
|
return (roomId
|
|
? Endpoint.put(`/room/${roomId}`, {}, data)
|
|
: Endpoint.post(`/room`, {}, data)
|
|
)
|
|
.then((res) => void dispatch(actions.roomSave(res.data)))
|
|
.catch(parseValidationErrors);
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Creates/Updates a scene with the given data
|
|
* @param {String} data.name the scene's name,
|
|
* @param {Number|null} sceneId the scene's id if update, null for creation
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
saveScene: (data, sceneId = null) => {
|
|
return (dispatch) => {
|
|
data = {
|
|
name: data.name,
|
|
};
|
|
|
|
return (sceneId
|
|
? Endpoint.put(`/scene/${sceneId}`, {}, data)
|
|
: Endpoint.post(`/scene`, {}, data)
|
|
)
|
|
.then((res) => void dispatch(actions.sceneSave(res.data)))
|
|
.catch(parseValidationErrors);
|
|
};
|
|
},
|
|
|
|
//
|
|
updateState: (data, type) => {
|
|
return (dispatch) => {
|
|
let url;
|
|
if (data.on !== undefined) {
|
|
url = "/switchableState";
|
|
} else {
|
|
url = "/dimmableState";
|
|
}
|
|
|
|
return Endpoint.put(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"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
deleteState: (id, type) => {
|
|
return (dispatch) => {
|
|
let url;
|
|
if (type === "dimmableState") {
|
|
url = "/dimmableState";
|
|
} else {
|
|
url = "/switchableState";
|
|
}
|
|
return Endpoint.delete(url + `/${id}`)
|
|
.then((_) => dispatch(actions.stateDelete(id)))
|
|
.catch((err) => {
|
|
console.warn("state delete error", err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
sceneApply: (id) => {
|
|
return (dispatch) => {
|
|
let url = `/scene/${id}/apply`;
|
|
|
|
return Endpoint.post(url)
|
|
.then((res) => dispatch(actions.deviceOperationUpdate(res.data)))
|
|
.catch((err) => {
|
|
console.warn("scene apply error", err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Creates/Updates a device 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 {Device} 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
|
|
*/
|
|
saveDevice: (data) => {
|
|
return (dispatch) => {
|
|
let url = "/device";
|
|
if ((data.id && data.flowType === "OUTPUT") || !data.id) {
|
|
url = "/" + data.kind;
|
|
}
|
|
|
|
return Endpoint[data.id ? "put" : "post"](url, {}, data)
|
|
.then((res) => {
|
|
dispatch(actions.deviceSave(res.data));
|
|
return res.data;
|
|
})
|
|
.catch((err) => {
|
|
console.warn("Update device: ", data, "error: ", err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
fastUpdateAutomation: (automation) => {
|
|
return (dispatch) => {
|
|
return Endpoint.put("/automation/fast", {}, automation)
|
|
.then((res) => dispatch(actions.automationSave(res.data)))
|
|
.catch((err) => {
|
|
console.warn("Update automation: ", automation, "error: ", err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Creates/Updates an automation with the given data. If
|
|
* data.id is truthy, then a update call is performed,
|
|
* otherwise a create call is performed.
|
|
* @param {Automation} data the automation to update.
|
|
* @returns {Promise<Device, RemoteError>} promise that resolves to the saved device and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
saveAutomation: (data) => {
|
|
const { automation, triggerList, order } = data;
|
|
console.log("automation: ", automation, triggerList, order);
|
|
automation.triggers = [];
|
|
automation.scenes = [];
|
|
return (dispatch) => {
|
|
let urlAutomation = "/automation";
|
|
let urlBooleanTrigger = "/booleanTrigger";
|
|
let urlRangeTrigger = "/rangeTrigger";
|
|
let urlScenePriority = "/scenePriority";
|
|
|
|
let rangeTriggerList = triggerList.filter((trigger) =>
|
|
trigger.hasOwnProperty("operand")
|
|
);
|
|
let booleanTriggerList = triggerList.filter(
|
|
(trigger) => !trigger.hasOwnProperty("operand")
|
|
);
|
|
|
|
return Endpoint["post"](urlAutomation, {}, automation).then(
|
|
async (automationRes) => {
|
|
const { id } = automationRes.data;
|
|
// Introduce the range triggers in the automation
|
|
for (let t of rangeTriggerList) {
|
|
const trigger = {
|
|
automationId: id,
|
|
deviceId: t.device,
|
|
operator: t.operand,
|
|
range: t.value,
|
|
};
|
|
let resRange = await Endpoint.post(urlRangeTrigger, {}, trigger);
|
|
automation.triggers.push(resRange.data);
|
|
}
|
|
|
|
for (let t of booleanTriggerList) {
|
|
const trigger = {
|
|
automationId: id,
|
|
deviceId: t.device,
|
|
on: t.value,
|
|
};
|
|
let resBoolean = await Endpoint.post(
|
|
urlBooleanTrigger,
|
|
{},
|
|
trigger
|
|
);
|
|
automation.triggers.push(resBoolean.data);
|
|
console.log("TRIGGERS: ", automation);
|
|
}
|
|
|
|
for (let [priority, sceneId] of order.entries()) {
|
|
const scenePriority = {
|
|
automationId: id,
|
|
priority,
|
|
sceneId,
|
|
};
|
|
let resScenes = await Endpoint["post"](
|
|
urlScenePriority,
|
|
{},
|
|
scenePriority
|
|
);
|
|
automation.scenes.push(resScenes.data);
|
|
}
|
|
automation.id = id;
|
|
dispatch(actions.automationSave(automation));
|
|
}
|
|
);
|
|
};
|
|
},
|
|
|
|
/**
|
|
* 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.
|
|
* Output devices for Dimmers input can be: Dimmable Light.
|
|
*
|
|
* @typedef {"switch" | "buttonDimmer" | "knobDimmer"} ConnectableInput
|
|
*
|
|
* @param {ConnectableInput} newDevice.kind kind of the input device
|
|
* @param {Integer} newDevice.id id of the input device
|
|
* @param {Integer[]} outputs ids of the output device
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
connectOutputs: (newDevice, outputs) => {
|
|
return (dispatch) => {
|
|
let url = `/${newDevice.kind}/${newDevice.id}/lights`;
|
|
|
|
return Endpoint.post(url, {}, outputs)
|
|
.then((res) => {
|
|
dispatch(actions.deviceOperationUpdate(res.data));
|
|
return res.data;
|
|
})
|
|
.catch((err) => {
|
|
console.warn(
|
|
"ConnectOutputs of ",
|
|
newDevice.id,
|
|
" with outputs: ",
|
|
outputs,
|
|
"error: ",
|
|
err
|
|
);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
_operateInput: (url, getUrl, action) => {
|
|
return (dispatch) => {
|
|
return Endpoint.put(url, {}, action)
|
|
.then(async (res) => {
|
|
const inputDevice = await Endpoint.get(getUrl);
|
|
delete inputDevice.outputs;
|
|
dispatch(
|
|
actions.deviceOperationUpdate([...res.data, inputDevice.data])
|
|
);
|
|
})
|
|
.catch((err) => {
|
|
console.warn(`${url} error`, err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Changes the state of a switch, by turning it on, off or toggling it.
|
|
*
|
|
* @typedef {"ON" | "OFF" | "TOGGLE"} SwitchOperation
|
|
*
|
|
* @param {Number} switchId the switch device id
|
|
* @param {SwitchOperation} type the operation to perform on the switch
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
switchOperate: (switchId, type) => {
|
|
return RemoteService._operateInput(
|
|
"/switch/operate",
|
|
`/switch/${switchId}`,
|
|
{
|
|
type: type.toUpperCase(),
|
|
id: switchId,
|
|
}
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Turns a knob dimmer to a specific amount
|
|
*
|
|
* @param {Number} dimmerId the knob dimmer id
|
|
* @param {number} intensity the absolute intensity to dim to. Must be >=0 and <= 100.
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
knobDimmerDimTo: (dimmerId, intensity) => {
|
|
return RemoteService._operateInput(
|
|
"/knobDimmer/dimTo",
|
|
`/knobDimmer/${dimmerId}`,
|
|
{
|
|
intensity,
|
|
id: dimmerId,
|
|
}
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Turns a button dimmer up or down
|
|
*
|
|
* @typedef {"UP" | "DOWN"} ButtonDimmerDimType
|
|
*
|
|
* @param {Number} dimmerId the button dimmer id
|
|
* @param {ButtonDimmerDimType} dimType the type of dim to perform
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
buttonDimmerDim: (dimmerId, dimType) => {
|
|
return RemoteService._operateInput(
|
|
"/buttonDimmer/dim",
|
|
`/buttonDimmer/${dimmerId}`,
|
|
{
|
|
dimType,
|
|
id: dimmerId,
|
|
}
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Resets the meter on a smart plug
|
|
*
|
|
* @param {Number} smartPlugId the smart plug to reset
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
smartPlugReset(smartPlugId) {
|
|
return (dispatch) => {
|
|
return Endpoint.delete(`/smartPlug/${smartPlugId}/meter`)
|
|
.then((res) => dispatch(actions.deviceOperationUpdate([res.data])))
|
|
.catch((err) => {
|
|
console.warn(`Smartplug reset error`, err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
/**
|
|
* Deletes a room
|
|
* @param {Number} roomId the id of the room to delete
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
deleteRoom: (roomId) => {
|
|
return (dispatch) => {
|
|
return Endpoint.delete(`/room/${roomId}`)
|
|
.then((_) => dispatch(actions.roomDelete(roomId)))
|
|
.catch((err) => {
|
|
console.warn("Room deletion error", err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
deleteAutomation: (id) => {
|
|
console.log("ID OF AUTO ", id);
|
|
return (dispatch) => {
|
|
return Endpoint.delete(`/automation/${id}`)
|
|
.then((_) => dispatch(actions.automationDelete(id)))
|
|
.catch((err) => {
|
|
console.warn("Automation deletion error", err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Deletes a scene
|
|
* @param {Number} sceneId the id of the scene to delete
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
deleteScene: (sceneId) => {
|
|
return (dispatch) => {
|
|
return Endpoint.delete(`/scene/${sceneId}`)
|
|
.then((_) => dispatch(actions.sceneDelete(sceneId)))
|
|
.catch((err) => {
|
|
console.warn("Scene deletion error", err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Deletes a device
|
|
* @param {Device} device the device to delete
|
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
|
* with user-fiendly errors as a RemoteError
|
|
*/
|
|
deleteDevice: (device) => {
|
|
return (dispatch) => {
|
|
return Endpoint.delete(`/${device.kind}/${device.id}`)
|
|
.then((_) => dispatch(actions.deviceDelete(device.id)))
|
|
.catch((err) => {
|
|
console.warn("Device deletion error", err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
};
|
|
},
|
|
};
|
|
|
|
for (const key in RemoteService) {
|
|
RemoteService[key] = RemoteService[key].bind(RemoteService);
|
|
}
|
|
|
|
export class Forms {
|
|
/**
|
|
* Attempts to create a new user from the given data.
|
|
* This method does not update the global state,
|
|
* please check its return value.
|
|
* @param {String} data.username the chosen username
|
|
* @param {String} data.password the chosen password
|
|
* @param {String} data.email the chosen email
|
|
* @param {String} data.name the chosen full name
|
|
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
|
* with validation errors as a String array
|
|
*/
|
|
static submitRegistration(data) {
|
|
return Endpoint.postNA(
|
|
"/register",
|
|
{},
|
|
{
|
|
username: data.username,
|
|
password: data.password,
|
|
name: data.name,
|
|
email: data.email,
|
|
}
|
|
)
|
|
.then((_) => void 0)
|
|
.catch(parseValidationErrors);
|
|
}
|
|
|
|
/**
|
|
* Sends a request to perform a password reset.
|
|
* This method does not update the global state,
|
|
* please check its return value.
|
|
* @param {String} email the email to which perform the reset
|
|
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
|
* with validation errors as a String array
|
|
*/
|
|
static submitInitResetPassword(email) {
|
|
return Endpoint.postNA(
|
|
"/register/init-reset-password",
|
|
{},
|
|
{
|
|
email: email,
|
|
}
|
|
)
|
|
.then((_) => void 0)
|
|
.catch((err) => {
|
|
console.warn("Init reset password failed", err);
|
|
throw new RemoteError(["Network error"]);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Sends the password for the actual password reset, haviug already
|
|
* performed email verification
|
|
* This method does not update the global state,
|
|
* please check its return value.
|
|
* @param {String} confirmationToken the confirmation token got from the email
|
|
* @param {String} password the new password
|
|
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
|
* with validation errors as a String array
|
|
*/
|
|
static submitResetPassword(confirmationToken, password) {
|
|
return Endpoint.putNA(
|
|
"/register/reset-password",
|
|
{},
|
|
{
|
|
confirmationToken,
|
|
password,
|
|
}
|
|
)
|
|
.then((_) => void 0)
|
|
.catch(parseValidationErrors);
|
|
}
|
|
}
|