diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 11e33690c8..a0cdec9ce7 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -21,7 +21,7 @@ We use github to host code, to track issues and feature requests, as well as acc
## Before making a Pull Request
-Make sure you discussed with the team on [Gladys Community](https://community.gladysassistant.com/) to ensure your pull request goes in the same direction as current developements 🙂
+Make sure you discussed with the team on our Gladys [french forum](https://community.gladysassistant.com/) or [english forum](https://en-community.gladysassistant.com/) to ensure your pull request goes in the same direction as current developements 🙂
Then, create a GitHub Issue to indicate that you're working on the topic.
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index e9091573ba..00ad834d7f 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -5,7 +5,7 @@ about: Report a bug to help us improve Gladys Assistant
First, are you sure that you found a Gladys bug?
-If you are not sure, you can come discuss with us on [Gladys Community](http://community.gladysassistant.com/) 🙂
+If you are not sure, you can come discuss with us on our community, in [french](http://community.gladysassistant.com/) or [english](https://en-community.gladysassistant.com/) 🙂
**Describe the bug**
A clear and concise description of what the bug is.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index aa8415c15d..94244216d5 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,8 +1,14 @@
blank_issues_enabled: true
contact_links:
- - name: Gladys Assistant Forum
+ - name: Gladys Assistant english forum
+ url: https://en-community.gladysassistant.com/
+ about: We use this forum for questions & support in english.
+ - name: Gladys Assistant french Forum
url: https://community.gladysassistant.com/
- about: We use the forum for questions & support.
- - name: Feature Requests
+ about: We use the forum for questions & support in french.
+ - name: Feature Requests (english)
+ url: https://en-community.gladysassistant.com/c/feature-requests/7/l/latest?order=votes
+ about: We use our forum as feature requests. You can create feature requests there, and vote for them.
+ - name: Feature Requests (french)
url: https://community.gladysassistant.com/c/international/feature-requests/53
about: We use our forum as feature requests. You can create feature requests there, and vote for them.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 6eb096bb57..1b557df5d7 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -7,7 +7,7 @@ To ensure your Pull Request can be accepted as fast as possible, make sure to re
- [ ] Is the linter passing? (`npm run eslint` on both front/server)
- [ ] Did you run prettier? (`npm run prettier` on both front/server)
- [ ] If you are adding a new features/services, did you run integration comparator? (`npm run compare-translations` on front)
-- [ ] Did you test this pull request in real life? With real devices? If this development is a big feature or a new service, we recommend that you provide a Docker image to [the community](https://community.gladysassistant.com/) for testing before merging.
+- [ ] Did you test this pull request in real life? With real devices? If this development is a big feature or a new service, we recommend that you provide a Docker image to the community ([french forum](https://community.gladysassistant.com/)/[english forum](https://en-community.gladysassistant.com/)) for testing before merging.
- [ ] If your changes modify the API (REST or Node.js), did you modify the API documentation? (Documentation is based on comments in code)
- [ ] If you are adding a new features/services which needs explanation, did you modify the user documentation? See [the GitHub repo](https://github.com/GladysAssistant/v4-website) and the [website](https://gladysassistant.com).
- [ ] Did you add fake requests data for the demo mode (`front/src/config/demo.js`) so that the demo website is working without a backend? (if needed) See [https://demo.gladysassistant.com](https://demo.gladysassistant.com).
diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md
index 38faf1683f..3f23caa862 100644
--- a/.github/SUPPORT.md
+++ b/.github/SUPPORT.md
@@ -2,7 +2,7 @@
First, thanks for trying out Gladys!
-The best place to ask for help is our [Gladys Community Forum](https://community.gladysassistant.com/).
+The best place to ask for help is our forum, in [french](https://community.gladysassistant.com/) or [english](https://en-community.gladysassistant.com/).
Please **_do not_** raise an issue on GitHub if it's a support problem!
diff --git a/.github/workflows/docker-release-build.yml b/.github/workflows/docker-release-build.yml
index c51d60ec23..a1408f29e5 100644
--- a/.github/workflows/docker-release-build.yml
+++ b/.github/workflows/docker-release-build.yml
@@ -168,9 +168,6 @@ jobs:
cache-to: type=inline
- name: 🐳 Legacy Tags
run: |
- echo '{"experimental": true}' | sudo tee -a /etc/docker/daemon.json
- export DOCKER_CLI_EXPERIMENTAL=enabled
- sudo systemctl restart docker
export DIGESTARM=$(docker manifest inspect ${{ env.DOCKERHUB_REPO }}:latest | jq -r '.manifests | to_entries[] | select(.value.platform.architecture == "arm" and .value.platform.variant == "v6").value | .digest')
docker pull ${{ env.DOCKERHUB_REPO }}@$DIGESTARM
docker tag ${{ env.DOCKERHUB_REPO }}@$DIGESTARM ${{ env.DOCKERHUB_REPO }}:v4-arm
diff --git a/SECURITY.md b/SECURITY.md
index 5336fcfdcd..4cd6f3cf18 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -2,4 +2,4 @@
## Reporting a Vulnerability
-To report a vulnerability, you can contact us on [Gladys Assistant forum](https://community.gladysassistant.com/) in private, or on the contact form [on our website](https://gladysassistant.com/contact/).
+To report a vulnerability, you can contact us on Gladys Assistant [french forum](https://community.gladysassistant.com/) or [english forum](https://en-community.gladysassistant.com/) in private, or on the contact form [on our website](https://gladysassistant.com/contact/).
diff --git a/front/package-lock.json b/front/package-lock.json
index 14ab2b1eea..6174a5d769 100644
--- a/front/package-lock.json
+++ b/front/package-lock.json
@@ -9,8 +9,8 @@
"@gladysassistant/gladys-gateway-js": "^4.13.1",
"@gladysassistant/theme-optimized": "^1.0.3",
"@jaames/iro": "^5.5.2",
- "@yaireo/tagify": "^4.5.0",
- "apexcharts": "^3.29.0",
+ "@yaireo/tagify": "4.5.0",
+ "apexcharts": "^3.41.1",
"axios": "^0.21.1",
"classnames": "^2.3.1",
"cropperjs": "^1.5.12",
@@ -31,7 +31,7 @@
"preact-router": "^3.2.1",
"qrcode": "^1.4.2",
"react-big-calendar": "^1.6.9",
- "react-clock": "^3.1.0",
+ "react-clock": "^4.5.0",
"react-datepicker": "^3.8.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
@@ -5000,6 +5000,19 @@
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
"dev": true
},
+ "node_modules/@types/lodash": {
+ "version": "4.14.197",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.197.tgz",
+ "integrity": "sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g=="
+ },
+ "node_modules/@types/lodash.memoize": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@types/lodash.memoize/-/lodash.memoize-4.1.7.tgz",
+ "integrity": "sha512-lGN7WeO4vO6sICVpf041Q7BX/9k1Y24Zo3FY0aUezr1QlKznpjzsDk3T3wvH8ofYzoK0QupN9TWcFAFZlyPwQQ==",
+ "dependencies": {
+ "@types/lodash": "*"
+ }
+ },
"node_modules/@types/mime": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@@ -5640,9 +5653,9 @@
}
},
"node_modules/@wojtekmaj/date-utils": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@wojtekmaj/date-utils/-/date-utils-1.0.3.tgz",
- "integrity": "sha512-1VPkkTBk07gMR1fjpBtse4G+oJqpmE+0gUFB0dg3VIL7qJmUVaBoD/vlzMm/jNeOPfvlmerl1lpnsZyBUFIRuw==",
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@wojtekmaj/date-utils/-/date-utils-1.5.0.tgz",
+ "integrity": "sha512-0mq88lCND6QiffnSDWp+TbOxzJSwy2V/3XN+HwWZ7S2n19QAgR5dy5hRVhlECXvQIq2r+VcblBu+S9V+yMcxXw==",
"funding": {
"url": "https://github.com/wojtekmaj/date-utils?sponsor=1"
}
@@ -6280,9 +6293,9 @@
}
},
"node_modules/apexcharts": {
- "version": "3.29.0",
- "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.29.0.tgz",
- "integrity": "sha512-PhI17VayidYAbLb5/g+7WOeirgFrVopzt0qGwLq8V+cd6NXx4CeHYq3S0pDZiUGO7UFQ4YIrT8+ie9/Fnler+w==",
+ "version": "3.41.1",
+ "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.41.1.tgz",
+ "integrity": "sha512-kta8fhXrfZYqW7K9kF7FqZ6imQaC6moyRgcUZjwIky/oeHVVISSN/2rjUIvZXnwxWHiSdDHMqLy+TqJhB4DXFA==",
"dependencies": {
"svg.draggable.js": "^2.2.2",
"svg.easing.js": "^2.0.0",
@@ -14793,10 +14806,11 @@
}
},
"node_modules/get-user-locale": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/get-user-locale/-/get-user-locale-1.5.1.tgz",
- "integrity": "sha512-WiNpoFRcHn1qxP9VabQljzGwkAQDrcpqUtaP0rNBEkFxJdh4f3tik6MfZsMYZc+UgQJdGCxWEjL9wnCUlRQXag==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/get-user-locale/-/get-user-locale-2.3.0.tgz",
+ "integrity": "sha512-I3rQvAUwu2nauRD9YyQBSXVFJZixNouwA+eZld51Sn4Pn0N1qFbgcgOi/nPigJPQlNY519mT95fiSPRgflQiTA==",
"dependencies": {
+ "@types/lodash.memoize": "^4.1.7",
"lodash.memoize": "^4.1.1"
},
"funding": {
@@ -19313,14 +19327,6 @@
"integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==",
"dev": true
},
- "node_modules/merge-class-names": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/merge-class-names/-/merge-class-names-1.4.2.tgz",
- "integrity": "sha512-bOl98VzwCGi25Gcn3xKxnR5p/WrhWFQB59MS/aGENcmUc6iSm96yrFDF0XSNurX9qN4LbJm0R9kfvsQ17i8zCw==",
- "funding": {
- "url": "https://github.com/wojtekmaj/merge-class-names?sponsor=1"
- }
- },
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@@ -24366,21 +24372,35 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/react-clock": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/react-clock/-/react-clock-3.1.0.tgz",
- "integrity": "sha512-KLV3pDBcETc7lHPPqK6EpRaPS8NA3STAes+zIdfr7IY67vYgYc3brOAnGC9IcgA4X4xNPnLZwwaLJXmHrQ/MnQ==",
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/react-clock/-/react-clock-4.5.0.tgz",
+ "integrity": "sha512-0LnNG1NbEOOl0M4v3Ul1D/4U1898S3JIc5GMunR6PGMv+ntGqOV/flV9MbIIXoFu8TEgS6xC/RVHZWCmaKJXhA==",
"dependencies": {
- "@wojtekmaj/date-utils": "^1.0.0",
- "get-user-locale": "^1.4.0",
- "merge-class-names": "^1.1.1",
+ "@wojtekmaj/date-utils": "^1.5.0",
+ "clsx": "^2.0.0",
+ "get-user-locale": "^2.2.1",
"prop-types": "^15.6.0"
},
"funding": {
"url": "https://github.com/wojtekmaj/react-clock?sponsor=1"
},
"peerDependencies": {
- "react": "^15.5.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
- "react-dom": "^15.5.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-clock/node_modules/clsx": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
+ "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==",
+ "engines": {
+ "node": ">=6"
}
},
"node_modules/react-datepicker": {
@@ -35130,6 +35150,19 @@
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
"dev": true
},
+ "@types/lodash": {
+ "version": "4.14.197",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.197.tgz",
+ "integrity": "sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g=="
+ },
+ "@types/lodash.memoize": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@types/lodash.memoize/-/lodash.memoize-4.1.7.tgz",
+ "integrity": "sha512-lGN7WeO4vO6sICVpf041Q7BX/9k1Y24Zo3FY0aUezr1QlKznpjzsDk3T3wvH8ofYzoK0QupN9TWcFAFZlyPwQQ==",
+ "requires": {
+ "@types/lodash": "*"
+ }
+ },
"@types/mime": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@@ -35682,9 +35715,9 @@
}
},
"@wojtekmaj/date-utils": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@wojtekmaj/date-utils/-/date-utils-1.0.3.tgz",
- "integrity": "sha512-1VPkkTBk07gMR1fjpBtse4G+oJqpmE+0gUFB0dg3VIL7qJmUVaBoD/vlzMm/jNeOPfvlmerl1lpnsZyBUFIRuw=="
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@wojtekmaj/date-utils/-/date-utils-1.5.0.tgz",
+ "integrity": "sha512-0mq88lCND6QiffnSDWp+TbOxzJSwy2V/3XN+HwWZ7S2n19QAgR5dy5hRVhlECXvQIq2r+VcblBu+S9V+yMcxXw=="
},
"@xtuc/ieee754": {
"version": "1.2.0",
@@ -36191,9 +36224,9 @@
}
},
"apexcharts": {
- "version": "3.29.0",
- "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.29.0.tgz",
- "integrity": "sha512-PhI17VayidYAbLb5/g+7WOeirgFrVopzt0qGwLq8V+cd6NXx4CeHYq3S0pDZiUGO7UFQ4YIrT8+ie9/Fnler+w==",
+ "version": "3.41.1",
+ "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.41.1.tgz",
+ "integrity": "sha512-kta8fhXrfZYqW7K9kF7FqZ6imQaC6moyRgcUZjwIky/oeHVVISSN/2rjUIvZXnwxWHiSdDHMqLy+TqJhB4DXFA==",
"requires": {
"svg.draggable.js": "^2.2.2",
"svg.easing.js": "^2.0.0",
@@ -42907,10 +42940,11 @@
}
},
"get-user-locale": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/get-user-locale/-/get-user-locale-1.5.1.tgz",
- "integrity": "sha512-WiNpoFRcHn1qxP9VabQljzGwkAQDrcpqUtaP0rNBEkFxJdh4f3tik6MfZsMYZc+UgQJdGCxWEjL9wnCUlRQXag==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/get-user-locale/-/get-user-locale-2.3.0.tgz",
+ "integrity": "sha512-I3rQvAUwu2nauRD9YyQBSXVFJZixNouwA+eZld51Sn4Pn0N1qFbgcgOi/nPigJPQlNY519mT95fiSPRgflQiTA==",
"requires": {
+ "@types/lodash.memoize": "^4.1.7",
"lodash.memoize": "^4.1.1"
}
},
@@ -46562,11 +46596,6 @@
"integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==",
"dev": true
},
- "merge-class-names": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/merge-class-names/-/merge-class-names-1.4.2.tgz",
- "integrity": "sha512-bOl98VzwCGi25Gcn3xKxnR5p/WrhWFQB59MS/aGENcmUc6iSm96yrFDF0XSNurX9qN4LbJm0R9kfvsQ17i8zCw=="
- },
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@@ -50423,14 +50452,21 @@
}
},
"react-clock": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/react-clock/-/react-clock-3.1.0.tgz",
- "integrity": "sha512-KLV3pDBcETc7lHPPqK6EpRaPS8NA3STAes+zIdfr7IY67vYgYc3brOAnGC9IcgA4X4xNPnLZwwaLJXmHrQ/MnQ==",
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/react-clock/-/react-clock-4.5.0.tgz",
+ "integrity": "sha512-0LnNG1NbEOOl0M4v3Ul1D/4U1898S3JIc5GMunR6PGMv+ntGqOV/flV9MbIIXoFu8TEgS6xC/RVHZWCmaKJXhA==",
"requires": {
- "@wojtekmaj/date-utils": "^1.0.0",
- "get-user-locale": "^1.4.0",
- "merge-class-names": "^1.1.1",
+ "@wojtekmaj/date-utils": "^1.5.0",
+ "clsx": "^2.0.0",
+ "get-user-locale": "^2.2.1",
"prop-types": "^15.6.0"
+ },
+ "dependencies": {
+ "clsx": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
+ "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q=="
+ }
}
},
"react-datepicker": {
diff --git a/front/package.json b/front/package.json
index 495d94cd81..1697828014 100644
--- a/front/package.json
+++ b/front/package.json
@@ -47,8 +47,8 @@
"@gladysassistant/gladys-gateway-js": "^4.13.1",
"@gladysassistant/theme-optimized": "^1.0.3",
"@jaames/iro": "^5.5.2",
- "@yaireo/tagify": "^4.5.0",
- "apexcharts": "^3.29.0",
+ "@yaireo/tagify": "4.5.0",
+ "apexcharts": "^3.41.1",
"axios": "^0.21.1",
"classnames": "^2.3.1",
"cropperjs": "^1.5.12",
@@ -69,7 +69,7 @@
"preact-router": "^3.2.1",
"qrcode": "^1.4.2",
"react-big-calendar": "^1.6.9",
- "react-clock": "^3.1.0",
+ "react-clock": "^4.5.0",
"react-datepicker": "^3.8.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
diff --git a/front/src/actions/dashboard/boxes/devicesInRoom.js b/front/src/actions/dashboard/boxes/devicesInRoom.js
deleted file mode 100644
index 31f8e304f4..0000000000
--- a/front/src/actions/dashboard/boxes/devicesInRoom.js
+++ /dev/null
@@ -1,220 +0,0 @@
-import { RequestStatus } from '../../../utils/consts';
-import createBoxActions from '../boxActions';
-import createDeviceActions from '../../device';
-import update from 'immutability-helper';
-import get from 'get-value';
-import debounce from 'debounce';
-const { DEVICE_FEATURE_TYPES, DEVICE_FEATURE_CATEGORIES } = require('../../../../../server/utils/constants');
-
-const BOX_KEY = 'DevicesInRoom';
-
-const getLightStatus = room => {
- let roomLightStatus = 0;
- room.devices.forEach(device => {
- device.features.forEach(feature => {
- // if it's a light
- const isLight =
- feature.category === DEVICE_FEATURE_CATEGORIES.LIGHT &&
- feature.type === DEVICE_FEATURE_TYPES.LIGHT.BINARY &&
- feature.read_only === false;
- // if it's a light and it's turned on, we consider that the light
- // is on in the room
- if (isLight && feature.last_value === 1) {
- roomLightStatus = 1;
- }
- });
- });
- return {
- roomLightStatus
- };
-};
-
-function createActions(store) {
- const boxActions = createBoxActions(store);
- const deviceActions = createDeviceActions(store);
-
- const actions = {
- async getDevicesInRoom(state, box, x, y) {
- boxActions.updateBoxStatus(state, BOX_KEY, x, y, RequestStatus.Getting);
- try {
- const room = await state.httpClient.get(`/api/v1/room/${box.room}?expand=devices`);
- // we test if there are lights ON/OFF device features to control in this room
- const { roomLightStatus } = getLightStatus(room);
- boxActions.mergeBoxData(state, BOX_KEY, x, y, {
- room,
- roomLightStatus
- });
- boxActions.updateBoxStatus(state, BOX_KEY, x, y, RequestStatus.Success);
- } catch (e) {
- boxActions.updateBoxStatus(state, BOX_KEY, x, y, RequestStatus.Error);
- }
- },
- async changeAllLightsStatusRoom(state, x, y, action) {
- boxActions.mergeBoxData(state, BOX_KEY, x, y, {
- roomLightStatus: action
- });
- const data = boxActions.getBoxData(state, BOX_KEY, x, y);
- const promises = [];
- data.room.devices.forEach(device => {
- device.features.forEach(feature => {
- const isLightBinary =
- feature.category === DEVICE_FEATURE_CATEGORIES.LIGHT && feature.type === DEVICE_FEATURE_TYPES.LIGHT.BINARY;
- if (isLightBinary) {
- promises.push(deviceActions.setValue(state, feature.selector, action));
- feature.last_value = action;
- feature.last_value_changed = new Date();
- }
- });
- });
- boxActions.mergeBoxData(state, BOX_KEY, x, y, {
- room: data.room
- });
- await Promise.all(promises);
- },
- async setValueDevice(state, deviceFeatureSelector, action) {
- await deviceActions.setValue(state, deviceFeatureSelector, action);
- },
- async updateValueWithDebounce(state, x, y, device, deviceFeature, deviceIndex, featureIndex, action) {
- const data = boxActions.getBoxData(state, BOX_KEY, x, y);
- const newData = update(data, {
- room: {
- devices: {
- [deviceIndex]: {
- features: {
- [featureIndex]: {
- last_value: {
- $set: action
- },
- last_value_changed: {
- $set: new Date()
- }
- }
- }
- }
- }
- }
- });
- const { roomLightStatus } = getLightStatus(newData.room);
- boxActions.mergeBoxData(state, BOX_KEY, x, y, {
- room: newData.room,
- roomLightStatus
- });
- await actions.setValueDeviceDebounce(state, deviceFeature.selector, action);
- },
- async updateValue(state, x, y, device, deviceFeature, deviceIndex, featureIndex, action) {
- const data = boxActions.getBoxData(state, BOX_KEY, x, y);
- const newData = update(data, {
- room: {
- devices: {
- [deviceIndex]: {
- features: {
- [featureIndex]: {
- last_value: {
- $set: action
- },
- last_value_changed: {
- $set: new Date()
- }
- }
- }
- }
- }
- }
- });
- const { roomLightStatus } = getLightStatus(newData.room);
- boxActions.mergeBoxData(state, BOX_KEY, x, y, {
- room: newData.room,
- roomLightStatus
- });
- await deviceActions.setValue(state, deviceFeature.selector, action);
- },
- deviceFeatureWebsocketEvent(state, x, y, payload) {
- const data = boxActions.getBoxData(state, BOX_KEY, x, y);
- const devices = get(data, 'room.devices');
- if (devices) {
- let found = false;
- let currentDeviceIndex = 0;
- let currentFeatureIndex = 0;
- while (!found && currentDeviceIndex < devices.length) {
- while (!found && currentFeatureIndex < devices[currentDeviceIndex].features.length) {
- if (
- devices[currentDeviceIndex].features[currentFeatureIndex].selector === payload.device_feature_selector
- ) {
- found = true;
- const newData = update(data, {
- room: {
- devices: {
- [currentDeviceIndex]: {
- features: {
- [currentFeatureIndex]: {
- last_value: {
- $set: payload.last_value
- },
- last_value_changed: {
- $set: payload.last_value_changed
- }
- }
- }
- }
- }
- }
- });
- const { roomLightStatus } = getLightStatus(newData.room);
- boxActions.mergeBoxData(state, BOX_KEY, x, y, {
- room: newData.room,
- roomLightStatus
- });
- }
- currentFeatureIndex += 1;
- }
- currentDeviceIndex += 1;
- currentFeatureIndex = 0;
- }
- }
- },
- deviceFeatureStringStateWebsocketEvent(state, x, y, payload) {
- const data = boxActions.getBoxData(state, BOX_KEY, x, y);
- const devices = get(data, 'room.devices');
- if (devices) {
- let found = false;
- let currentDeviceIndex = 0;
- let currentFeatureIndex = 0;
- while (!found && currentDeviceIndex < devices.length) {
- while (!found && currentFeatureIndex < devices[currentDeviceIndex].features.length) {
- if (devices[currentDeviceIndex].features[currentFeatureIndex].selector === payload.device_feature) {
- found = true;
- const newData = update(data, {
- room: {
- devices: {
- [currentDeviceIndex]: {
- features: {
- [currentFeatureIndex]: {
- last_value_string: {
- $set: payload.last_value_string
- },
- last_value_changed: {
- $set: payload.last_value_changed
- }
- }
- }
- }
- }
- }
- });
- boxActions.mergeBoxData(state, BOX_KEY, x, y, {
- room: newData.room
- });
- }
- currentFeatureIndex += 1;
- }
- currentDeviceIndex += 1;
- currentFeatureIndex = 0;
- }
- }
- }
- };
- actions.setValueDeviceDebounce = debounce(actions.setValueDevice, 500);
- return Object.assign({}, actions, boxActions);
-}
-
-export default createActions;
diff --git a/front/src/components/boxs/clock/Clock.jsx b/front/src/components/boxs/clock/Clock.jsx
index 388b72688d..c440dfc656 100644
--- a/front/src/components/boxs/clock/Clock.jsx
+++ b/front/src/components/boxs/clock/Clock.jsx
@@ -22,8 +22,10 @@ const Clock = ({ children, ...props }) => (
{props.year}
-
)}
diff --git a/front/src/components/boxs/clock/style.css b/front/src/components/boxs/clock/style.css
index a1e4b6db9e..973ad34aa6 100644
--- a/front/src/components/boxs/clock/style.css
+++ b/front/src/components/boxs/clock/style.css
@@ -12,22 +12,24 @@
.analogCol {
display: flex;
flex-direction: column;
- flex: auto;
+ flex: 1 0 50%;
}
.analogSmallText {
- font-size: 18px;
+ font-size: 1rem;
text-align: left;
}
.analogBigText {
- font-size: 30px;
+ font-size: 1.3rem;
text-align: left;
}
.analogClock {
- align-items: center;
- justify-content: center;
+ display: flex;
+ margin-left: auto;
+ margin-right: 15%;
+ width: 100px;
}
.digitalTime {
@@ -42,4 +44,7 @@
text-align: center;
}
-
+.reactClock {
+ width: inherit !important;
+ padding-top: 100%;
+}
diff --git a/front/src/components/boxs/device-in-room/DeviceCard.jsx b/front/src/components/boxs/device-in-room/DeviceCard.jsx
index e207471847..cec313b3c8 100644
--- a/front/src/components/boxs/device-in-room/DeviceCard.jsx
+++ b/front/src/components/boxs/device-in-room/DeviceCard.jsx
@@ -2,8 +2,8 @@ import cx from 'classnames';
import DeviceRow from './DeviceRow';
import { DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_TYPES } from '../../../../../server/utils/constants';
-const hasSwitchFeature = (device, featureSelectors) => {
- return device.features.find(feature => isSwitchFeature(feature, featureSelectors));
+const hasSwitchFeature = (features, featureSelectors) => {
+ return features.find(feature => isSwitchFeature(feature, featureSelectors));
};
const isSwitchFeature = (feature, featureSelectors) => {
@@ -15,16 +15,11 @@ const isSwitchFeature = (feature, featureSelectors) => {
);
};
-const changeAllLightsStatusRoom = (props, roomLightStatus) => () => {
- const newStatus = roomLightStatus === 1 ? 0 : 1;
- props.changeAllLightsStatusRoom(props.x, props.y, newStatus);
-};
-
const DeviceCard = ({ children, ...props }) => {
- const { boxTitle, roomLightStatus, loading, devices = [], box = {} } = props;
+ const { boxTitle, roomLightStatus, loading, deviceFeatures = [], box = {} } = props;
const { device_features: featureSelectors = [] } = box;
- const hasBinaryLightDeviceFeature = devices.find(device => hasSwitchFeature(device, featureSelectors)) !== undefined;
+ const hasBinaryLightDeviceFeature = hasSwitchFeature(deviceFeatures, featureSelectors) !== undefined;
return (
@@ -39,7 +34,7 @@ const DeviceCard = ({ children, ...props }) => {
value="1"
class="custom-switch-input"
checked={roomLightStatus === 1}
- onClick={changeAllLightsStatusRoom(props, roomLightStatus)}
+ onClick={props.changeAllLightsStatusRoom}
/>
@@ -56,25 +51,19 @@ const DeviceCard = ({ children, ...props }) => {
- {devices.map((device, deviceIndex) =>
- device.features.map(
- (deviceFeature, deviceFeatureIndex) =>
- featureSelectors.indexOf(deviceFeature.selector) !== -1 && (
-
- )
- )
- )}
+ {deviceFeatures.map((deviceFeature, deviceFeatureIndex) => (
+
+ ))}
diff --git a/front/src/components/boxs/device-in-room/DeviceListWithDragAndDrop.jsx b/front/src/components/boxs/device-in-room/DeviceListWithDragAndDrop.jsx
new file mode 100644
index 0000000000..b722f0e97e
--- /dev/null
+++ b/front/src/components/boxs/device-in-room/DeviceListWithDragAndDrop.jsx
@@ -0,0 +1,97 @@
+import { DndProvider, useDrag, useDrop } from 'react-dnd';
+import { TouchBackend } from 'react-dnd-touch-backend';
+import { HTML5Backend } from 'react-dnd-html5-backend';
+import { useRef } from 'preact/hooks';
+import cx from 'classnames';
+import style from './style.css';
+
+const DEVICE_TYPE = 'DEVICE_TYPE';
+
+const DeviceRow = ({ selectedDeviceFeature, moveDevice, index, removeDevice, updateDeviceFeatureName }) => {
+ const ref = useRef(null);
+ const [{ isDragging }, drag, preview] = useDrag(() => ({
+ type: DEVICE_TYPE,
+ item: () => {
+ return { index };
+ },
+ collect: monitor => ({
+ isDragging: !!monitor.isDragging()
+ })
+ }));
+ const [{ isActive }, drop] = useDrop({
+ accept: DEVICE_TYPE,
+ collect: monitor => ({
+ isActive: monitor.canDrop() && monitor.isOver()
+ }),
+ drop(item) {
+ if (!ref.current) {
+ return;
+ }
+ moveDevice(item.index, index);
+ }
+ });
+ preview(drop(ref));
+ const removeThisDevice = () => {
+ removeDevice(index);
+ };
+
+ const updateThisDeviceFeatureName = e => {
+ updateDeviceFeatureName(index, e.target.value);
+ };
+
+ return (
+
+
+
+
+ );
+};
+
+const DeviceListWithDragAndDrop = ({
+ selectedDeviceFeaturesOptions,
+ isTouchDevice,
+ moveDevice,
+ removeDevice,
+ updateDeviceFeatureName
+}) => (
+
+ {selectedDeviceFeaturesOptions.map((selectedDeviceFeature, index) => (
+
+ ))}
+
+);
+
+export { DeviceListWithDragAndDrop };
diff --git a/front/src/components/boxs/device-in-room/DeviceRow.jsx b/front/src/components/boxs/device-in-room/DeviceRow.jsx
index 2f0e87431f..c490cf2a1e 100644
--- a/front/src/components/boxs/device-in-room/DeviceRow.jsx
+++ b/front/src/components/boxs/device-in-room/DeviceRow.jsx
@@ -1,6 +1,8 @@
import { createElement } from 'preact';
import { DEVICE_FEATURE_TYPES } from '../../../../../server/utils/constants';
+import { getDeviceName } from '../../../utils/device';
+
import BinaryDeviceFeature from './device-features/BinaryDeviceFeature';
import ColorDeviceFeature from './device-features/ColorDeviceFeature';
import SensorDeviceFeature from './device-features/sensor-value/SensorDeviceFeature';
@@ -29,9 +31,12 @@ const ROW_TYPE_BY_FEATURE_TYPE = {
};
const DeviceRow = ({ children, ...props }) => {
+ const { device, deviceFeature } = props;
+ const rowName = deviceFeature.new_label || getDeviceName(device, deviceFeature);
+
// if device is a sensor, we display the sensor deviceFeature
if (props.deviceFeature.read_only) {
- return
;
+ return
;
}
const elementType = ROW_TYPE_BY_FEATURE_TYPE[props.deviceFeature.type];
@@ -41,7 +46,7 @@ const DeviceRow = ({ children, ...props }) => {
return null;
}
- return createElement(elementType, props);
+ return createElement(elementType, { ...props, rowName });
};
export default DeviceRow;
diff --git a/front/src/components/boxs/device-in-room/DevicesBox.jsx b/front/src/components/boxs/device-in-room/DevicesBox.jsx
index 61ea6a8ae1..28bcd56a5a 100644
--- a/front/src/components/boxs/device-in-room/DevicesBox.jsx
+++ b/front/src/components/boxs/device-in-room/DevicesBox.jsx
@@ -1,26 +1,39 @@
import { Component } from 'preact';
import { connect } from 'unistore/preact';
+import Promise from 'bluebird';
import get from 'get-value';
import { RequestStatus } from '../../../utils/consts';
-import { WEBSOCKET_MESSAGE_TYPES } from '../../../../../server/utils/constants';
+import {
+ WEBSOCKET_MESSAGE_TYPES,
+ DEVICE_FEATURE_CATEGORIES,
+ DEVICE_FEATURE_TYPES
+} from '../../../../../server/utils/constants';
import DeviceCard from './DeviceCard';
import debounce from 'debounce';
-const updateDevices = (devices, deviceFeatureSelector, lastValue, lastValueChange) => {
- return devices.map(device => {
- return {
- ...device,
- features: device.features.map(feature => {
- if (feature.selector === deviceFeatureSelector) {
- return {
- ...feature,
- last_value: lastValue,
- last_value_changed: lastValueChange
- };
- }
- return feature;
- })
- };
+const updateDeviceFeatures = (deviceFeatures, deviceFeatureSelector, lastValue, lastValueChange) => {
+ return deviceFeatures.map(feature => {
+ if (feature.selector === deviceFeatureSelector) {
+ return {
+ ...feature,
+ last_value: lastValue,
+ last_value_changed: lastValueChange
+ };
+ }
+ return feature;
+ });
+};
+
+const updateDeviceFeaturesString = (deviceFeatures, deviceFeatureSelector, lastValueString, lastValueChange) => {
+ return deviceFeatures.map(feature => {
+ if (feature.selector === deviceFeatureSelector) {
+ return {
+ ...feature,
+ last_value_string: lastValueString,
+ last_value_changed: lastValueChange
+ };
+ }
+ return feature;
});
};
@@ -28,23 +41,39 @@ class DevicesComponent extends Component {
constructor(props) {
super(props);
this.state = {
- devices: [],
+ deviceFeatures: [],
status: RequestStatus.Getting
};
}
refreshData = () => {
- this.getDevices();
+ this.getDeviceFeatures();
};
- getDevices = async () => {
+ getDeviceFeatures = async () => {
this.setState({ status: RequestStatus.Getting });
try {
+ const deviceFeatureSelectors = this.props.box.device_features;
const devices = await this.props.httpClient.get(`/api/v1/device`, {
- device_feature_selectors: this.props.box.device_features.join(',')
+ device_feature_selectors: deviceFeatureSelectors.join(',')
+ });
+ const deviceFeaturesFlat = [];
+ devices.forEach(device => {
+ device.features.forEach(feature => {
+ deviceFeaturesFlat.push({ ...feature, device });
+ });
});
+ const deviceFeaturesSorted = deviceFeaturesFlat.sort(
+ (a, b) => deviceFeatureSelectors.indexOf(a.selector) - deviceFeatureSelectors.indexOf(b.selector)
+ );
+ const deviceFeaturesNewNames = this.props.box.device_feature_names;
+ if (deviceFeaturesNewNames) {
+ deviceFeaturesSorted.forEach((deviceFeature, index) => {
+ deviceFeature.new_label = deviceFeaturesNewNames[index];
+ });
+ }
this.setState({
- devices,
+ deviceFeatures: deviceFeaturesSorted,
status: RequestStatus.Success
});
} catch (e) {
@@ -55,20 +84,30 @@ class DevicesComponent extends Component {
};
updateDeviceStateWebsocket = payload => {
- let devices = this.state.devices;
- if (devices) {
- devices = updateDevices(devices, payload.device_feature_selector, payload.last_value, payload.last_value_changed);
+ let { deviceFeatures } = this.state;
+ if (deviceFeatures) {
+ deviceFeatures = updateDeviceFeatures(
+ deviceFeatures,
+ payload.device_feature_selector,
+ payload.last_value,
+ payload.last_value_changed
+ );
this.setState({
- devices
+ deviceFeatures
});
}
};
updateDeviceTextWebsocket = payload => {
- let devices = this.state.devices;
- if (devices) {
- devices = updateDevices(devices, payload.device_feature, payload.last_value, payload.last_value_changed);
+ let { deviceFeatures } = this.state;
+ if (deviceFeatures) {
+ deviceFeatures = updateDeviceFeaturesString(
+ deviceFeatures,
+ payload.device_feature,
+ payload.last_value_string,
+ payload.last_value_changed
+ );
this.setState({
- devices
+ deviceFeatures
});
}
};
@@ -79,24 +118,56 @@ class DevicesComponent extends Component {
});
};
- //Remove x, y when DeviceInRoom is rewrite without action
- updateValue = async (x, y, device, deviceFeature, deviceIndex, featureIndex, value) => {
- const devices = updateDevices(this.state.devices, deviceFeature.selector, value, new Date());
- this.setState({
- devices
+ changeAllLightsStatusRoom = async () => {
+ const newValue = this.getLightStatus() === 0 ? 1 : 0;
+ // Foreach device features
+ await Promise.map(this.state.deviceFeatures, async feature => {
+ const isLightBinary =
+ feature.category === DEVICE_FEATURE_CATEGORIES.LIGHT && feature.type === DEVICE_FEATURE_TYPES.LIGHT.BINARY;
+ // if device feature is a light, we control it
+ if (isLightBinary) {
+ return this.updateValue(feature, newValue);
+ }
});
- await this.setValueDevice(deviceFeature, value);
+ };
+
+ updateValue = async (deviceFeature, value) => {
+ const deviceFeatures = updateDeviceFeatures(this.state.deviceFeatures, deviceFeature.selector, value, new Date());
+ await this.setState({
+ deviceFeatures
+ });
+ try {
+ await this.setValueDevice(deviceFeature, value);
+ } catch (e) {
+ console.error(e);
+ }
};
setValueDeviceDebounce = debounce(this.updateValue.bind(this), 200);
- //Remove x, y when DeviceInRoom is rewrite without action
- updateValueWithDebounce = async (x, y, device, deviceFeature, deviceIndex, featureIndex, value) => {
- const devices = updateDevices(this.state.devices, deviceFeature.selector, value, new Date());
+ updateValueWithDebounce = async (deviceFeature, value) => {
+ const deviceFeatures = updateDeviceFeatures(this.state.deviceFeatures, deviceFeature.selector, value, new Date());
this.setState({
- devices
+ deviceFeatures
+ });
+ await this.setValueDeviceDebounce(deviceFeature, value);
+ };
+
+ getLightStatus = () => {
+ let roomLightStatus = 0;
+ this.state.deviceFeatures.forEach(feature => {
+ // if it's a light
+ const isLight =
+ feature.category === DEVICE_FEATURE_CATEGORIES.LIGHT &&
+ feature.type === DEVICE_FEATURE_TYPES.LIGHT.BINARY &&
+ feature.read_only === false;
+ // if it's a light and it's turned on, we consider that the light
+ // is on in the room
+ if (isLight && feature.last_value === 1) {
+ roomLightStatus = 1;
+ }
});
- await this.setValueDeviceDebounce(x, y, device, deviceFeature, deviceIndex, featureIndex, value);
+ return roomLightStatus;
};
componentDidMount() {
@@ -129,21 +200,24 @@ class DevicesComponent extends Component {
);
}
- render(props, { devices, status }) {
+ render(props, { deviceFeatures, status }) {
const boxTitle = props.box.name;
const loading = status === RequestStatus.Getting && !status;
+ const roomLightStatus = this.getLightStatus();
return (
);
}
}
-export default connect('session,httpClient', {})(DevicesComponent);
+export default connect('session,httpClient,user', {})(DevicesComponent);
diff --git a/front/src/components/boxs/device-in-room/DevicesInRoomsBox.jsx b/front/src/components/boxs/device-in-room/DevicesInRoomsBox.jsx
index 969b77d0cf..724661809c 100644
--- a/front/src/components/boxs/device-in-room/DevicesInRoomsBox.jsx
+++ b/front/src/components/boxs/device-in-room/DevicesInRoomsBox.jsx
@@ -1,73 +1,26 @@
import { Component } from 'preact';
import { connect } from 'unistore/preact';
-import get from 'get-value';
-import actions from '../../../actions/dashboard/boxes/devicesInRoom';
-import { RequestStatus, DASHBOARD_BOX_STATUS_KEY, DASHBOARD_BOX_DATA_KEY } from '../../../utils/consts';
-import { WEBSOCKET_MESSAGE_TYPES } from '../../../../../server/utils/constants';
-import DeviceCard from './DeviceCard';
+
+import DeviceBox from './DevicesBox';
class DevicesInRoomComponent extends Component {
- refreshData = () => {
- this.props.getDevicesInRoom(this.props.box, this.props.x, this.props.y);
+ getRoom = async () => {
+ try {
+ const room = await this.props.httpClient.get(`/api/v1/room/${this.props.box.room}`);
+ this.setState({
+ room
+ });
+ } catch (e) {
+ console.error(e);
+ }
};
- updateDeviceStateWebsocket = payload => this.props.deviceFeatureWebsocketEvent(this.props.x, this.props.y, payload);
- updateDeviceTextWebsocket = payload =>
- this.props.deviceFeatureStringStateWebsocketEvent(this.props.x, this.props.y, payload);
-
componentDidMount() {
- this.refreshData();
- this.props.session.dispatcher.addListener(
- WEBSOCKET_MESSAGE_TYPES.DEVICE.NEW_STATE,
- this.updateDeviceStateWebsocket
- );
- this.props.session.dispatcher.addListener(
- WEBSOCKET_MESSAGE_TYPES.DEVICE.NEW_STRING_STATE,
- this.updateDeviceTextWebsocket
- );
- }
-
- componentDidUpdate(previousProps) {
- const roomChanged = get(previousProps, 'box.room') !== get(this.props, 'box.room');
- const deviceFeaturesChanged = get(previousProps, 'box.device_features') !== get(this.props, 'box.device_features');
- if (roomChanged || deviceFeaturesChanged) {
- this.refreshData();
- }
+ this.getRoom();
}
-
- componentWillUnmount() {
- this.props.session.dispatcher.removeListener(
- WEBSOCKET_MESSAGE_TYPES.DEVICE.NEW_STATE,
- this.updateDeviceStateWebsocket
- );
- this.props.session.dispatcher.removeListener(
- WEBSOCKET_MESSAGE_TYPES.DEVICE.NEW_STRING_STATE,
- this.updateDeviceTextWebsocket
- );
- }
-
- render(props, {}) {
- // safely get all data
- const boxData = get(props, `${DASHBOARD_BOX_DATA_KEY}DevicesInRoom.${props.x}_${props.y}`);
- const boxStatus = get(props, `${DASHBOARD_BOX_STATUS_KEY}DevicesInRoom.${props.x}_${props.y}`);
- const roomName = get(boxData, `room.name`);
- const devices = get(boxData, `room.devices`);
-
- const roomLightStatus = get(boxData, `roomLightStatus`);
- const loading = boxStatus === RequestStatus.Getting && !boxData;
-
- return (
-
- );
+ render({ box, x, y }, { room }) {
+ const boxModified = { ...box, name: room && room.name };
+ return
;
}
}
-export default connect(
- 'session,user,DashboardBoxDataDevicesInRoom,DashboardBoxStatusDevicesInRoom',
- actions
-)(DevicesInRoomComponent);
+export default connect('httpClient', {})(DevicesInRoomComponent);
diff --git a/front/src/components/boxs/device-in-room/EditDeviceInRoom.jsx b/front/src/components/boxs/device-in-room/EditDeviceInRoom.jsx
index 0bb732c9c1..b7f2313722 100644
--- a/front/src/components/boxs/device-in-room/EditDeviceInRoom.jsx
+++ b/front/src/components/boxs/device-in-room/EditDeviceInRoom.jsx
@@ -74,8 +74,12 @@ class EditDeviceInRoom extends Component {
}
componentDidUpdate(prevProps) {
- if (prevProps.box.room !== this.props.box.room && this.props.box.room) {
- this.getDeviceFeatures();
+ if (prevProps.box && this.props.box && this.props.box.room) {
+ const deviceFeaturesChanged = prevProps.box.device_features !== this.props.box.device_features;
+ const roomChanged = prevProps.box.room !== this.props.box.room;
+ if (deviceFeaturesChanged || roomChanged) {
+ this.getDeviceFeatures();
+ }
}
}
diff --git a/front/src/components/boxs/device-in-room/EditDevices.jsx b/front/src/components/boxs/device-in-room/EditDevices.jsx
index 39fe467b94..1ed1c42dc5 100644
--- a/front/src/components/boxs/device-in-room/EditDevices.jsx
+++ b/front/src/components/boxs/device-in-room/EditDevices.jsx
@@ -2,19 +2,18 @@ import { Component } from 'preact';
import { Localizer, Text } from 'preact-i18n';
import { connect } from 'unistore/preact';
import Select from 'react-select';
+import update from 'immutability-helper';
import BaseEditBox from '../baseEditBox';
import { getDeviceFeatureName } from '../../../utils/device';
+import { DeviceListWithDragAndDrop } from './DeviceListWithDragAndDrop';
import withIntlAsProp from '../../../utils/withIntlAsProp';
import SUPPORTED_FEATURE_TYPES from './SupportedFeatureTypes';
class EditDevices extends Component {
- updateDeviceFeatures = selectedDeviceFeaturesOptions => {
- selectedDeviceFeaturesOptions = selectedDeviceFeaturesOptions || [];
- const deviceFeatures = selectedDeviceFeaturesOptions.map(option => option.value);
- this.props.updateBoxConfig(this.props.x, this.props.y, {
- device_features: deviceFeatures
- });
- this.setState({ selectedDeviceFeaturesOptions });
+ addDeviceFeature = async selectedDeviceFeatureOption => {
+ const newSelectedDeviceFeaturesOptions = [...this.state.selectedDeviceFeaturesOptions, selectedDeviceFeatureOption];
+ await this.setState({ selectedDeviceFeaturesOptions: newSelectedDeviceFeaturesOptions });
+ this.refreshDeviceFeaturesNames();
};
updateName = e => {
@@ -23,54 +22,149 @@ class EditDevices extends Component {
});
};
- getDeviceFeatures = async () => {
- try {
- this.setState({ loading: true });
- // we get the rooms with the devices
- const devices = await this.props.httpClient.get(`/api/v1/device`);
- const deviceOptions = [];
- const selectedDeviceFeaturesOptions = [];
+ refreshDeviceFeaturesNames = () => {
+ const newDeviceFeatureNames = this.state.selectedDeviceFeaturesOptions.map(o => {
+ return o.new_label !== undefined ? o.new_label : o.label;
+ });
+ const newDeviceFeature = this.state.selectedDeviceFeaturesOptions.map(o => {
+ return o.value;
+ });
+ this.props.updateBoxConfig(this.props.x, this.props.y, {
+ device_feature_names: newDeviceFeatureNames,
+ device_features: newDeviceFeature
+ });
+ };
+
+ refreshDisplayForNewProps = async () => {
+ if (!this.state.devices) {
+ return;
+ }
+ if (!this.props.box || !this.props.box.device_features) {
+ return;
+ }
+ if (!this.state.deviceOptions) {
+ return;
+ }
+ const { selectedDeviceFeaturesOptions } = this.getSelectedDeviceFeaturesAndOptions(this.state.devices);
+ await this.setState({ selectedDeviceFeaturesOptions });
+ };
- devices.forEach(device => {
- const deviceFeatures = [];
- device.features.forEach(feature => {
- const featureOption = {
- value: feature.selector,
- label: getDeviceFeatureName(this.props.intl.dictionary, device, feature)
- };
- if (feature.read_only || SUPPORTED_FEATURE_TYPES.includes(feature.type)) {
- deviceFeatures.push(featureOption);
+ updateDeviceFeatureName = async (index, name) => {
+ const newState = update(this.state, {
+ selectedDeviceFeaturesOptions: {
+ [index]: {
+ new_label: {
+ $set: name
}
- if (this.props.box.device_features && this.props.box.device_features.indexOf(feature.selector) !== -1) {
+ }
+ }
+ });
+ await this.setState(newState);
+ this.refreshDeviceFeaturesNames();
+ };
+
+ getSelectedDeviceFeaturesAndOptions = devices => {
+ const deviceOptions = [];
+ let selectedDeviceFeaturesOptions = [];
+
+ devices.forEach(device => {
+ const deviceFeatures = [];
+ device.features.forEach(feature => {
+ const featureOption = {
+ value: feature.selector,
+ label: getDeviceFeatureName(this.props.intl.dictionary, device, feature)
+ };
+ if (feature.read_only || SUPPORTED_FEATURE_TYPES.includes(feature.type)) {
+ deviceFeatures.push(featureOption);
+ }
+ // If the feature is already selected
+ if (this.props.box.device_features) {
+ const featureIndex = this.props.box.device_features.indexOf(feature.selector);
+ if (this.props.box.device_features && featureIndex !== -1) {
+ // and there is a name associated to it
+ if (this.props.box.device_feature_names && this.props.box.device_feature_names[featureIndex]) {
+ // We set the new_label in the object
+ featureOption.new_label = this.props.box.device_feature_names[featureIndex];
+ }
+ // And we push this to the list of selected feature
selectedDeviceFeaturesOptions.push(featureOption);
}
- });
- if (deviceFeatures.length > 0) {
- deviceFeatures.sort((a, b) => {
- if (a.label < b.label) {
- return -1;
- } else if (a.label > b.label) {
- return 1;
- }
- return 0;
- });
- deviceOptions.push({
- label: device.name,
- options: deviceFeatures
- });
}
});
- await this.setState({ deviceOptions, selectedDeviceFeaturesOptions, loading: false });
+ if (deviceFeatures.length > 0) {
+ deviceFeatures.sort((a, b) => {
+ if (a.label < b.label) {
+ return -1;
+ } else if (a.label > b.label) {
+ return 1;
+ }
+ return 0;
+ });
+ deviceOptions.push({
+ label: device.name,
+ options: deviceFeatures
+ });
+ }
+ });
+ if (this.props.box.device_features) {
+ selectedDeviceFeaturesOptions = selectedDeviceFeaturesOptions.sort(
+ (a, b) => this.props.box.device_features.indexOf(a.value) - this.props.box.device_features.indexOf(b.value)
+ );
+ }
+ return { deviceOptions, selectedDeviceFeaturesOptions };
+ };
+
+ getDeviceFeatures = async () => {
+ try {
+ this.setState({ loading: true });
+ // we get the rooms with the devices
+ const devices = await this.props.httpClient.get(`/api/v1/device`);
+ const { deviceOptions, selectedDeviceFeaturesOptions } = this.getSelectedDeviceFeaturesAndOptions(devices);
+ await this.setState({ devices, deviceOptions, selectedDeviceFeaturesOptions, loading: false });
+ this.refreshDeviceFeaturesNames();
} catch (e) {
console.error(e);
this.setState({ loading: false });
}
};
+ moveDevice = async (currentIndex, newIndex) => {
+ const element = this.state.selectedDeviceFeaturesOptions[currentIndex];
+
+ const newStateWithoutElement = update(this.state, {
+ selectedDeviceFeaturesOptions: {
+ $splice: [[currentIndex, 1]]
+ }
+ });
+ const newState = update(newStateWithoutElement, {
+ selectedDeviceFeaturesOptions: {
+ $splice: [[newIndex, 0, element]]
+ }
+ });
+ await this.setState(newState);
+ this.refreshDeviceFeaturesNames();
+ };
+
+ removeDevice = async index => {
+ const newStateWithoutElement = update(this.state, {
+ selectedDeviceFeaturesOptions: {
+ $splice: [[index, 1]]
+ }
+ });
+ await this.setState(newStateWithoutElement);
+ this.refreshDeviceFeaturesNames();
+ };
+
componentDidMount() {
this.getDeviceFeatures();
}
+ componentDidUpdate(prevProps) {
+ if (prevProps.box.device_features !== this.props.box.device_features) {
+ this.refreshDisplayForNewProps();
+ }
+ }
+
render(props, { selectedDeviceFeaturesOptions, deviceOptions, loading }) {
return (
@@ -91,19 +185,26 @@ class EditDevices extends Component {
/>
+
+
+ {selectedDeviceFeaturesOptions && (
+
+ )}
+
{deviceOptions && (
-
+
)}
diff --git a/front/src/components/boxs/device-in-room/device-features/AirConditioningModeDeviceFeature.jsx b/front/src/components/boxs/device-in-room/device-features/AirConditioningModeDeviceFeature.jsx
index 9edc443c11..043b53b9f3 100644
--- a/front/src/components/boxs/device-in-room/device-features/AirConditioningModeDeviceFeature.jsx
+++ b/front/src/components/boxs/device-in-room/device-features/AirConditioningModeDeviceFeature.jsx
@@ -2,25 +2,15 @@ import get from 'get-value';
import { Text } from 'preact-i18n';
import cx from 'classnames';
-import { getDeviceName } from '../../../../utils/device';
import { DeviceFeatureCategoriesIcon } from '../../../../utils/consts';
import { AC_MODE } from '../../../../../../server/utils/constants';
const AirConditioningModeDeviceFeature = ({ children, ...props }) => {
- const { device, deviceFeature } = props;
+ const { deviceFeature } = props;
const { category, type, last_value: lastValue } = deviceFeature;
function updateValue(value) {
- props.updateValueWithDebounce(
- props.x,
- props.y,
- device,
- deviceFeature,
- props.deviceIndex,
- props.deviceFeatureIndex,
- value,
- lastValue
- );
+ props.updateValueWithDebounce(deviceFeature, value);
}
function auto() {
@@ -40,7 +30,7 @@ const AirConditioningModeDeviceFeature = ({ children, ...props }) => {
|
- {getDeviceName(device, deviceFeature)} |
+ {props.rowName} |
diff --git a/front/src/components/boxs/device-in-room/device-features/BinaryDeviceFeature.jsx b/front/src/components/boxs/device-in-room/device-features/BinaryDeviceFeature.jsx
index 705d9d7a9d..a9dbd65a18 100644
--- a/front/src/components/boxs/device-in-room/device-features/BinaryDeviceFeature.jsx
+++ b/front/src/components/boxs/device-in-room/device-features/BinaryDeviceFeature.jsx
@@ -1,16 +1,6 @@
-import { getDeviceName } from '../../../../utils/device';
-
const BinaryDeviceType = ({ children, ...props }) => {
function updateValue() {
- props.updateValue(
- props.x,
- props.y,
- props.device,
- props.deviceFeature,
- props.deviceIndex,
- props.deviceFeatureIndex,
- props.deviceFeature.last_value === 0 ? 1 : 0
- );
+ props.updateValue(props.deviceFeature, props.deviceFeature.last_value === 0 ? 1 : 0);
}
return (
@@ -18,12 +8,12 @@ const BinaryDeviceType = ({ children, ...props }) => {
|
- {getDeviceName(props.device, props.deviceFeature)} |
+ {props.rowName} |
| |