├── .gitattributes
├── .github
├── dependabot.yml
└── workflows
│ └── runtest.yml
├── .gitignore
├── .markdownlint.json
├── .prettierignore
├── .prettierrc.json
├── LICENSE
├── MMM-JsonTable.js
├── README.md
├── eslint.config.mjs
├── example1.png
├── example2.png
├── example4.png
├── example5.png
├── node_helper.js
├── package-lock.json
└── package.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "npm" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "monthly"
12 |
--------------------------------------------------------------------------------
/.github/workflows/runtest.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [master]
9 | pull_request:
10 | branches: [master]
11 |
12 | jobs:
13 | build:
14 | runs-on: ubuntu-latest
15 |
16 | strategy:
17 | matrix:
18 | node-version: [18.x, 20.x]
19 |
20 | steps:
21 | - uses: actions/checkout@v4
22 | - name: Use Node.js ${{ matrix.node-version }}
23 | uses: actions/setup-node@v4
24 | with:
25 | node-version: ${{ matrix.node-version }}
26 | cache: "npm"
27 | - run: npm install
28 | - run: npm run test
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
--------------------------------------------------------------------------------
/.markdownlint.json:
--------------------------------------------------------------------------------
1 | {
2 | "line_length": false,
3 | "no-inline-html": false
4 | }
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | *.js
2 | *.mjs
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "none",
3 | "overrides": [
4 | {
5 | "files": "*.md",
6 | "options": {
7 | "parser": "markdown"
8 | }
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 timdows
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MMM-JsonTable.js:
--------------------------------------------------------------------------------
1 | /* global Module moment */
2 |
3 | Module.register("MMM-JsonTable", {
4 | jsonData: null,
5 |
6 | // Default module config.
7 | defaults: {
8 | url: "",
9 | arrayName: null,
10 | noDataText:
11 | "Json data is not of type array! Maybe the config arrayName is not used and should be, or is configured wrong.",
12 | keepColumns: [],
13 | size: 0,
14 | tryFormatDate: false,
15 | updateInterval: 15000,
16 | animationSpeed: 500,
17 | descriptiveRow: null
18 | },
19 |
20 | start () {
21 | this.getJson();
22 | this.scheduleUpdate();
23 | },
24 |
25 | scheduleUpdate () {
26 | const self = this;
27 | setInterval(() => {
28 | self.getJson();
29 | }, this.config.updateInterval);
30 | },
31 |
32 | // Request node_helper to get json from url
33 | getJson () {
34 | this.sendSocketNotification("MMM-JsonTable_GET_JSON", {url:this.config.url,id:this.identifier});
35 | },
36 |
37 | socketNotificationReceived (notification, payload) {
38 | if (notification === "MMM-JsonTable_JSON_RESULT") {
39 | // Only continue if the notification came from the request we made
40 | // This way we can load the module more than once
41 | if (payload.id === this.identifier) {
42 | this.jsonData = payload.data;
43 | this.updateDom(this.config.animationSpeed);
44 | }
45 | }
46 | },
47 |
48 | // Override dom generator.
49 | getDom () {
50 | const wrapper = document.createElement("div");
51 | wrapper.className = "xsmall";
52 |
53 | if (!this.jsonData) {
54 | wrapper.innerHTML = "Awaiting json data...";
55 | return wrapper;
56 | }
57 |
58 | const table = document.createElement("table");
59 | const tbody = document.createElement("tbody");
60 |
61 | let items = [];
62 | if (this.config.arrayName) {
63 | items = this.jsonData[this.config.arrayName];
64 | } else {
65 | items = this.jsonData;
66 | }
67 |
68 | // Check if items is of type array
69 | if (!(items instanceof Array)) {
70 | wrapper.innerHTML = this.config.noDataText;
71 | return wrapper;
72 | }
73 |
74 | items.forEach((element) => {
75 | const row = this.getTableRow(element);
76 | tbody.appendChild(row);
77 | });
78 |
79 | // Add in Descriptive Row Header
80 | if (this.config.descriptiveRow) {
81 | const header = table.createTHead();
82 | header.innerHTML = this.config.descriptiveRow;
83 | }
84 |
85 | table.appendChild(tbody);
86 | wrapper.appendChild(table);
87 | return wrapper;
88 | },
89 |
90 | getTableRow (jsonObject) {
91 | const row = document.createElement("tr");
92 | Object.entries(jsonObject).forEach(([key, value]) => {
93 | const cell = document.createElement("td");
94 |
95 | let valueToDisplay = "";
96 | let cellValue = "";
97 |
98 | if (value.constructor === Object) {
99 | if ("value" in value) {
100 | cellValue = value.value;
101 | } else {
102 | cellValue = "";
103 | }
104 |
105 | if ("color" in value) {
106 | cell.style.color = value.color;
107 | }
108 | } else {
109 | cellValue = value;
110 | }
111 |
112 | if (key === "icon") {
113 | cell.classList.add("fa", cellValue);
114 | } else if (this.config.tryFormatDate) {
115 | valueToDisplay = this.getFormattedValue(cellValue);
116 | } else if (
117 | this.config.keepColumns.length === 0 ||
118 | this.config.keepColumns.indexOf(key) >= 0
119 | ) {
120 | valueToDisplay = cellValue;
121 | }
122 |
123 | const cellText = document.createTextNode(valueToDisplay);
124 |
125 | if (this.config.size > 0 && this.config.size < 9) {
126 | const heading = document.createElement(`H${this.config.size}`);
127 | heading.appendChild(cellText);
128 | cell.appendChild(heading);
129 | } else {
130 | cell.appendChild(cellText);
131 | }
132 |
133 | row.appendChild(cell);
134 | });
135 | return row;
136 | },
137 |
138 | // Format a date string or return the input
139 | getFormattedValue (input) {
140 | const momentObj = moment(input);
141 | if (typeof input === "string" && momentObj.isValid()) {
142 | // Show a formatted time if it occures today
143 | if (
144 | momentObj.isSame(new Date(Date.now()), "day") &&
145 | momentObj.hours() !== 0 &&
146 | momentObj.minutes() !== 0 &&
147 | momentObj.seconds() !== 0
148 | ) {
149 | return momentObj.format("HH:mm:ss");
150 | }
151 | return momentObj.format("YYYY-MM-DD");
152 | }
153 | return input;
154 | }
155 | });
156 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MMM-JsonTable
2 |
3 | A module for the [MagicMirror²](https://github.com/MagicMirrorOrg/MagicMirror) project which creates a table filled with a list gathered from a json request.
4 |
5 | All the variables of the objects in the array are represented by a table column.
6 | For every column it checks if a valid DateTime is given, and then formats it to `HH:mm:ss` if it is today or `YYYY-MM-DD` otherwise.
7 |
8 | ## Installation
9 |
10 | Just clone the module into your modules folder of your MagicMirror².
11 |
12 | ```shell
13 | git clone https://github.com/timdows/MMM-JsonTable
14 | ```
15 |
16 | That's it!
17 |
18 | If you are a developer please also install the depenendies for linter and prettier:
19 |
20 | ```shell
21 | cd MMM-JsonTable
22 | npm install
23 | ```
24 |
25 | ## Build and Test status
26 |
27 | [](https://github.com/timdows/MMM-JsonTable/actions/workflows/runtest.yml)
28 |
29 | ## Config options
30 |
31 | Except `url` all options are optional.
32 |
33 |
34 | | **Option** | **Description** |
35 | | -------------- | -------------- |
36 | | url | The full url to get the json response from
**Default value:** `""` |
37 | | arrayName | Define the name of the variable that holds the array to display
**Default value:** `null` |
38 | | noDataText | Text indicating that there is no data.
**Default value:** `Json data is not of type array! Maybe the config arrayName is not used and should be, or is configured wrong.` |
39 | | keepColumns | Columns on json will be showed
**Default value:** `[]` |
40 | | tryFormatDate | For every column it checks if a valid DateTime is given, and then formats it to `HH:mm:ss` if it is today or `YYYY-MM-DD` otherwise
**Default value:** `false`
**Possible values:** `false` and `true` |
41 | | size | Text size at table, 0 is default and 3 is H3
**Default value:** `0`
**Possible values:** `0` - `3` |
42 | | updateInterval | Milliseconds between the refersh
**Default value:** `15000` |
43 | | animationSpeed | Speed of the update animation. (Milliseconds)
If you don't want that the module blinks during an update, set the value to `0`.
**Default value:** `500`
**Possible values:** `0` - `5000` |
44 | | descriptiveRow | Complete html table row that will be added above the array data
**Default value:** `""` |
45 |
46 |
47 | ## Example 1
48 |
49 | End result:
50 |
51 | 
52 |
53 | Raw json response:
54 |
55 | ```json
56 | {
57 | "items": [
58 | {
59 | "name": "Watt",
60 | "value": "270 Watt"
61 | },
62 | {
63 | "name": "Today",
64 | "value": "5.85 kWh"
65 | },
66 | {
67 | "name": "ThisWeek",
68 | "value": "5.83 kWh"
69 | },
70 | {
71 | "name": "ThisMonth",
72 | "value": "12.8 kWh"
73 | },
74 | {
75 | "name": "LastMonth",
76 | "value": "246.75 kWh"
77 | }
78 | ]
79 | }
80 | ```
81 |
82 | Configuration:
83 |
84 | ```javascript
85 | {
86 | module: 'MMM-JsonTable',
87 | position: 'top_right',
88 | header: 'HouseDB Sevensegment',
89 | config: {
90 | url: 'https://xyz/abc/get.json', // Required
91 | arrayName: 'items' // Optional
92 | }
93 | }
94 | ```
95 |
96 | ## Example 2
97 |
98 | 
99 |
100 | Raw json response:
101 |
102 | ```json
103 | {
104 | "currentUsages": [
105 | {
106 | "deviceName": "P1",
107 | "currentWattValue": 180,
108 | "todayKwhUsage": 5.902,
109 | "lastUpdate": "2018-04-02T18:12:06Z"
110 | },
111 | {
112 | "deviceName": "Studie - MainDown",
113 | "currentWattValue": 76,
114 | "todayKwhUsage": 0.46,
115 | "lastUpdate": "2018-04-02T18:06:52Z"
116 | },
117 | {
118 | "deviceName": "BoilerPower",
119 | "currentWattValue": 0,
120 | "todayKwhUsage": 2.21,
121 | "lastUpdate": "2018-04-02T17:30:01Z"
122 | },
123 | {
124 | "deviceName": "Koelkast",
125 | "currentWattValue": 1.3,
126 | "todayKwhUsage": 0.55,
127 | "lastUpdate": "2018-04-02T18:09:55Z"
128 | },
129 | {
130 | "deviceName": "Vaatwasser",
131 | "currentWattValue": 0.5,
132 | "todayKwhUsage": 0.01,
133 | "lastUpdate": "2018-04-02T18:10:51Z"
134 | },
135 | {
136 | "deviceName": "Wasmachine",
137 | "currentWattValue": 0,
138 | "todayKwhUsage": 0,
139 | "lastUpdate": "2018-04-02T18:12:06Z"
140 | }
141 | ]
142 | }
143 | ```
144 |
145 | Configuration:
146 |
147 | ```javascript
148 | {
149 | module: 'MMM-JsonTable',
150 | position: 'top_right',
151 | header: 'HouseDB Current Usages',
152 | config: {
153 | url: 'https://xyz/abc/get.json', // Required
154 | arrayName: 'currentUsages', // Optional
155 | tryFormatDate: true
156 | }
157 | }
158 | ```
159 |
160 | ## Example 3 (with font awesome icons)
161 |
162 | 
163 |
164 | Raw json response:
165 |
166 | ```json
167 | {
168 | "cups":[
169 | {
170 | "icon":"fa-calendar",
171 | "data":"Senaste bryggning",
172 | "value":"2019-03-07",
173 | "type":""
174 | },
175 | {
176 | "icon":"fa-clock-o",
177 | "data":"Klockan",
178 | "value":"17:32:06",
179 | "type":""
180 | },
181 | {
182 | "icon":"fa-coffee",
183 | "data":"Totalt antal bryggda koppar",
184 | "value":60,
185 | "type":"st"
186 | },
187 | ...
188 | ]
189 | }
190 | ```
191 |
192 | ## Example 4 (with descriptive row)
193 |
194 | 
195 |
196 | Raw json response:
197 |
198 | ```json
199 | {
200 | "deviceKwhUsages":[
201 | {
202 | "name": "Studie - MainDown",
203 | "today": 0,
204 | "todayFormatted": "0",
205 | "thisWeek": 1.27,
206 | "thisWeekFormatted": "1,27",
207 | "lastWeek": 7,
208 | "lastWeekFormatted": "7,00",
209 | "thisMonth": 17.41,
210 | "thisMonthFormatted": "17,41",
211 | "lastMonth": 30.58,
212 | "tLastMonthFormatted": "30,58"
213 | },
214 | {
215 | "name": "BoilerPower",
216 | "today": 0,
217 | "todayFormatted": "0",
218 | "thisWeek": 1.9,
219 | "thisWeekFormatted": "1,90",
220 | "lastWeek": 13.3,
221 | "lastWeekFormatted": "13,30",
222 | "thisMonth": 30.44,
223 | "thisMonthFormatted": "30,44",
224 | "lastMonth": 54.99,
225 | "tLastMonthFormatted": "54,99"
226 | },
227 | ...
228 | ]
229 | }
230 | ```
231 |
232 | Configuration:
233 |
234 | ```javascript
235 | {
236 | module: 'MMM-JsonTable',
237 | position: 'top_right',
238 | header: 'HouseDB Kwh Statistics',
239 | config: {
240 | url: 'https://xyz/abc/get.json',
241 | arrayName: 'deviceKwhUsages',
242 | descriptiveRow: '