Merge branch 'dev' into '106-rangetrigger-values-must-be-validated-to-a-sensible-range-for-the-respective-device'
# Conflicts: # smart-hut/src/store.js
This commit is contained in:
commit
3e53f11da0
18 changed files with 301 additions and 11544 deletions
|
@ -3,6 +3,7 @@ image: node:latest
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
- test
|
- test
|
||||||
|
- code_quality
|
||||||
- deploy
|
- deploy
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
|
@ -24,6 +25,14 @@ testing_testing:
|
||||||
- cd smart-hut
|
- cd smart-hut
|
||||||
- yarn test
|
- yarn test
|
||||||
|
|
||||||
|
sonar-scanner:
|
||||||
|
stage: code_quality
|
||||||
|
only:
|
||||||
|
- dev
|
||||||
|
script:
|
||||||
|
- cd smart-hut
|
||||||
|
- yarn sonar-scanner -Dsonar.host.url=$SONAR_URL -Dsonar.login=$SONAR_LOGIN -Dsonar.projectName=$CI_PROJECT_PATH_SLUG -Dsonar.projectKey=$CI_PROJECT_PATH_SLUG -Dsonar.exclusion=cypress -Dsonar.sources=src -Dsonar.javascript.file.suffixes=.js,.jsx -Dsonar.sourceEncoding=UTF-8 -Dsonar.scm.disabled=True
|
||||||
|
|
||||||
smartHut_deploy:
|
smartHut_deploy:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
tags:
|
tags:
|
||||||
|
|
66
smart-hut/package-lock.json
generated
66
smart-hut/package-lock.json
generated
|
@ -1075,6 +1075,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
|
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
|
||||||
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
|
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
|
||||||
},
|
},
|
||||||
|
"@giantmachines/redux-websocket": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@giantmachines/redux-websocket/-/redux-websocket-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-87GXE32CnsA9/AA7jVDQupWLmycHAuoWPwNCTz2YnlRR1l8EqLlOJK6SyeMQaVRIuAvM5B7e06dylmaS9Jlgnw==",
|
||||||
|
"requires": {
|
||||||
|
"redux": "~4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@hapi/address": {
|
"@hapi/address": {
|
||||||
"version": "2.1.4",
|
"version": "2.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
|
||||||
|
@ -6356,6 +6364,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz",
|
||||||
"integrity": "sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg=="
|
"integrity": "sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg=="
|
||||||
},
|
},
|
||||||
|
"immutability-helper": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-fcrJ26wpvUcuGRpoGY4hyQ/JOeR1HAunMmE3C0XYXSe6plAGtgTlB2S4BzueBANCPrDJ7AByL1yrIRLIlVfwpA==",
|
||||||
|
"requires": {
|
||||||
|
"invariant": "^2.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"import-cwd": {
|
"import-cwd": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
|
||||||
|
@ -10753,6 +10769,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-circular-slider-svg/-/react-circular-slider-svg-0.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/react-circular-slider-svg/-/react-circular-slider-svg-0.1.5.tgz",
|
||||||
"integrity": "sha512-ZXSlwKHExjb950+84gydH5wxX1CnND1v4w1DywOHcOgzD48Wmjr+nME4JeOumNJ9QOXhm1HROhSEDMlTFIAGqw=="
|
"integrity": "sha512-ZXSlwKHExjb950+84gydH5wxX1CnND1v4w1DywOHcOgzD48Wmjr+nME4JeOumNJ9QOXhm1HROhSEDMlTFIAGqw=="
|
||||||
},
|
},
|
||||||
|
"react-confirm-alert": {
|
||||||
|
"version": "2.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-confirm-alert/-/react-confirm-alert-2.6.1.tgz",
|
||||||
|
"integrity": "sha512-KxlpQoR4x/ET1oFPm/IGpsqnpzP17qkkQZuaO3pw7zGZ9oP5hElPtq/1vgoikoqNHQ2tMm6Iw9HQUNLoNgXkRA=="
|
||||||
|
},
|
||||||
"react-dev-utils": {
|
"react-dev-utils": {
|
||||||
"version": "10.2.0",
|
"version": "10.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.0.tgz",
|
||||||
|
@ -10968,6 +10989,27 @@
|
||||||
"warning": "^4.0.2"
|
"warning": "^4.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-rangeslider": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-rangeslider/-/react-rangeslider-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-5K7Woa+cyqZ5wiW5+KhqGV+3+FiFxGKQ9rUxTMh52sObXVYEeBbfxFrp1eBvS8mRIxnUbHz9ppnFP0LhwOyNeg==",
|
||||||
|
"requires": {
|
||||||
|
"classnames": "^2.2.3",
|
||||||
|
"resize-observer-polyfill": "^1.4.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"react-redux": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.5.5",
|
||||||
|
"hoist-non-react-statics": "^3.3.0",
|
||||||
|
"loose-envify": "^1.4.0",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react-is": "^16.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-round-slider": {
|
"react-round-slider": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-round-slider/-/react-round-slider-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-round-slider/-/react-round-slider-1.0.1.tgz",
|
||||||
|
@ -11163,6 +11205,20 @@
|
||||||
"strip-indent": "^3.0.0"
|
"strip-indent": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"redux": {
|
||||||
|
"version": "4.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz",
|
||||||
|
"integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==",
|
||||||
|
"requires": {
|
||||||
|
"loose-envify": "^1.4.0",
|
||||||
|
"symbol-observable": "^1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redux-thunk": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw=="
|
||||||
|
},
|
||||||
"regenerate": {
|
"regenerate": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
|
||||||
|
@ -11382,6 +11438,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
|
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
|
||||||
},
|
},
|
||||||
|
"resize-observer-polyfill": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
|
||||||
|
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
|
||||||
|
},
|
||||||
"resolve": {
|
"resolve": {
|
||||||
"version": "1.15.0",
|
"version": "1.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz",
|
||||||
|
@ -12625,6 +12686,11 @@
|
||||||
"util.promisify": "~1.0.0"
|
"util.promisify": "~1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"symbol-observable": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
|
||||||
|
},
|
||||||
"symbol-tree": {
|
"symbol-tree": {
|
||||||
"version": "3.2.4",
|
"version": "3.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"react-axios": "^2.0.3",
|
"react-axios": "^2.0.3",
|
||||||
"react-circular-input": "^0.1.6",
|
"react-circular-input": "^0.1.6",
|
||||||
"react-circular-slider-svg": "^0.1.5",
|
"react-circular-slider-svg": "^0.1.5",
|
||||||
|
"react-confirm-alert": "^2.6.1",
|
||||||
"react-device-detect": "^1.11.14",
|
"react-device-detect": "^1.11.14",
|
||||||
"react-dom": "^16.12.0",
|
"react-dom": "^16.12.0",
|
||||||
"react-modal": "^2.2.2",
|
"react-modal": "^2.2.2",
|
||||||
|
@ -53,6 +54,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "2.0.1"
|
"prettier": "2.0.1",
|
||||||
|
"sonarqube-scanner": "^2.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
smart-hut/public/img/curtains-icon.png
Normal file
BIN
smart-hut/public/img/curtains-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
BIN
smart-hut/public/img/thermostat-icon.png
Normal file
BIN
smart-hut/public/img/thermostat-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
|
@ -8,6 +8,7 @@ import {
|
||||||
Icon,
|
Icon,
|
||||||
Responsive,
|
Responsive,
|
||||||
Image,
|
Image,
|
||||||
|
Confirm,
|
||||||
} from "semantic-ui-react";
|
} from "semantic-ui-react";
|
||||||
import SelectIcons from "./SelectIcons";
|
import SelectIcons from "./SelectIcons";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
@ -34,6 +35,7 @@ class RoomModal extends Component {
|
||||||
name: this.type === "new" ? "New Room" : this.props.room.name,
|
name: this.type === "new" ? "New Room" : this.props.room.name,
|
||||||
img: this.type === "new" ? null : this.props.room.image,
|
img: this.type === "new" ? null : this.props.room.image,
|
||||||
openModal: false,
|
openModal: false,
|
||||||
|
sure: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +93,14 @@ class RoomModal extends Component {
|
||||||
.catch((err) => console.error("error in deleting room", err));
|
.catch((err) => console.error("error in deleting room", err));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setSureTrue = () => {
|
||||||
|
this.setState({ sure: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
setSureFalse = () => {
|
||||||
|
this.setState({ sure: false });
|
||||||
|
};
|
||||||
|
|
||||||
changeSomething = (event) => {
|
changeSomething = (event) => {
|
||||||
let nam = event.target.name;
|
let nam = event.target.name;
|
||||||
let val = event.target.value;
|
let val = event.target.value;
|
||||||
|
@ -220,16 +230,23 @@ class RoomModal extends Component {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{this.type === "modify" ? (
|
{this.type === "modify" ? (
|
||||||
<Button
|
<div>
|
||||||
icon
|
<Button
|
||||||
labelPosition="left"
|
icon
|
||||||
inverted
|
labelPosition="left"
|
||||||
color="red"
|
inverted
|
||||||
onClick={this.deleteRoom}
|
color="red"
|
||||||
>
|
onClick={this.setSureTrue}
|
||||||
<Icon name="trash alternate" />
|
>
|
||||||
Delete room
|
<Icon name="trash alternate" />
|
||||||
</Button>
|
Delete Room{" "}
|
||||||
|
</Button>
|
||||||
|
<Confirm
|
||||||
|
open={this.state.sure}
|
||||||
|
onCancel={this.setSureFalse}
|
||||||
|
onConfirm={this.deleteRoom}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</Modal.Content>
|
</Modal.Content>
|
||||||
<Modal.Actions>
|
<Modal.Actions>
|
||||||
|
|
|
@ -25,7 +25,7 @@ class DevicePanel extends Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Card.Group centered style={{ paddingTop: "3rem" }}>
|
<Card.Group centered style={{ paddingTop: "3rem" }}>
|
||||||
{this.props.devices.length !== 0 ? (
|
{this.props.numbeOfRooms > 0 ? (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{this.props.devices.map((e, i) => {
|
{this.props.devices.map((e, i) => {
|
||||||
return <Device key={i} tab={this.props.tab} id={e.id} />;
|
return <Device key={i} tab={this.props.tab} id={e.id} />;
|
||||||
|
@ -70,6 +70,9 @@ const mapStateToProps = (state, _) => ({
|
||||||
return state.active.activeRoom === -1;
|
return state.active.activeRoom === -1;
|
||||||
},
|
},
|
||||||
activeRoom: state.active.activeRoom,
|
activeRoom: state.active.activeRoom,
|
||||||
|
get numbeOfRooms() {
|
||||||
|
return Object.keys(state.rooms).length;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const DevicePanelContainer = connect(
|
const DevicePanelContainer = connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
|
|
|
@ -16,6 +16,13 @@ class HostsPanel extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyHostScene(id) {
|
||||||
|
this.props
|
||||||
|
.sceneApply(id, this.props.activeHost)
|
||||||
|
.then(() => console.log("SCCUESS"))
|
||||||
|
.catch((err) => console.error("sceneApply update error", err));
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.props.isActiveDefaultHost) {
|
if (this.props.isActiveDefaultHost) {
|
||||||
return (
|
return (
|
||||||
|
@ -46,9 +53,11 @@ class HostsPanel extends Component {
|
||||||
{scene.name} <Icon name={scene.icon} />
|
{scene.name} <Icon name={scene.icon} />
|
||||||
</Header>
|
</Header>
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Content extras>
|
<Card.Content extras={true}>
|
||||||
<div className="ui two buttons">
|
<div className="ui two buttons">
|
||||||
<Button>Apply</Button>
|
<Button onClick={() => this.applyHostScene(scene.id)}>
|
||||||
|
Apply
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
@ -141,10 +141,14 @@ class NewDevice extends Component {
|
||||||
case "buttonDimmer":
|
case "buttonDimmer":
|
||||||
case "knobDimmer":
|
case "knobDimmer":
|
||||||
outputs = this.state.lightsAttached;
|
outputs = this.state.lightsAttached;
|
||||||
if (this.state.lightsAttached === undefined ||
|
if (
|
||||||
this.state.lightsAttached.length === 0) {
|
this.state.lightsAttached === undefined ||
|
||||||
alert("No lights attached to this switch! Please, add a light a first.");
|
this.state.lightsAttached.length === 0
|
||||||
return;
|
) {
|
||||||
|
alert(
|
||||||
|
"No lights attached to this switch! Please, add a light a first."
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -165,8 +169,18 @@ class NewDevice extends Component {
|
||||||
render() {
|
render() {
|
||||||
const deviceOptions = [
|
const deviceOptions = [
|
||||||
//stuff
|
//stuff
|
||||||
{ key: "thermostat", text: "Thermostat", value: "thermostat", image: {} },
|
{
|
||||||
{ key: "curtains", text: "Curtain", value: "curtains", image: {} },
|
key: "thermostat",
|
||||||
|
text: "Thermostat",
|
||||||
|
value: "thermostat",
|
||||||
|
image: { avatar: true, src: "/img/thermostat-icon.png" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "curtains",
|
||||||
|
text: "Curtain",
|
||||||
|
value: "curtains",
|
||||||
|
image: { avatar: true, src: "/img/curtains-icon.png" },
|
||||||
|
},
|
||||||
//stuff ends
|
//stuff ends
|
||||||
{
|
{
|
||||||
key: "light",
|
key: "light",
|
||||||
|
|
|
@ -123,6 +123,18 @@ class Sensor extends Component {
|
||||||
return this.iconOff;
|
return this.iconOff;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
temperatureColor = (value) => {
|
||||||
|
let hue = 100;
|
||||||
|
const min = 16;
|
||||||
|
const max = 20;
|
||||||
|
if (value >= min && value < max) {
|
||||||
|
hue = 100 - ((value - min) * 100) / (max - min);
|
||||||
|
} else if (value >= max) {
|
||||||
|
hue = 0;
|
||||||
|
}
|
||||||
|
return `hsl(${hue}, 100%, 50%)`;
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const MotionSensor = (props) => {
|
const MotionSensor = (props) => {
|
||||||
return (
|
return (
|
||||||
|
@ -161,11 +173,18 @@ class Sensor extends Component {
|
||||||
>
|
>
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
strokeWidth="2rem"
|
strokeWidth="2rem"
|
||||||
stroke={this.colors.progress}
|
stroke={
|
||||||
|
this.props.stateOrDevice.sensor === "TEMPERATURE"
|
||||||
|
? this.temperatureColor(this.state.value)
|
||||||
|
: this.colors.progress
|
||||||
|
}
|
||||||
fill={this.colors.circle}
|
fill={this.colors.circle}
|
||||||
/>
|
/>
|
||||||
<text
|
<text
|
||||||
style={{ ...valueStyle, fill: this.colors.text }}
|
style={{
|
||||||
|
...valueStyle,
|
||||||
|
fill: this.colors.text,
|
||||||
|
}}
|
||||||
x={100}
|
x={100}
|
||||||
y={110}
|
y={110}
|
||||||
textAnchor="middle"
|
textAnchor="middle"
|
||||||
|
@ -177,7 +196,10 @@ class Sensor extends Component {
|
||||||
{this.units}
|
{this.units}
|
||||||
</text>
|
</text>
|
||||||
<text
|
<text
|
||||||
style={{ ...sensorText, fill: this.colors.text }}
|
style={{
|
||||||
|
...sensorText,
|
||||||
|
fill: this.colors.text,
|
||||||
|
}}
|
||||||
x={100}
|
x={100}
|
||||||
y={150}
|
y={150}
|
||||||
textAnchor="middle"
|
textAnchor="middle"
|
||||||
|
|
|
@ -70,12 +70,17 @@ class Videocam extends Component {
|
||||||
/>
|
/>
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
</Grid.Row>
|
</Grid.Row>
|
||||||
|
<Grid.Row>
|
||||||
|
<Grid.Column>
|
||||||
|
<Checkbox
|
||||||
|
checked={this.props.stateOrDevice.on}
|
||||||
|
toggle
|
||||||
|
label="Turn on/off"
|
||||||
|
onChange={(e, val) => this.setOnOff(val.checked)}
|
||||||
|
/>
|
||||||
|
</Grid.Column>
|
||||||
|
</Grid.Row>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Checkbox
|
|
||||||
checked={this.props.stateOrDevice.on}
|
|
||||||
toggle
|
|
||||||
onChange={(e, val) => this.setOnOff(val.checked)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -549,12 +549,21 @@ export const RemoteService = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
sceneApply: (id) => {
|
sceneApply: (id, hostId = null) => {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
let url = `/scene/${id}/apply`;
|
let url = `/scene/${id}/apply`;
|
||||||
|
if (hostId) {
|
||||||
|
url = url + "?hostId=" + hostId;
|
||||||
|
}
|
||||||
|
|
||||||
return Endpoint.post(url)
|
return Endpoint.post(url)
|
||||||
.then((res) => dispatch(actions.deviceOperationUpdate(res.data)))
|
.then((res) =>
|
||||||
|
dispatch(
|
||||||
|
hostId
|
||||||
|
? actions.hostDevicesUpdate(hostId, res.data, true)
|
||||||
|
: actions.deviceOperationUpdate(res.data)
|
||||||
|
)
|
||||||
|
)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.warn("scene apply error", err);
|
console.warn("scene apply error", err);
|
||||||
throw new RemoteError(["Network error"]);
|
throw new RemoteError(["Network error"]);
|
||||||
|
|
|
@ -5,7 +5,81 @@ import reduxWebSocket, { connect } from "@giantmachines/redux-websocket";
|
||||||
import { socketURL } from "./endpoint";
|
import { socketURL } from "./endpoint";
|
||||||
|
|
||||||
function reducer(previousState, action) {
|
function reducer(previousState, action) {
|
||||||
let newState, change;
|
let newState, change;
|
||||||
|
|
||||||
|
const createOrUpdateRoom = (room) => {
|
||||||
|
if (!newState.rooms[room.id]) {
|
||||||
|
newState = update(newState, {
|
||||||
|
rooms: { [room.id]: { $set: { ...room, devices: new Set() } } },
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
newState = update(newState, {
|
||||||
|
rooms: {
|
||||||
|
[room.id]: {
|
||||||
|
name: { $set: room.name },
|
||||||
|
image: { $set: room.image },
|
||||||
|
icon: { $set: room.icon },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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() } } },
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
newState = update(newState, {
|
||||||
|
scenes: {
|
||||||
|
[scene.id]: {
|
||||||
|
name: { $set: scene.name },
|
||||||
|
icon: { $set: scene.icon },
|
||||||
|
guestAccessEnabled: { $set: scene.guestAccessEnabled },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newState.pendingJoins.scenes[scene.id]) {
|
||||||
|
newState = update(newState, {
|
||||||
|
pendingJoins: { scenes: { $unset: [scene.id] } },
|
||||||
|
scenes: {
|
||||||
|
[scene.id]: {
|
||||||
|
sceneStates: {
|
||||||
|
$add: [...newState.pendingJoins.scenes[scene.id]],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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] };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const createOrUpdateRoom = (room) => {
|
const createOrUpdateRoom = (room) => {
|
||||||
if (!newState.rooms[room.id]) {
|
if (!newState.rooms[room.id]) {
|
||||||
|
|
|
@ -63,9 +63,10 @@ const actions = {
|
||||||
devices,
|
devices,
|
||||||
partial,
|
partial,
|
||||||
}),
|
}),
|
||||||
hostDevicesUpdate: (hostId, devices) => ({
|
hostDevicesUpdate: (hostId, devices, partial = false) => ({
|
||||||
type: "HOST_DEVICES_UPDATE",
|
type: "HOST_DEVICES_UPDATE",
|
||||||
hostId,
|
hostId,
|
||||||
|
partial,
|
||||||
devices,
|
devices,
|
||||||
}),
|
}),
|
||||||
stateDelete: (stateId) => ({
|
stateDelete: (stateId) => ({
|
||||||
|
|
|
@ -8,10 +8,7 @@ import ScenesNavbar from "./ScenesNavbar";
|
||||||
import HostsNavbar from "./HostsNavbar";
|
import HostsNavbar from "./HostsNavbar";
|
||||||
import MyHeader from "../components/HeaderController";
|
import MyHeader from "../components/HeaderController";
|
||||||
import { Grid, Responsive, Button, Menu } from "semantic-ui-react";
|
import { Grid, Responsive, Button, Menu } from "semantic-ui-react";
|
||||||
import {
|
import { mobilePanelStyle } from "../components/dashboard/devices/styleComponents";
|
||||||
panelStyle,
|
|
||||||
mobilePanelStyle,
|
|
||||||
} from "../components/dashboard/devices/styleComponents";
|
|
||||||
|
|
||||||
import { RemoteService } from "../remote";
|
import { RemoteService } from "../remote";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
@ -81,6 +78,15 @@ class Dashboard extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
// needed to correctly assign the background image
|
||||||
|
//in case a room has one.
|
||||||
|
let backgroundImageHelper;
|
||||||
|
if (this.activeTab === "Devices") {
|
||||||
|
backgroundImageHelper = this.props.backgroundImage;
|
||||||
|
} else {
|
||||||
|
backgroundImageHelper = null;
|
||||||
|
}
|
||||||
|
//console.log("helper is",helper)
|
||||||
return (
|
return (
|
||||||
<div style={{ background: "#1b1c1d" }}>
|
<div style={{ background: "#1b1c1d" }}>
|
||||||
<Responsive minWidth={768}>
|
<Responsive minWidth={768}>
|
||||||
|
@ -126,7 +132,19 @@ class Dashboard extends Component {
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
)}
|
)}
|
||||||
<Grid.Column width={this.hasNavbar ? 13 : 16}>
|
<Grid.Column width={this.hasNavbar ? 13 : 16}>
|
||||||
<div style={panelStyle}>{this.renderTab(this.activeTab)}</div>
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundImage: "url(" + backgroundImageHelper + ")",
|
||||||
|
backgroundColor: "#fafafa",
|
||||||
|
height: "85vh",
|
||||||
|
padding: "0rem 3rem",
|
||||||
|
color: "#000000",
|
||||||
|
overflow: "auto",
|
||||||
|
maxHeight: "75vh",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{this.renderTab(this.activeTab)}
|
||||||
|
</div>
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
</Grid.Row>
|
</Grid.Row>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -199,6 +217,16 @@ class Dashboard extends Component {
|
||||||
|
|
||||||
const mapStateToProps = (state, _) => ({
|
const mapStateToProps = (state, _) => ({
|
||||||
activeTab: state.active.activeTab,
|
activeTab: state.active.activeTab,
|
||||||
|
get currentRoom() {
|
||||||
|
return state.active.activeRoom;
|
||||||
|
},
|
||||||
|
get backgroundImage() {
|
||||||
|
if (state.active.activeRoom === -1) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return state.rooms[state.active.activeRoom].image;
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const setActiveTab = (activeTab) => {
|
const setActiveTab = (activeTab) => {
|
||||||
|
|
|
@ -247,7 +247,7 @@ const Home = () => (
|
||||||
</a>
|
</a>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<a href="https://theshell.ch" target="blank">
|
<a href="https://www.theshell.ch" target="blank">
|
||||||
The Shell
|
The Shell
|
||||||
</a>
|
</a>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
|
|
11506
smart-hut/yarn.lock
11506
smart-hut/yarn.lock
File diff suppressed because it is too large
Load diff
4
yarn.lock
Normal file
4
yarn.lock
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue