Merge branch 'dashboard-feature' into 'dev'

Almost all the devices in the dashboard are implemented, along with the...

See merge request sa4-2020/the-sanmarinoes/frontend!26
This commit is contained in:
Claudio Maggioni 2020-03-15 16:44:21 +01:00
commit b2503156a9
17 changed files with 690 additions and 254 deletions

View file

@ -0,0 +1 @@
<svg id="Layer_1" enable-background="new 0 0 511.929 511.929" height="512" viewBox="0 0 511.929 511.929" width="512" xmlns="http://www.w3.org/2000/svg"><g><path d="m176.652 174.329v57.6h32v-57.6c0-8.84-7.16-16-16-16-8.83 0-16 7.16-16 16z"/><path d="m303.592 174.329v57.6h32v-57.6c0-8.84-7.17-16-16-16-8.84 0-16 7.16-16 16z"/><path d="m344.052 263.929h-175.86c-8.84 0-16 7.16-16 16 0 8.83 7.16 16 16 16h4.2v41.6c0 21.17 17.22 38.4 38.4 38.4h90.66c21.18 0 38.4-17.23 38.4-38.4v-41.6h4.2c8.84 0 16-7.17 16-16 0-8.84-7.16-16-16-16z"/><path d="m224.122 407.929v8.53c0 11.843 6.438 22.175 16 27.708v51.762c0 8.84 7.16 16 16 16s16-7.16 16-16v-51.762c9.562-5.534 16-15.866 16-27.708v-8.53c-24.422 0-39.67 0-64 0z"/><path d="m193.731 89.704c-6.249 6.248-6.249 16.379 0 22.627 6.248 6.248 16.379 6.249 22.627 0 21.886-21.885 57.309-21.887 79.196 0 3.124 3.124 7.219 4.686 11.313 4.686 14.126 0 21.422-17.206 11.313-27.314-34.391-34.39-90.057-34.394-124.449.001z"/><path d="m363.436 44.448c-59.263-59.264-155.695-59.266-214.96 0-6.249 6.249-6.249 16.379 0 22.627 6.248 6.249 16.379 6.249 22.627 0 46.897-46.896 122.804-46.901 169.706 0 3.124 3.125 7.219 4.687 11.313 4.687 14.126 0 21.422-17.206 11.314-27.314z"/><circle cx="255.955" cy="151.929" r="24"/></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1 @@
<svg id="Layer_1" enable-background="new 0 0 511.929 511.929" height="512" viewBox="0 0 511.929 511.929" width="512" xmlns="http://www.w3.org/2000/svg"><path d="m343.964 263.929v56c0 30.93-25.07 56-56 56h-64c-30.924 0-56-25.071-56-56v-56z" fill="#a2d0ff"/><path d="m343.964 263.929v32h-82.04c-38.53 0-69.96 31.216-69.96 69.96-14.51-10.12-24-26.93-24-45.96v-56z" fill="#8bc4ff"/><path d="m255.965 439.929c-17.624 0-32-14.278-32-32v-32h64v32c-.001 17.673-14.327 32-32 32z" fill="#8bc4ff"/><g fill="#2e58ff"><path d="m367.964 247.929c-13.903 0-26.139 0-40 0v-64c0-8.836-7.163-16-16-16s-16 7.164-16 16v64h-80v-64c0-8.836-7.163-16-16-16s-16 7.164-16 16v64c-13.873 0-26.083 0-40 0-8.837 0-16 7.164-16 16s7.163 16 16 16h8v48c0 32.579 24.475 59.531 56 63.482v16.518c0 20.858 13.377 38.643 32 45.248v42.752c0 8.836 7.163 16 16 16s16-7.164 16-16v-42.752c18.623-6.605 32-24.389 32-45.248v-16.518c31.525-3.951 56-30.904 56-63.482v-48h8c8.837 0 16-7.164 16-16s-7.163-16-16-16zm-96 160c0 8.822-7.178 16-16 16s-16-7.178-16-16v-16h32zm56-80c0 17.645-14.355 32-32 32-16.043 0-63.799 0-80 0-17.645 0-32-14.355-32-32v-48h144z"/><circle cx="255.795" cy="151.929" r="24"/><path d="m306.71 117.017c14.126 0 21.422-17.206 11.313-27.314-34.392-34.391-90.058-34.394-124.45 0-6.249 6.248-6.249 16.379 0 22.627 6.248 6.248 16.379 6.249 22.627 0 21.886-21.885 57.309-21.887 79.196 0 3.124 3.125 7.219 4.687 11.314 4.687z"/><path d="m170.945 67.076c46.897-46.896 122.804-46.901 169.706 0 6.247 6.248 16.379 6.249 22.627 0 6.249-6.248 6.249-16.379 0-22.627-59.263-59.264-155.695-59.266-214.96 0-6.249 6.249-6.249 16.379 0 22.627 6.248 6.249 16.379 6.249 22.627 0z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -1,85 +1,134 @@
import React, {Component} from 'react';
import {
Grid,
Grid,
} from "semantic-ui-react";
import Device from "./devices/Device";
import NewDevice from "./devices/NewDevice";
import {LightDevice, TemperatureSensor} from "./devices/TypesOfDevices";
import {LightDevice, SmartPlugDevice} from "./devices/TypesOfDevices";
import {editButtonStyle, panelStyle} from "./devices/styleComponents";
import {checkMaxLength, DEVICE_NAME_MAX_LENGTH} from "./devices/constants";
import Light from "./devices/Light";
import SmartPlug from "./devices/SmartPlug";
import Sensor from "./devices/Sensor";
const devices = [
{
"id" : 1,
"name": "Bedroom Light",
"type" : "light",
"hasIntensity" : true,
"intensityLevel" : 0.20,
...LightDevice
},
{
"id" : 2,
"name": "Bathroom Light",
"type" : "light",
...LightDevice
},
{
"id" : 3,
"name": "Desktop Light",
"type" : "light",
...LightDevice
},
{
"id" : 4,
"name": "Entrance Light",
"type" : "light",
...LightDevice
},
{
"id" : 5,
"name": "Smart Plug",
"type" : "smartplug",
...SmartPlugDevice
},
{
"id" : 6,
"name": "Bedroom Thermometer",
"type" : "temperature-sensor",
},
];
class Panel extends Component {
constructor(props) {
super(props);
this.state = {
editMode : false
constructor(props) {
super(props);
this.state = {
editMode : false,
devices : devices,
};
}
editModeController = (e) => this.setState((prevState) => ({ editMode: !prevState.editMode }));
changeDeviceData = (deviceId, newSettings) => {
console.log(newSettings.name, " <-- new name --> ", deviceId );
this.state.devices.map(device => {
if(device.id === deviceId){
for(let prop in newSettings){
if(device.hasOwnProperty(prop)){
if(prop==="name"){
if(checkMaxLength(newSettings[prop])){
device[prop] = newSettings[prop];
}
else{
alert("Name must be less than " + DEVICE_NAME_MAX_LENGTH + " characters.");
}
}else{
device[prop] = newSettings[prop];
}
}
}
}
return null;
});
this.forceUpdate();
};
}
editModeController = (e) => {
this.setState((prevState) => ({ editMode: !prevState.editMode }));
};
render() {
const devices = [
{
"name": "Bedroom Light",
...LightDevice
},
{
"name": "Bathroom Light",
...LightDevice
},
{
"name": "Desktop Light",
...LightDevice
},
{
"name": "Entrance Light",
...LightDevice
},
{
"name": "Bedroom",
...TemperatureSensor
}
];
return (
<div style={panelStyle}>
<button style={editButtonStyle} onClick={this.editModeController}>Edit</button>
<Grid doubling columns={5} divided="vertically">
{devices.map((e, i) => {
return (
<Grid.Column key={i.toString()}>
<Device device={e} edit={this.state.editMode}/>
</Grid.Column>
)
})}
<Grid.Column>
<NewDevice/>
</Grid.Column>
</Grid>
</div>
)
}
render() {
return (
<div style={panelStyle}>
<button style={editButtonStyle} onClick={this.editModeController}>Edit</button>
<Grid doubling columns={5} divided="vertically">
<Grid.Column>
<Light onChangeData={this.changeDeviceData} device={devices[0]} edit={this.state.editMode}/>
</Grid.Column>
<Grid.Column>
<SmartPlug onChangeData={this.changeDeviceData} device={devices[4]} edit={this.state.editMode}/>
</Grid.Column>
<Grid.Column>
<Sensor onChangeData={this.changeDeviceData} device={devices[5]} edit={this.state.editMode}/>
</Grid.Column>
<Grid.Column>
<NewDevice/>
</Grid.Column>
</Grid>
</div>
)
}
}
export default class DevicePanel extends Component {
constructor(props) {
super(props);
this.state = {
shownRoom: "All"
constructor(props) {
super(props);
this.state = {
shownRoom: "All"
}
}
}
render() {
return (
<Panel/>
)
}
render() {
return (
<Panel/>
)
}
}

View file

@ -1,87 +1,48 @@
import React, {Component} from 'react';
import styled from 'styled-components';
import {Image} from "semantic-ui-react";
import Sensor from './Sensor';
import {editModeIconStyle, editModeStyle} from "./styleComponents";
import {iconStyle, nameStyle, StyledDiv} from "./styleComponents";
import Settings from './DeviceSettings';
const StyledDiv = styled.div`
background-color : white;
padding : 3rem;
width: 10rem;
height: 10rem;
border-radius : 100%;
border : none;
position : relative;
box-shadow: 3px 2px 10px 5px #ccc;
transition : all .3s ease-out;
:hover{
background-color : #f2f2f2;
}
:active{
transform : translate(0.3px, 0.8px);
box-shadow: 0.5px 0.5px 7px 3.5px #ccc;
}
`;
const iconStyle = {
width: "4rem",
height: "auto",
position: "absolute",
top: "20%",
left: "50%",
transform: "translateX(-50%)",
filter: "drop-shadow( 1px 1px 0.5px rgba(0, 0, 0, .25))"
};
const nameStyle = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translateX(-50%)"
}
export default class Device extends Component {
constructor(props) {
super(props);
this.state = {
turnOnOff: "off",
icon: this.props.device.img
constructor(props) {
super(props);
this.state = {
turnOnOff: "off",
icon: this.props.device.img
}
}
}
onClickDevice = () => {
if (this.props.device.type === "light") {
if (this.state.turnOnOff === "on") {
this.setState({
turnOnOff: "off",
icon: this.props.device.img
});
} else {
this.setState({
turnOnOff: "on",
icon: this.props.device.imgClick
});
}
onClickDevice = () => {
if (!this.props.edit) {
if (this.props.device.type === "light") {
if (this.state.turnOnOff === "on") {
this.setState({
turnOnOff: "off",
icon: this.props.device.img
});
} else {
this.setState({
turnOnOff: "on",
icon: this.props.device.imgClick
});
}
}
}
};
render() {
return (
<StyledDiv onClick={this.props.edit ? () => {
} : this.onClickDevice} style={{textAlign: "center"}}>
<Settings
deviceId={this.props.device.id}
edit={this.props.edit}
onChangeData={(id, newSettings) => this.props.onChangeData(id, newSettings)}/>
<Image src={this.state.icon} style={iconStyle}/>
<h5 style={nameStyle}>{this.props.device.name}</h5>
</StyledDiv>
)
}
};
render() {
if (this.props.device.type === "temperature_sensor") {
return (
<StyledDiv>
{this.props.edit ? (<span style={editModeStyle}><img src="/img/settings.svg" style={editModeIconStyle}/></span>) : ("")}
<Sensor device={this.props.device}/>
</StyledDiv>
)
}
return (
<StyledDiv onClick={this.onClickDevice} style={{textAlign: "center"}}>
{this.props.edit ? (<span style={editModeStyle}><img src="/img/settings.svg" style={editModeIconStyle}/></span>) : ("")}
<Image src={this.state.icon} style={iconStyle}/>
<h5 style={nameStyle}>{this.props.device.name}</h5>
</StyledDiv>
)
}
}

View file

@ -0,0 +1,73 @@
import React, {Component} from "react";
import {Button, Form} from "semantic-ui-react";
import {editModeIconStyle, editModeStyle, formStyle} from "./styleComponents";
class SettingsForm extends Component {
constructor(props) {
super(props);
this.state = {}
};
onChangeHandler = (event) => {
let nam = event.target.name;
let val = event.target.value;
this.setState({[nam]: val});
};
saveChanges = () => {
let newName = this.state["new-name"];
this.props.onChangeData(this.props.id, {"name": newName});
};
render() {
return (
<Form style={formStyle}>
<Form.Field>
<label style={{color: "white"}}>New Device Name</label>
<input name="new-name" placeholder='New name' onChange={this.onChangeHandler}/>
</Form.Field>
<Button type='submit' onClick={this.saveChanges}>Save</Button>
</Form>
)
}
}
export default class Settings extends Component {
constructor(props) {
super(props);
this.state = {
displayForm: false,
}
};
displayForm = () => {
this.setState((prevState) => ({displayForm: !prevState.displayForm}));
};
render() {
const view = (
<div>
{this.state.displayForm ? (
<SettingsForm id={this.props.deviceId} onChangeData={this.props.onChangeData}/>) : ("")}
<div onClick={this.displayForm}>
<span style={editModeStyle}>
{!this.state.displayForm ? (
<img
src="/img/settings.svg"
alt=""
style={editModeIconStyle}/>)
:
<p style={{color: "white"}}>&times;</p>
}
</span>
</div>
</div>
);
return (
<React.Fragment>
{this.props.edit ? view : ("")}
</React.Fragment>
)
};
}

View file

@ -0,0 +1,52 @@
/**
Users can add dimmers, a particular kind of switch that can also modify the intensity level of a given light.
There are two types of dimmers:
A dimmer with state stores a given intensity level and sets the light to that level. <-- StatefulDimmer
A dimmer without state can just increase or decrease the intensity of a light. <-- DefualtDimmer
The user can change the state of a dimmer through an intuitive UI in SmartHut .
**/
import React, {Component} from 'react';
export class StatefulDimmer extends Component{
constructor(props){
super(props);
this.state = {
intensityLevel : 0,
pointingLDevices:[]
}
}
componentDidMount() {
}
render() {
return(
<div>
This is a Dimmer
</div>
)
}
}
export class DefaultDimmer extends Component{
// As far as I am concern, even though this dimmer doesn't have state, internally it's needed
constructor(props){
super(props);
this.state = {
pointingDevices :[]
}
}
componentDidMount() {
}
render() {
return(
<div>
This is a Dimmer
</div>
)
}
}

View file

@ -0,0 +1,92 @@
/**
* Users can add lights in their rooms.
* Lights are devices like bulbs, LED strip lights, lamps.
* Lights may support an intensity level (from 0% to 100%).
* Lights have an internal state that can be changed and it must
* be shown accordingly in the SmartHut views (house view and room views).
*/
import React, {Component} from "react";
import {iconStyle, StyledDiv, nameStyle} from "./styleComponents";
import Settings from "./DeviceSettings";
import {Image} from "semantic-ui-react";
import {CircularInput, CircularProgress, CircularThumb, CircularTrack} from "react-circular-input";
import {valueStyle, intensityLightStyle, style} from "./LightStyle";
export default class Light extends Component {
constructor(props) {
super(props);
this.state = {
turnedOn: false,
hasIntensity : false
};
this.iconOn = "/img/lightOn.svg";
this.iconOff = "/img/lightOff.svg"
}
onClickDevice = () => {
this.setState((prevState) => ({turnedOn: !prevState.turnedOn}));
};
setIntensity = (newValue) => {
this.setState({intensityLevel : newValue});
};
getIcon = () => {
if(this.state.turnedOn){
return this.iconOn;
}
return this.iconOff;
};
componentDidMount() {
if(this.props.device.hasOwnProperty("hasIntensity") && this.props.device.hasOwnProperty("intensityLevel")) {
this.setState({
hasIntensity: this.props.device.hasIntensity,
intensityLevel: this.props.device.intensityLevel
});
}
// Get the state and update it
}
render() {
const intensityLightView = (
<CircularInput
value={this.state.intensityLevel}
onChange={this.setIntensity}
style={style}
>
<CircularTrack/>
<CircularProgress/>
<CircularThumb/>
<text style={valueStyle} x={100} y={100} textAnchor="middle" dy="0.3em" fontWeight="bold">
{Math.round(this.state.intensityLevel*100)}%
</text>
<text style={intensityLightStyle} x={100} y={150} textAnchor="middle" dy="0.3em" fontWeight="bold">
{this.props.device.name}
</text>
</CircularInput>
);
const normalLightView = (
<StyledDiv onClick={this.props.edit ? () => {
} : this.onClickDevice} style={{textAlign: "center"}}>
<Settings
deviceId={this.props.device.id}
edit={this.props.edit}
onChangeData={(id, newSettings) => this.props.onChangeData(id, newSettings)}/>
<Image src={this.getIcon()} style={iconStyle}/>
<h5 style={nameStyle}>{this.props.device.name}</h5>
</StyledDiv>
);
return (
<React.Fragment>
{this.state.hasIntensity ? (intensityLightView) : (normalLightView)}
</React.Fragment>
)
}
}

View file

@ -0,0 +1,14 @@
export const style = {width: "10rem", height: "10rem", position: "absolute", top: "0", left: "0"};
export const valueStyle = {
fill: "#3e99ff",
fontSize: "2.5rem",
fontFamily: "Lato",
textShadow: "1px 1px 0.5px rgba(0, 0, 0, .2)",
};
export const intensityLightStyle = {
fill: "#3e99ff",
fontSize: "1.5rem",
fontFamily: "Lato",
textShadow: "1px 1px 0.5px rgba(0, 0, 0, .2)",
};

View file

@ -1,17 +1,8 @@
import React, {Component} from 'react';
import styled, {keyframes} from 'styled-components';
import {Image} from "semantic-ui-react";
const rotateAddButton = keyframes`
0% {
transform : translate(0px, 0px) rotate(0deg);
box-shadow: 3px 2px 10px 5px #ccc;
}
100% {
transform : translate(0.3px, 0.8px) rotate(90deg);
box-shadow: 0.5px 0.5px 7px 3.5px #ccc;
}
`;
import styled from 'styled-components';
import {Button, Dropdown, Form, Image} from "semantic-ui-react";
import {addDeviceFormStyle} from "./styleComponents";
import {deviceList} from "./TypesOfDevices";
const StyledDiv = styled.div`
background-color : #ff4050;
@ -26,48 +17,92 @@ const StyledDiv = styled.div`
:hover{
background-color : #ff2436;
}
:active{
animation-name: ${rotateAddButton};
animation-duration: 0.5s;
animation-timing-function: ease;
animation-delay: 0s;
animation-direction: normal;
animation-play-state: running;
animation-fill-mode: forwards;
}
`;
const iconStyle = {
width : "4rem",
height : "auto",
position : "absolute",
top : "20%",
left : "50%",
transform : "translateX(-50%)"
class NewDeviceForm extends Component {
constructor(props) {
super(props);
}
formSelector = (option) => {
switch (option) {
case "Light":
return <LightForm/>;
case "Sensor":
return "This is a sensor form";
default:
return "This is a default text"
}
};
render() {
let options = [];
deviceList.forEach((e, i) => {
options.push({key: i, text: e, value: e})
});
return (
<Form style={addDeviceFormStyle}>
<Form.Field>
<label style={{color: "white"}}>Select the type of device</label>
<Dropdown clearable options={options} selection/>
</Form.Field>
<Button type='submit' onClick={this.saveChanges}>Save</Button>
</Form>
);
}
}
class LightForm extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
let options = [
{
key: 1,
value: "common",
text: "Normal Light"
},
{
key: 1,
value: "intensity",
text: "Supports intensity level"
}
]
return (
<Form.Field>
<label style={{color: "white"}}>Type of light</label>
<Dropdown clearable options={options} selection/>
</Form.Field>
);
};
};
const nameStyle = {
position : "absolute",
top : "50%",
left : "50%",
transform : "translateX(-50%)"
}
export default class NewDevice extends Component {
constructor(props) {
super(props);
this.state = {
constructor(props) {
super(props);
this.state = {
openForm: false
}
}
}
onClickDevice = (event) => {
console.log(this.props.children);
};
onClickDevice = (event) => {
this.setState((prevState) => ({openForm: !prevState.openForm}));
};
render() {
return (
<StyledDiv onClick={this.onClickDevice} style={{textAlign: "center"}}>
<Image src="/img/add.svg" style={{filter : "invert()"}}/>
</StyledDiv>
)
}
render() {
return (
<StyledDiv onClick={this.onClickDevice} style={{textAlign: "center"}}>
<Image src="/img/add.svg" style={{filter: "invert()"}}/>
{this.state.openForm ? (
<NewDeviceForm/>
) : ""}
</StyledDiv>
)
}
}

View file

@ -1,67 +1,56 @@
import React, {useState} from "react";
import {
CircularInput,
CircularTrack,
CircularProgress,
CircularThumb,
useCircularInputContext,
} from 'react-circular-input'
/**
* Users can add sensors in their rooms.
* Sensors typically measure physical quantities in a room.
* You must support temperature sensors, humidity sensors, light sensors (which measure luminosity1).
* Sensors have an internal state that cannot be changed by the user.
* For this story, make the sensors return a constant value with some small random error.
*/
import React, {Component} from "react";
import {CircularInput, CircularProgress, CircularTrack} from "react-circular-input";
import {errorStyle, sensorText, style, valueStyle} from "./SensorStyle";
export default class Light extends Component {
constructor(props) {
super(props);
this.state = {
turnedOn: false,
value: 20,
error : 2.4
};
this.units = "ºC"
}
setName = () => {
if(this.props.device.name.length > 15){
return this.props.device.name.slice(0,12) + "..."
}
return this.props.device.name;
};
componentDidMount() {
}
// Example of a custom component to display text on top of the thumb
render() {
return (
<CircularInput
value={this.state.value/100}
style={style}
>
<CircularTrack/>
<CircularProgress/>
function TemperatureDisplay() {
const {getPointFromValue, value} = useCircularInputContext();
const {x, y} = getPointFromValue();
const style = {
fontFamily: "Lato",
fontSize: "1.2rem",
color: "white",
};
return (
<text x={x} y={y} style={style}>
{Math.round(value * 100)}
</text>
)
<text style={valueStyle} x={100} y={80} textAnchor="middle" dy="0.3em" fontWeight="bold">
{Math.round(this.state.value)}{this.units}
</text>
<text style={errorStyle} x={100} y={100} textAnchor="middle" dy="0.6em" fontWeight="bold">
&#177;{this.state.error}
</text>
<text style={sensorText} x={100} y={150} textAnchor="middle" dy="0.3em" fontWeight="bold">
{this.setName()}
</text>
</CircularInput>
)
}
}
export default function Sensor(props) {
const style = {width: "10rem", height: "10rem", position: "absolute", top: "0", left: "0"};
const valueStyle = {
fill: "#3e99ff",
fontSize: "2.5rem",
fontFamily: "Lato",
textShadow: "1px 1px 0.5px rgba(0, 0, 0, .2)",
}
const nameStyle = {
fill: "#3e99ff",
fontSize: "1.5rem",
fontFamily: "Lato",
textShadow: "1px 1px 0.5px rgba(0, 0, 0, .2)",
}
const [value, setValue] = useState(0.25);
return (
<CircularInput
value={value}
onChange={setValue}
style={style}
>
<CircularTrack/>
<CircularProgress/>
<CircularThumb/>
<text style={valueStyle} x={100} y={100} textAnchor="middle" dy="0.3em" fontWeight="bold">
{Math.round(value * props.device.maxValue)}{props.device.units}
</text>
<text style={nameStyle} x={100} y={150} textAnchor="middle" dy="0.3em" fontWeight="bold">
{props.device.name}
</text>
</CircularInput>
)
}

View file

@ -0,0 +1,23 @@
export const style = {width: "10rem", height: "10rem", position: "absolute", top: "0", left: "0"};
export const sensorText = {
fill: "#3e99ff",
fontSize: "1.2rem",
fontFamily: "Lato",
textShadow: "1px 1px 0.5px rgba(0, 0, 0, .2)",
}
export const valueStyle = {
fill: "#3e99ff",
fontSize: "2.5rem",
fontFamily: "Lato",
textShadow: "1px 1px 0.5px rgba(0, 0, 0, .2)",
};
export const errorStyle = {
fill: "#ff4050",
fontSize: "1.5rem",
fontFamily: "Lato",
textShadow: "1px 1px 0.5px rgba(0, 0, 0, .2)",
}

View file

@ -0,0 +1,60 @@
/**
A smart plug is a plug that has a boolean internal state, i.e., that can be turned on or off, either with the
SmartHut interface or by a switch.
The smart plug also stores the total energy consumed while the plug is active, in terms of kilowatt-hours
2
(kWh) . The user can reset this value.
**/
import React, {Component} from 'react';
import {iconStyle, nameStyle, StyledDiv} from "./styleComponents";
import Settings from "./DeviceSettings";
import {Image} from "semantic-ui-react";
import {energyConsumedStyle, imageStyle} from "./SmartPlugStyle";
export default class SmartPlug extends Component {
constructor(props){
super(props);
this.state = {
turnedOn: false,
energyConsumed : 0 // kWh
};
this.iconOn = "/img/smart-plug.svg";
this.iconOff = "/img/smart-plug-off.svg"
}
onClickDevice = () => {
this.setState((prevState) => ({turnedOn: !prevState.turnedOn}));
};
getIcon = () => {
if(this.state.turnedOn){
return this.iconOn;
}
return this.iconOff;
};
resetEnergyConsumedValue = () => {
// In the settings form there must be an option to restore this value
// along with the rename feature.
}
componentDidMount() {
}
render(){
return (
<StyledDiv onClick={this.props.edit ? () => {} : this.onClickDevice} style={{textAlign: "center"}}>
<Settings
deviceId={this.props.device.id}
edit={this.props.edit}
onChangeData={(id, newSettings) => this.props.onChangeData(id, newSettings)}/>
<Image src={this.getIcon()} style={imageStyle}/>
<h4 style={energyConsumedStyle}>{this.state.energyConsumed} KWh</h4>
<h5 style={nameStyle}>{this.props.device.name}</h5>
</StyledDiv>
)
}
}

View file

@ -0,0 +1,19 @@
import {iconStyle} from "./styleComponents";
export const energyConsumedStyle = {
fontSize : "1.3rem",
position: "absolute",
top: "30%",
left: "50%",
transform: "translateX(-50%)"
};
export const imageStyle = {
width: "3.5rem",
height: "auto",
position: "absolute",
top: "10%",
left: "50%",
transform: "translateX(-50%)",
filter: "drop-shadow( 1px 1px 0.5px rgba(0, 0, 0, .25))"
};

View file

@ -1,13 +1,18 @@
export const LightDevice = {
type : "light",
img : "/img/lightOff.svg",
imgClick : "/img/lightOn.svg"
};
export const SmartPlugDevice = {
img : "/img/smart-plug.svg",
imgClick : "/img/smart-plug-off.svg"
};
export const TemperatureSensor = {
type : "temperature_sensor",
img : "",
imgClick : "",
units: "ºC",
maxValue : 30
};
export const deviceList = ["Light", "Dimmer", "Switcher", "Smart Plug", "Sensor"];

View file

@ -0,0 +1,5 @@
export const DEVICE_NAME_MAX_LENGTH = 15;
export function checkMaxLength(name){
return !(name.length > DEVICE_NAME_MAX_LENGTH);
}

View file

@ -1,3 +1,5 @@
import styled from "styled-components";
export const editButtonStyle = {
position : "absolute",
top: "0",
@ -12,6 +14,7 @@ export const editButtonStyle = {
};
export const panelStyle = {
position : "relative",
backgroundColor: "#fafafa",
height: "100%",
width: "auto",
@ -38,4 +41,58 @@ export const editModeIconStyle = {
height : "0.75rem",
borderRadius : "20%",
zIndex : "101"
}
};
export const iconStyle = {
width: "4rem",
height: "auto",
position: "absolute",
top: "20%",
left: "50%",
transform: "translateX(-50%)",
filter: "drop-shadow( 1px 1px 0.5px rgba(0, 0, 0, .25))"
};
export const nameStyle = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translateX(-50%)"
};
export const formStyle = {
position : "absolute",
zIndex: "1000",
width : "80rem",
height : "10rem",
padding : "1rem",
margin : "1rem",
borderRadius : "10%",
boxShadow : "1px 1px 5px 2px #5d5d5d",
backgroundColor: "#3e99ff",
};
export const addDeviceFormStyle = {
maxWidth : "400px",
background : "#3e99ff",
paddingRight : "5rem",
};
export const StyledDiv = styled.div`
background-color : white;
padding : 3rem;
width: 10rem;
height: 10rem;
border-radius : 100%;
border : none;
position : relative;
box-shadow: 3px 2px 10px 5px #ccc;
transition : all .3s ease-out;
:hover{
background-color : #f2f2f2;
}
:active{
transform : translate(0.3px, 0.8px);
box-shadow: 0.5px 0.5px 7px 3.5px #ccc;
}
`;