From 78975e499304ca07c74810d0a8a7f2314fb7dcdf Mon Sep 17 00:00:00 2001 From: Maddie Date: Fri, 24 Jan 2020 15:34:00 +0100 Subject: [PATCH 1/6] Added background functionality --- smart-home-sensors/background/main.js | 176 +++++++++++ smart-home-sensors/background/package.json | 5 + smart-home-sensors/foreground/index.html | 130 +++++--- smart-home-sensors/foreground/index.js | 329 +++++++++++++-------- smart-home-sensors/foreground/style.css | 70 +++-- 5 files changed, 522 insertions(+), 188 deletions(-) create mode 100644 smart-home-sensors/background/main.js create mode 100644 smart-home-sensors/background/package.json diff --git a/smart-home-sensors/background/main.js b/smart-home-sensors/background/main.js new file mode 100644 index 0000000..64ea9e7 --- /dev/null +++ b/smart-home-sensors/background/main.js @@ -0,0 +1,176 @@ +let Wappsto = require("wapp-api"); + +let wappsto = new Wappsto(); + +let wappstoConsole = require("wapp-api/console"); +wappstoConsole.start(); + +let device, sensors, led, data; + +wappsto + .get( + "network", + { + name: "Smart Home" + }, + { + quantity: 1, + expand: 5, + subscribe: true + } + ) + .then(function(collection) { + device = collection.get("0.device"); + sensors = device.find({ name: "Sensors" }).get("value"); + led = device.find({ name: "LED" }).get("value"); + + initSensorListerners(); + initButtonListener(); + initBrightnessListener(); + }) + .catch(console.error); + +wappsto + .get( + "data", + {}, + { + expand: 5, + subscribe: true + } + ) + .then(function(collection) { + data = collection.first(); + + data.on("change", function() { + updateSensorData(data.get("sensorToUpdate")); + }); + }) + .catch(console.error); + +function initSensorListerners() { + if (sensors) { + sensors.each(sensorValue => { + let sensorState = sensorValue.get("state").find({ type: "Report" }); + + sensorState.on("change:data", function() { + let currentlySelectedSensor = data.get("sensorToUpdate"); + let sensorName = sensorValue.get("name"); + + if (currentlySelectedSensor === sensorName) { + updateSensorData(currentlySelectedSensor); + } else if (currentlySelectedSensor === "temperature-CO2") { + updateSensorData(currentlySelectedSensor); + } + }); + }); + } +} + +function updateSensorData(sensorValueToBeUpdated) { + let panel = led + .find({ name: "LED Panel" }) + .get("state") + .find({ type: "Control" }); + + if (sensors) { + if (sensorValueToBeUpdated === "temperature-CO2") { + let temp_value = sensors.find({ name: "Temperature" }); + let temp_data = temp_value + .get("state") + .find({ type: "Report" }) + .get("data"); + + let CO2_value = sensors.find({ name: "CO2" }); + let CO2_data = CO2_value.get("state") + .find({ type: "Report" }) + .get("data"); + + panel.save( + { + data: + +Number(temp_data).toFixed(2) + + " " + + temp_value.get("number.unit") + + " " + + +Number(CO2_data).toFixed(2) + + " " + + CO2_value.get("number.unit") + }, + { patch: true } + ); + } else { + let foundSensorValue = sensors.find({ name: sensorValueToBeUpdated }); + + if (foundSensorValue) { + let data = foundSensorValue + .get("state") + .find({ type: "Report" }) + .get("data"); + + panel.save({ + data: + +Number(data).toFixed(2) + + " " + + foundSensorValue.get("number.unit") + + " " + }); + } else { + console.log("Sensor could not be found"); + } + } + } +} + +function initButtonListener() { + if (led) { + let button = led + .find({ name: "Button" }) + .get("state") + .find({ type: "Report" }); + let brightness = led + .find({ name: "Brightness" }) + .get("state") + .find({ type: "Control" }); + + button.on("change:data", function(model) { + let isButtonOn = model.get("data"); + let userBrightnessOption = data.get("brightnessOption"); + let setting = 0; + + if (isButtonOn === "1") { + setting = userBrightnessOption; + } + + brightness.save({ data: setting.toString() }, { patch: true }); + }); + } +} + +function initBrightnessListener() { + if (led) { + let button = led + .find({ name: "Button" }) + .get("state") + .find({ type: "Report" }); + let brightnessState = led + .find({ name: "Brightness" }) + .get("state") + .find({ type: "Report" }); + let userBrightnessOption = data.get("brightnessOption"); + + brightnessState.on("change:data", function() { + let currentBrightnessOption = brightnessState.get("data"); + let currentButtonOption = button.get("data"); + + if (currentButtonOption === "1") { + if (currentBrightnessOption !== userBrightnessOption) { + data.save( + { brightnessOption: currentBrightnessOption }, + { patch: true } + ); + } + } + }); + } +} diff --git a/smart-home-sensors/background/package.json b/smart-home-sensors/background/package.json new file mode 100644 index 0000000..92db5ab --- /dev/null +++ b/smart-home-sensors/background/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "wapp-api": "^1.2.2" + } +} diff --git a/smart-home-sensors/foreground/index.html b/smart-home-sensors/foreground/index.html index 954dd26..f0264b4 100644 --- a/smart-home-sensors/foreground/index.html +++ b/smart-home-sensors/foreground/index.html @@ -1,46 +1,67 @@ - + - - - - + + + + - + - + - +
-
-

Smart Home

+

Smart Home

-
-

Sensors

-
- +
-
@@ -93,7 +113,6 @@
CO2
-
@@ -117,7 +136,6 @@
Temperature
-
@@ -141,7 +159,6 @@
Pressure
-
@@ -162,15 +179,15 @@
Humidity

Step:

-

LED

-
- +
RGB
@@ -180,7 +197,9 @@
RGB
-
+
+ +
@@ -223,44 +242,79 @@
Brightness
-
Panel
-
- +
+ +
- - + +
@@ -279,24 +333,14 @@
Panel
-
Button
- -
- - -
-
- - + +
diff --git a/smart-home-sensors/foreground/index.js b/smart-home-sensors/foreground/index.js index c072049..09ed1ff 100644 --- a/smart-home-sensors/foreground/index.js +++ b/smart-home-sensors/foreground/index.js @@ -1,101 +1,131 @@ // Define shared across the functions variables: -var device, sensors, led, ledListen; +let device, sensors, led, ledListen, data; // Instantiate wapp-api: const wapp = new Wappsto(); // Define a color transition schema (HEX values) for Sensor icons: const colorSchema = { - CO2: ['60A664', 'EEC800', 'D86C00', 'C1312C'], - temperature: ['8AD5D7', '01B7CD', 'FDB813', 'F37020', 'C9234B'], - pressure: ['60A664', 'EEC800', 'D86C00', 'C1312C'], - humidity: ['C7F2FF', '6EADDF', '4F96D8', '357ECD', '1C68C0'] -} + CO2: ["60A664", "EEC800", "D86C00", "C1312C"], + temperature: ["8AD5D7", "01B7CD", "FDB813", "F37020", "C9234B"], + pressure: ["60A664", "EEC800", "D86C00", "C1312C"], + humidity: ["C7F2FF", "6EADDF", "4F96D8", "357ECD", "1C68C0"] +}; // Get the "network" object that contains all the necessary information for this wapp: -wapp.get('network', { - name: 'Smart Home' -}, { - expand: 5, - subscribe: true, - quantity: 1 -}).then(function(collection) { - // Extract objects and assign them to the shared variables: - device = collection.get('0.device'); - sensors = device.find({ name: 'Sensors' }).get('value'); - led = device.find({ name: 'LED' }).get('value'); - - // Call the wapp kick-off functions: - initValueListeners(); - init(); -}).catch(console.error); +wapp + .get( + "network", + { + name: "Smart Home" + }, + { + expand: 5, + subscribe: true, + quantity: 1 + } + ) + .then(function(collection) { + // Extract objects and assign them to the shared variables: + device = collection.get("0.device"); + sensors = device.find({ name: "Sensors" }).get("value"); + led = device.find({ name: "LED" }).get("value"); + + // Call the wapp kick-off functions: + initValueListeners(); + init(); + }) + .catch(console.error); + +// Get the data model to enable communication between the foreground and background +wapp + .get( + "data", + {}, + { + subscribe: true + } + ) + .then(function(collection) { + data = collection.first(); + }) + .catch(console.error); function initValueListeners() { - var panel = led.find({ name: 'LED Panel' }).get('state').find({ type: 'Control' }); + var panel = led + .find({ name: "LED Panel" }) + .get("state") + .find({ type: "Control" }); // Find all boxes that represent devices: $.each($('[id^="v-"]'), function(i, e) { e = $(e); // Convert the plain HTML element to a jQuery element: // Extract and modify some information: - var name = e.attr('id').split('v-')[1].replace('_', ' '); + var name = e + .attr("id") + .split("v-")[1] + .replace("_", " "); var schema = colorSchema[name]; - name = name.charAt(0).toUpperCase()+name.slice(1); + name = name.charAt(0).toUpperCase() + name.slice(1); var value = sensors.find({ name: name }) || led.find({ name: name }); - var state = value.get('state').find({ type: 'Report' }); + var state = value.get("state").find({ type: "Report" }); // Listen to the incoming "state" changes (events) using a WebSocket // connection and trigger the following function on such events: - state.on('change:data', function(model) { - + state.on("change:data", function(model) { // Extract some information: - var data = model.get('data'); - var min = value.get('number.min'); - var max = value.get('number.max'); + var data = model.get("data"); + var min = value.get("number.min"); + var max = value.get("number.max"); if (schema) { // Do some math to calculate the % based on the // current value and the allowed value range: var index = Math.round(((+data - min) * 100) / (max - min)); // Generate a palette of 100 colors based on the color schema for the given sensor: - var color = chroma.bezier(schema).scale().colors(100); + var color = chroma + .bezier(schema) + .scale() + .colors(100); // Find the icon and change its color: - e.find('i').css('color', color[index]); + e.find("i").css("color", color[index]); } // Convert some information: - var date = new Date(model.get('timestamp')); - var dataString = +Number(data).toFixed(2)+' '+value.get('number.unit'); + var date = new Date(model.get("timestamp")); + var dataString = + +Number(data).toFixed(2) + " " + value.get("number.unit") + " "; // Find HTML elements and change their current content // with the extracted and calculated data: - e.find('.date').html(date.toLocaleDateString()); - e.find('.time').html(date.toLocaleTimeString()); - e.find('.value').html(dataString); - e.find('.report').html(+Number(data).toFixed(2) || data); - e.find('.unit').html(value.get('number.unit')); - e.find('.min').html(min); - e.find('.max').html(max); - e.find('.step').html(value.get('number.step')); - e.find('time').timeago('update', state.get('timestamp')); + e.find(".date").html(date.toLocaleDateString()); + e.find(".time").html(date.toLocaleTimeString()); + e.find(".value").html(dataString); + e.find(".report").html(+Number(data).toFixed(2) || data); + e.find(".unit").html(value.get("number.unit")); + e.find(".min").html(min); + e.find(".max").html(max); + e.find(".step").html(value.get("number.step")); + e.find("time").timeago("update", state.get("timestamp")); // Pass the current value to the LED Panel if the selected sensor matches: - if (e.attr('id') === ledListen) { + if (e.attr("id") === ledListen) { panel.save({ data: dataString }, { patch: true }); } }); // Trigger the "state" change event manually to initialize the view: - state.emit('change:data', state); + state.emit("change:data", state); // Extract the "Control" information and display them: - var stateC = value.get('state').find({ type: 'Control' }); + var stateC = value.get("state").find({ type: "Control" }); if (stateC) { - stateC.on('change:data', function(model) { - var data = model.get('data'); - e.find('.control').html(data); + stateC.on("change:data", function(model) { + var data = model.get("data"); + e.find(".control").html(data); }); - stateC.emit('change:data', stateC); + stateC.emit("change:data", stateC); } }); } @@ -105,14 +135,14 @@ function init() { initRoundSlider(); if (led) { initPanel(); - initSwitch() + initButton(); } } $(document).ready(init); function initColorPicker() { - var htmlColorPicker = $('#colorPicker'); + var htmlColorPicker = $("#colorPicker"); var colorPicker = function() { // Remove the current Color Picker HTML element to achieve @@ -120,22 +150,22 @@ function initColorPicker() { htmlColorPicker.empty(); // Create a Color Picker HTML element with the current box width: - var iroCp = new iro.ColorPicker('#colorPicker', { + var iroCp = new iro.ColorPicker("#colorPicker", { width: htmlColorPicker.parent().width() }); // Listen to the selected Color Picker HEX values, convert // to decimal and send them to the LED strip: - iroCp.on(['input:end'], function(event) { + iroCp.on(["input:end"], function(event) { if (led) { var decimal = parseInt(event.hexString.slice(1), 16).toString(); - rgb.find({ type: 'Control' }).save({ data: decimal }, { patch: true }); + rgb.find({ type: "Control" }).save({ data: decimal }, { patch: true }); } }); if (led) { - var rgb = led.find({ name: 'RGB' }).get('state'); - var color = rgb.find({ type: 'Control' }).get('data'); + var rgb = led.find({ name: "RGB" }).get("state"); + var color = rgb.find({ type: "Control" }).get("data"); try { // Set the Color Picker pointer to the current stored value: iroCp.color.hexString = (+color).toString(16); @@ -143,15 +173,18 @@ function initColorPicker() { console.error(e); } - var rgbR = rgb.find({ type: 'Report' }); - rgbR.on('change:data', function(model) { + var rgbR = rgb.find({ type: "Report" }); + rgbR.on("change:data", function(model) { // Listen to the reported Color Picker values and display them: - var col = (+model.get('data')).toString(16); - htmlColorPicker.parent().find('i').css('color', '#'+col); + var col = (+model.get("data")).toString(16); + htmlColorPicker + .parent() + .find("i") + .css("color", "#" + col); }); - rgbR.emit('change:data', rgbR); + rgbR.emit("change:data", rgbR); } - } + }; // Listen to the window size changes: $(window).resize(colorPicker); @@ -161,23 +194,25 @@ function initColorPicker() { } function initRoundSlider() { - var htmlRoundSlider = $('#roundSlider'); + var htmlRoundSlider = $("#roundSlider"); var roundSlider = function() { // Find the current "Brightness" value to set the Slider pointer: if (led) { - var model = led.find({ name: 'Brightness' }) - .get('state').find({ type: 'Control' }); - var value = model.get('data'); + var model = led + .find({ name: "Brightness" }) + .get("state") + .find({ type: "Control" }); + var value = model.get("data"); } else { var value = 0; } htmlRoundSlider.roundSlider({ - radius: htmlRoundSlider.parent().width()/2, - handleSize: '+10', - handleShape: 'dot', - sliderType: 'min-range', + radius: htmlRoundSlider.parent().width() / 2, + handleSize: "+10", + handleShape: "dot", + sliderType: "min-range", value: +value, change: function(event) { if (led) { @@ -185,7 +220,7 @@ function initRoundSlider() { } } }); - } + }; // Listen to the window size changes, when it happens, recreate the Round Slider // HTML element to achieve the resize effect as it's not responsive on its own: @@ -197,85 +232,137 @@ function initRoundSlider() { function initPanel() { // Find HTML elements and extract some data: - var form = $('#v-LED_Panel form'); + var form = $("#v-LED_Panel form"); var input = form.find('input[type="text"]'); - var panel = led.find({ name: 'LED Panel' }).get('state').find({ type: 'Control' }); - input.val(panel.get('data')); + var panel = led + .find({ name: "LED Panel" }) + .get("state") + .find({ type: "Control" }); + input.val(panel.get("data")); // Listen to the input text field changes: - form.submit(function(event) { - // Unselect the sensor icon to avoid current text overwrites: - $('#v-LED_Panel .panel-icons input:checked').click(); - panel.save({ data: input.val() || ' ' }, { patch: true }); - event.preventDefault(); - }).keypress(function(event) { - // Listen to enter keystrokes: - if (event.which == 13) { - form.submit(); + form + .submit(function(event) { + // Unselect the sensor icon to avoid current text overwrites: + $("#v-LED_Panel .panel-icons input:checked").click(); + panel.save({ data: input.val() || " " }, { patch: true }); event.preventDefault(); - } - }); + }) + .keypress(function(event) { + // Listen to enter keystrokes: + if (event.which == 13) { + form.submit(); + event.preventDefault(); + } + }); // Listen to the icon selects, once icon is selected it will pass // the current value of the selected sensor to the LED Panel: - $('#v-LED_Panel .panel-icons input').click(function() { + $("#v-LED_Panel .panel-icons input").click(function() { if (this.value === ledListen) { this.checked = false; ledListen = undefined; } else { ledListen = this.value; - var name = this.value.split('v-')[1].replace('_', ' '); - name = name.charAt(0).toUpperCase()+name.slice(1); - var value = sensors.find({ name: name }); - var data = value.get('state').find({ type: 'Report' }).get('data'); - panel.save({ - data: +Number(data).toFixed(2)+' '+value.get('number.unit') - }, { patch: true }); + if (this.value === "temperature-CO2") { + // Save custom option used to display temperature along with CO2 on the panel + saveSelectedSensor("temperature-CO2"); + var temp_value = sensors.find({ name: "Temperature" }); + var temp_data = temp_value + .get("state") + .find({ type: "Report" }) + .get("data"); + var CO2_value = sensors.find({ name: "CO2" }); + var CO2_data = CO2_value.get("state") + .find({ type: "Report" }) + .get("data"); + + panel.save( + { + data: + +Number(temp_data).toFixed(2) + + " " + + temp_value.get("number.unit") + + " " + + +Number(CO2_data).toFixed(2) + + " " + + CO2_value.get("number.unit") + + " " + }, + { patch: true } + ); + } else { + var name = this.value.split("v-")[1].replace("_", " "); + name = name.charAt(0).toUpperCase() + name.slice(1); + var value = sensors.find({ name: name }); + saveSelectedSensor(value.get("name")); + var data = value + .get("state") + .find({ type: "Report" }) + .get("data"); + + panel.save( + { + data: + +Number(data).toFixed(2) + " " + value.get("number.unit") + " " + }, + { patch: true } + ); + } } }); } -function initSwitch() { - // Find HTML elements and extract some data: - var btn = led.find({ name: 'Button' }).get('state').find({ type: 'Report' }); - var brightness = led.find({ name: 'Brightness' }).get('state').find({ type: 'Control' }); - var switchBrigtness = $('#switch-brightness').is(':checked'); +function saveSelectedSensor(value) { + // Saves a sensor value to the data model + data.save({ sensorToUpdate: value }, { patch: true }); +} - // Listen to the physical button presses and display the current state: - btn.on('change:data', function(model) { - var isOn = model.get('data') == '1'; - $('#led-btb-onOff').prop('checked', isOn); +function saveBrightnessOption(option) { + // Saves brightness state data to the data model + data.save({ brightnessOption: option }, { patch: true }); +} - if (switchBrigtness) { - if (isOn) { - var value = $('#roundSlider').roundSlider('option', 'value'); - } else { - var value = 0; - } - brightness.save({ data: value.toString() }, { patch: true }); +function initButton() { + let btn = led + .find({ name: "Button" }) + .get("state") + .find({ type: "Report" }); + + // Listen to the physical button presses and display the current state: + btn.on("change:data", function() { + if (btn.get("data") === "1") { + $("#switch-brightness").prop("checked", true); + $("#brightness-label").html("Brightness On"); + } else { + $("#switch-brightness").prop("checked", false); + $("#brightness-label").html("Brightness Off"); } }); - btn.emit('change:data', btn); + btn.emit("change:data", btn); - // Listen to the button clicks (in the wapp): - $('#switch-brightness').change(function() { - switchBrigtness = this.checked; - }) + // Get the currently selected brightness state and save it + let value = $("#roundSlider").roundSlider("option", "value"); + saveBrightnessOption(value); } function showDetails() { // Toggle the view of boxes: - $.each($('.info'), function(i, e) { - $(e).toggle().parent().find('div:first').toggle(); + $.each($(".info"), function(i, e) { + $(e) + .toggle() + .parent() + .find("div:first") + .toggle(); }); } function updateDevices() { // Pull data from the Raspberry Pi: wapp.send({ - method: 'patch', - url: '/value', - data: { status: 'update' } + method: "patch", + url: "/value", + data: { status: "update" } }); } diff --git a/smart-home-sensors/foreground/style.css b/smart-home-sensors/foreground/style.css index d238e6f..0bda725 100644 --- a/smart-home-sensors/foreground/style.css +++ b/smart-home-sensors/foreground/style.css @@ -8,10 +8,10 @@ h3 { margin-top: 25px; } - /* Style the boxes: */ -.sensors > .grid-x > .cell, .led > .grid-x > .cell { - box-shadow: 0 2px 5px rgba(0,0,0,0.1); +.sensors > .grid-x > .cell, +.led > .grid-x > .cell { + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); border-bottom: 3px solid white; background: white; text-align: center; @@ -23,7 +23,8 @@ h3 { min-height: 290px; } -.sensors > .grid-x > .cell:hover, .led > .grid-x > .cell:hover { +.sensors > .grid-x > .cell:hover, +.led > .grid-x > .cell:hover { /* Show box bottom line on hover: */ border-bottom: 3px solid #7366f0; } @@ -31,7 +32,7 @@ h3 { .sensors > .grid-x > .cell:hover { /* Box hover annimation efect: */ transform: translateY(-8px) !important; - box-shadow: 0 1rem 3rem rgba(31,45,61,.125) !important; + box-shadow: 0 1rem 3rem rgba(31, 45, 61, 0.125) !important; } .sensors > .grid-x .type { @@ -62,36 +63,35 @@ time { outline: none; } - /* Overwrite the default Slider style: */ .iro__slider { display: none !important; } -#roundSlider .rs-range-color { - background-color: #33B5E5; +#roundSlider .rs-range-color { + background-color: #33b5e5; } -#roundSlider .rs-path-color { - background-color: #C2E9F7; +#roundSlider .rs-path-color { + background-color: #c2e9f7; } -#roundSlider .rs-handle { - background-color: #C2E9F7; +#roundSlider .rs-handle { + background-color: #c2e9f7; padding: 7px; - border: 2px solid #C2E9F7; + border: 2px solid #c2e9f7; } -#roundSlider .rs-handle.rs-focus { - border-color: #33B5E5; +#roundSlider .rs-handle.rs-focus { + border-color: #33b5e5; } -#roundSlider .rs-handle:after { - border-color: #33B5E5; - background-color: #33B5E5; +#roundSlider .rs-handle:after { + border-color: #33b5e5; + background-color: #33b5e5; } -#roundSlider .rs-border { +#roundSlider .rs-border { border-color: transparent; } @@ -104,10 +104,9 @@ time { } #roundSlider .rs-tooltip-text::after { - content: ' %'; + content: " %"; } - /* Style the "Panel" box icons: */ .panel-icons > .cell { cursor: pointer; @@ -130,10 +129,9 @@ time { margin: 30px 0; } - /* Style the "Switch brightness" button: */ .ks-cboxtags label { - border: 2px solid rgba(139, 139, 139, .3); + border: 2px solid rgba(139, 139, 139, 0.3); color: #adadad; border-radius: 25px; white-space: nowrap; @@ -144,7 +142,7 @@ time { -ms-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent; - transition: all .2s ease; + transition: all 0.2s ease; } .ks-cboxtags label { @@ -181,3 +179,27 @@ time { position: absolute; opacity: 0; } + +#temp-CO2-icon { + display: inline-block; + position: relative; + width: 1.5rem; + height: 1.5rem; +} + +#temp-icon, +#CO2-icon { + font-size: 50%; +} + +#CO2-icon { + position: absolute; + bottom: 0; + right: 0; +} + +#temp-icon { + position: absolute; + top: 0; + left: 0; +} From b2908bd2beb3f4abc3639518d85717013a877fa9 Mon Sep 17 00:00:00 2001 From: Maddie Date: Mon, 10 Feb 2020 09:00:19 +0100 Subject: [PATCH 2/6] Added weather station --- weather-station/background/config.js | 9 + weather-station/background/main.js | 358 ++++++++++++++++++++ weather-station/background/networkInfo.json | 68 ++++ weather-station/background/package.json | 12 + weather-station/foreground/index.html | 57 ++++ weather-station/foreground/index.js | 203 +++++++++++ weather-station/foreground/style.css | 23 ++ 7 files changed, 730 insertions(+) create mode 100644 weather-station/background/config.js create mode 100644 weather-station/background/main.js create mode 100644 weather-station/background/networkInfo.json create mode 100644 weather-station/background/package.json create mode 100644 weather-station/foreground/index.html create mode 100644 weather-station/foreground/index.js create mode 100644 weather-station/foreground/style.css diff --git a/weather-station/background/config.js b/weather-station/background/config.js new file mode 100644 index 0000000..7bab569 --- /dev/null +++ b/weather-station/background/config.js @@ -0,0 +1,9 @@ +const config = { + clientId: "your_client_id", + clientSecret: "your_client_secret", + deviceId: "your_device_id", + username: "your_username", + password: "your_password" +}; + +module.exports = config; diff --git a/weather-station/background/main.js b/weather-station/background/main.js new file mode 100644 index 0000000..68b1a13 --- /dev/null +++ b/weather-station/background/main.js @@ -0,0 +1,358 @@ +let Wappsto = require("wapp-api"); +let axios = require("axios"); +let config = require("./config"); +let networkInfo = require("./networkInfo.json"); + +let wappsto = new Wappsto({ + baseUrl: "https://wappsto.com/services", + session: "68c355e7-d255-4f4c-b912-d35b12f709c7" +}); + +let network, data; +let unreacheableDevices = []; +// Timer used for updating data +let updateTimer; +// 3 min +let timeInterval = 180000; + +let statusMessage = { + warning_user_login: + "Please login into your Netatmo account to grant permissions to this app", + success_user_granted_netatmo_data_access: "Permission granted", + error_user_denied_netatmo_data_access: + "Permission to access Netatmo account data denied", + success_retrieve_wappsto_data: "Succesfully retrieved Wappsto data", + error_retrieve_wappsto_data: "Failed to retrieve Wappsto data" +}; + +wappsto.get( + "data", + {}, + { + expand: 1, + subscribe: true, + success: function(collection) { + data = collection.first(); + + checkNetwork(); + }, + error: function(error) { + console.log(error); + } + } +); + +// Check if network exists; if not then create it +let checkNetwork = function() { + wappsto.get( + "network", + {}, + { + expand: 5, + subscribe: true, + success: function(collection) { + if (collection.length > 0) { + network = collection.first(); + + setUpdateTimer(); + } else { + network = createNetwork(); + // get the users station data with which to populate the network + getStationData(); + } + }, + error: function(error) { + console.log(error); + } + } + ); +}; + +// Update station data +let updateStationData = function() { + unreacheableDevices = []; + // Remove previous unreacheable devices + updateWappstoData({ lostDevices: unreacheableDevices }); + + if (updateTimer) { + clearInterval(updateTimer); + } + // Destroy existing network and then create new network using new data + network + .destroy() + .catch(function(error) { + // console.log(error); + }) + .then(function() { + network = createNetwork(); + + getStationData(); + }); +}; + +let setUpdateTimer = function() { + updateTimer = setInterval(function() { + updateStationData(); + }, timeInterval); +}; + +// Create and return network +let createNetwork = function() { + let newNetwork = new wappsto.models.Network(); + + newNetwork.set("name", networkInfo.name); + + return newNetwork; +}; + +// Create and return device +let createDevice = function(deviceData) { + let newDevice = new wappsto.models.Device(); + // If a device is not reachable it will be skipped + if (deviceData.reachable === false) { + unreacheableDevices.push(deviceData.module_name); + return null; + } + // Device type is used to differentiate between the Main Module and the other modules + // Thus the right attributes can be set for each case + if (deviceData.type === "NAMain") { + newDevice.set({ + name: deviceData.module_name, + description: networkInfo.device[0].description, + manufacturer: networkInfo.device[0].manufacturer, + communication: networkInfo.device[0].communication + }); + } else { + newDevice.set({ + name: deviceData.module_name, + description: "Module device", + manufacturer: networkInfo.device[0].manufacturer, + communication: networkInfo.device[0].communication + }); + } + return newDevice; +}; + +// Create and return device value +let createValue = function(dataType, device) { + let newValue = new wappsto.models.Value(); + + networkInfo.device[0].value.forEach(function(value) { + if (value.param === dataType) { + newValue.set({ + name: value.name, + type: value.type, + permission: value.permission, + dataType: value.dataType, + // all the values are of type number + number: { + min: value.min ? parseInt(value.min) : -999, + max: value.max ? parseInt(value.max) : 999, + step: value.step ? parseInt(value.step) : 1, + unit: value.unit + }, + description: value.description + }); + + if (newValue) { + // get the state data + let stateData = device.dashboard_data[value.param]; + // all the value permissions are of type Report + let reportState = createState("Report", stateData); + + newValue.get("state").push(reportState); + } + } + }); + return newValue; +}; + +// Create and return value state +let createState = function(type, data) { + let newState = new wappsto.models.State(); + + let timestamp = new Date().toISOString(); + + newState.set({ + type: type, + data: data + "", + timestamp: timestamp + "" + }); + + return newState; +}; + +// Save network and set update timer +let saveNetwork = function() { + network.save( + {}, + { + subscribe: true, + success: function() { + if (updateTimer) { + clearInterval(updateTimer); + } + + setUpdateTimer(); + }, + error: function(error) { + console.log(error); + } + } + ); +}; + +// Save and update data to wappsto data model +let updateWappstoData = function(dataToUpdate) { + data.set(dataToUpdate); + data.save(dataToUpdate, { + patch: true + }); +}; + +// By default, axios serializes JavaScript objects to JSON. +// To send data in the application/x-www-form-urlencoded format instead, use querystring to stringify nested objects! +const querystring = require("querystring"); + +// Get access token with client credentials grant type - use only for development and testing +let getAccessToken = function() { + axios({ + method: "POST", + headers: { + Host: "api.netatmo.com", + "Content-type": "application/x-www-form-urlencoded;charset=UTF-8" + }, + url: "/oauth2/token", + baseURL: "https://api.netatmo.com/", + data: querystring.stringify({ + grant_type: "password", + client_id: config.clientId, + client_secret: config.clientSecret, + username: config.username, + password: config.password, + scope: "read_station" + }) + }) + .then(function(response) { + updateWappstoData({ + accessToken: response.data.access_token, + refreshToken: response.data.refresh_token, + expiresIn: response.data.expires_in + }); + + getStationData(); + }) + .catch(function(error) { + console.log(error); + }); +}; + +// Send request to refresh token and update tokens with new values +let getRefreshToken = function() { + let refreshToken = data.get("refreshToken"); + + axios({ + method: "POST", + headers: { + Host: "api.netatmo.com", + "Content-type": "application/x-www-form-urlencoded;charset=UTF-8" + }, + url: "/oauth2/token", + baseURL: "https://api.netatmo.com/", + data: querystring.stringify({ + grant_type: "refresh_token", + refresh_token: refreshToken, + client_id: config.clientId, + client_secret: config.clientSecret + }) + }) + .then(function(response) { + updateWappstoData({ + accessToken: response.data.access_token, + refreshToken: response.data.refresh_token, + expiresIn: response.data.expires_in + }); + + getStationData(); + }) + .catch(function(error) { + console.log(error); + }); +}; + +// Use device data to create device, values and state and then add device to the network +let addDevicesToNetwork = function(deviceData) { + deviceData.forEach(function(device) { + let deviceToAdd = createDevice(device); + + if (deviceToAdd) { + let deviceDataTypes = device.data_type; + + deviceDataTypes.forEach(function(dataType) { + let valueToAdd = createValue(dataType, device); + + deviceToAdd.get("value").push(valueToAdd); + }); + network.get("device").push(deviceToAdd); + } + }); +}; + +// Get the users station data +let getStationData = function() { + let accessToken = data.get("accessToken"); + + if (!accessToken) { + getAccessToken(); + } + + axios({ + method: "GET", + headers: { + Host: "api.netatmo.com", + Authorization: "Bearer " + accessToken + }, + url: "/getstationsdata", + baseURL: "https://api.netatmo.com/api/", + data: querystring.stringify({ + device_id: config.deviceId, + get_favorites: false + }) + }) + .then(function(response) { + // Data of the Main Module - every station has this device + let deviceData = response.data.body.devices; + // Saving station name to display in the FG + let stationName = deviceData[0].station_name; + + if (data.get("stationName") !== stationName) { + updateWappstoData({ stationName: stationName }); + } + + addDevicesToNetwork(deviceData); + // Data of the modules associated with the Main Module + let moduleData = response.data.body.devices[0].modules; + + addDevicesToNetwork(moduleData); + // Saving network + saveNetwork(); + // Save unreachable devices if any + if (unreacheableDevices.length > 0) { + updateWappstoData({ + status_message: statusMessage.success_retrieve_wappsto_data, + lostDevices: unreacheableDevices + }); + } else { + updateWappstoData({ + status_message: statusMessage.success_retrieve_wappsto_data + }); + } + }) + .catch(function(error) { + updateWappstoData({ + status_message: statusMessage.error_retrieve_wappsto_data + }); + + getRefreshToken(); + }); +}; diff --git a/weather-station/background/networkInfo.json b/weather-station/background/networkInfo.json new file mode 100644 index 0000000..762c189 --- /dev/null +++ b/weather-station/background/networkInfo.json @@ -0,0 +1,68 @@ +{ + "name": "Netatmo Weather Station", + "device": [ + { + "name": "Indoor", + "description": "Main module - required module in all cases", + "manufacturer": "Netatmo", + "communication": "always", + "value": [ + { + "param": "Temperature", + "name": "Temperature", + "type": "temperature", + "permission": "r", + "dataType": "number", + "min": "-40", + "max": "65", + "step": "1", + "unit": "C" + }, + { + "param": "CO2", + "name": "CO2", + "type": "CO2", + "permission": "r", + "dataType": "number", + "min": "0", + "max": "5000", + "step": "1", + "unit": "ppm" + }, + { + "param": "Humidity", + "name": "Humidity", + "type": "humidity", + "permission": "r", + "dataType": "number", + "min": "0", + "max": "100", + "step": "1", + "unit": "%" + }, + { + "param": "Noise", + "name": "Noise", + "type": "noise", + "permission": "r", + "dataType": "number", + "min": "35", + "max": "120", + "step": "1", + "unit": "dB" + }, + { + "param": "Pressure", + "name": "Pressure", + "type": "pressure", + "permission": "r", + "dataType": "number", + "min": "260", + "max": "1260", + "step": "1", + "unit": "mbar" + } + ] + } + ] +} diff --git a/weather-station/background/package.json b/weather-station/background/package.json new file mode 100644 index 0000000..b2e9ccb --- /dev/null +++ b/weather-station/background/package.json @@ -0,0 +1,12 @@ +{ + "scripts": { + "start": "nodemon main.js" + }, + "dependencies": { + "axios": "0.18.0", + "wapp-api": "^1.0.5" + }, + "devDependencies": { + "nodemon": "^2.0.2" + } +} diff --git a/weather-station/foreground/index.html b/weather-station/foreground/index.html new file mode 100644 index 0000000..0da46bf --- /dev/null +++ b/weather-station/foreground/index.html @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + +
+
+

Netatmo Weather Station Converter

+

+ This Wapp converts your Netatmo Weather Station data into the Wappsto + unified data model. +

+
+
+

Options

+ + + +
+
+

+ Status +

+
Waiting for status update..
+
+
+

+
+
+
+ + diff --git a/weather-station/foreground/index.js b/weather-station/foreground/index.js new file mode 100644 index 0000000..04bfc67 --- /dev/null +++ b/weather-station/foreground/index.js @@ -0,0 +1,203 @@ +let client_id = "your_client_id"; + +let wappsto = new Wappsto(); +let data, network; +let devices = []; + +let showStatus; +let stationName; +let stationData; + +$(document).ready(function() { + showStatus = document.getElementById("statusId"); + stationName = document.getElementById("stationId"); + stationData = document.getElementById("stationData"); + + wappsto.get( + "data", + {}, + { + expand: 1, + subscribe: true, + success: function(collection) { + data = collection.first(); + + getNetwork(); + + data.on("change", function() { + getNetwork(); + }); + }, + error: function(error) { + console.log(error); + } + } + ); +}); + +let getNetwork = function() { + wappsto.get( + "network", + { name: "Netatmo Weather Station" }, + { + expand: 5, + subscribe: true, + success: function(collection) { + if (collection.length > 0) { + network = collection.first(); + + devices = network.get("device"); + console.log("updating data"); + updateData(); + } else { + showStatus.innerHTML = + "

Could not find Netatmo Weather Station network

"; + stationName.innerHTML = ""; + stationData.innerHTML = ""; + } + }, + error: function(error) { + console.log(error); + } + } + ); +}; + +let deleteExistingData = function() { + wappsto.get( + "network", + { name: "Netatmo Weather Station" }, + { + expand: 5, + subscribe: true, + success: function(collection) { + if (collection.length > 0) { + network = collection.first(); + + network.destroy().catch(function(error) { + // console.log(error); + }); + + showStatus.innerHTML = + "

Succesfully deleted existing data

"; + stationName.innerHTML = ""; + stationData.innerHTML = ""; + } + }, + error: function(error) { + console.log(error); + } + } + ); +}; + +let restart = function() { + getNetwork(); +}; + +let updateData = function() { + showStatus.innerHTML = ""; + status = data.get("status_message"); + showStatus.innerHTML = status + ""; + + if (status === "Succesfully retrieved Wappsto data") { + showStatus.innerHTML = "

" + status + "

"; + stationName.innerHTML = ""; + stationName.innerHTML = data.get("stationName") + " Station"; + stationData.innerHTML = ""; + if (devices) { + devices.forEach(function(device) { + stationData.innerHTML += "

" + device.get("name") + "

"; + device.get("value").forEach(function(value) { + let state = value + .get("state") + .find({ type: "Report" }) + .get("data"); + + let valueName = value.get("name"); + switch (valueName) { + case "Temperature": + stationData.innerHTML += + "

" + + value.get("name") + + " : " + + state + + " " + + value.get("number").unit + + "

"; + break; + case "CO2": + stationData.innerHTML += + "

" + + value.get("name") + + " : " + + state + + " " + + value.get("number").unit + + "

"; + break; + case "Humidity": + stationData.innerHTML += + "

" + + value.get("name") + + " : " + + state + + " " + + value.get("number").unit + + "

"; + break; + case "Noise": + stationData.innerHTML += + "

" + + value.get("name") + + " : " + + state + + " " + + value.get("number").unit + + "

"; + break; + case "Pressure": + stationData.innerHTML += + "

" + + value.get("name") + + " : " + + state + + " " + + value.get("number").unit + + "

"; + break; + default: + stationData.innerHTML += + "

" + + value.get("name") + + " : " + + state + + " " + + value.get("number").unit + + "

"; + } + }); + }); + + if (data.get("lostDevices")) { + let lostDevices = data.get("lostDevices"); + lostDevices.forEach(function(lostDevice) { + stationData.innerHTML += + "

" + lostDevice + " is unreachable

"; + }); + } + } + } +}; + +let openLoginPage = function() { + // needs redirect uri + let url = + "https://api.netatmo.com/oauth2/authorize?client_id=" + + client_id + + "&redirect_uri=http://localhost:3000&scope=read_station&state=seluxit"; + + window.open(url); + + //window.location.replace(url); +}; diff --git a/weather-station/foreground/style.css b/weather-station/foreground/style.css new file mode 100644 index 0000000..0ddadec --- /dev/null +++ b/weather-station/foreground/style.css @@ -0,0 +1,23 @@ +* { + font-family: sans-serif; +} + +body { + padding: 2rem; +} + +button { + margin-right: 1rem; +} + +.success { + border: solid thin green; + padding: 0.5rem; + color: darkgreen; +} + +.failure { + border: solid thin red; + padding: 0.5rem; + color: darkred; +} From 17c7f49b44ef43456b16dcd6f9d29b22f3954b3c Mon Sep 17 00:00:00 2001 From: Maddie Date: Mon, 24 Feb 2020 09:35:32 +0100 Subject: [PATCH 3/6] implement suggested changes --- smart-home-sensors/background/main.js | 115 +++++++++++------------ smart-home-sensors/foreground/index.html | 2 +- 2 files changed, 58 insertions(+), 59 deletions(-) diff --git a/smart-home-sensors/background/main.js b/smart-home-sensors/background/main.js index 64ea9e7..cd7013b 100644 --- a/smart-home-sensors/background/main.js +++ b/smart-home-sensors/background/main.js @@ -1,59 +1,68 @@ -let Wappsto = require("wapp-api"); +const Wappsto = require("wapp-api"); -let wappsto = new Wappsto(); +const wappsto = new Wappsto(); -let wappstoConsole = require("wapp-api/console"); +const wappstoConsole = require("wapp-api/console"); wappstoConsole.start(); let device, sensors, led, data; -wappsto - .get( - "network", - { - name: "Smart Home" - }, - { - quantity: 1, - expand: 5, - subscribe: true - } - ) - .then(function(collection) { +let getNetwork = async () => { + try { + const collection = await wappsto.get( + "network", + { + name: "Smart Home" + }, + { + quantity: 1, + expand: 5, + subscribe: true + } + ); device = collection.get("0.device"); - sensors = device.find({ name: "Sensors" }).get("value"); - led = device.find({ name: "LED" }).get("value"); - - initSensorListerners(); - initButtonListener(); - initBrightnessListener(); - }) - .catch(console.error); - -wappsto - .get( - "data", - {}, - { - expand: 5, - subscribe: true - } - ) - .then(function(collection) { + } catch (error) { + console.log(error); + } +}; + +getNetwork().then(() => { + sensors = device.find({ name: "Sensors" }).get("value"); + led = device.find({ name: "LED" }).get("value"); + + initSensorListerners(); + initButtonListener(); + initBrightnessListener(); +}); + +let getData = async () => { + try { + const collection = await wappsto.get( + "data", + {}, + { + expand: 5, + subscribe: true + } + ); data = collection.first(); + } catch (error) { + console.log(error); + } +}; - data.on("change", function() { - updateSensorData(data.get("sensorToUpdate")); - }); - }) - .catch(console.error); +getData().then(() => { + data.on("change", () => { + updateSensorData(data.get("sensorToUpdate")); + }); +}); function initSensorListerners() { if (sensors) { sensors.each(sensorValue => { let sensorState = sensorValue.get("state").find({ type: "Report" }); - sensorState.on("change:data", function() { + sensorState.on("change:data", () => { let currentlySelectedSensor = data.get("sensorToUpdate"); let sensorName = sensorValue.get("name"); @@ -81,21 +90,15 @@ function updateSensorData(sensorValueToBeUpdated) { .find({ type: "Report" }) .get("data"); - let CO2_value = sensors.find({ name: "CO2" }); - let CO2_data = CO2_value.get("state") + let co2_value = sensors.find({ name: "CO2" }); + let co2_data = co2_value + .get("state") .find({ type: "Report" }) .get("data"); panel.save( { - data: - +Number(temp_data).toFixed(2) + - " " + - temp_value.get("number.unit") + - " " + - +Number(CO2_data).toFixed(2) + - " " + - CO2_value.get("number.unit") + data: `${+Number(temp_data).toFixed(2)} ${temp_value.get("number.unit")} ${+Number(co2_data).toFixed(2)} ${co2_value.get("number.unit")}` }, { patch: true } ); @@ -109,11 +112,7 @@ function updateSensorData(sensorValueToBeUpdated) { .get("data"); panel.save({ - data: - +Number(data).toFixed(2) + - " " + - foundSensorValue.get("number.unit") + - " " + data: `${+Number(data).toFixed(2)} ${foundSensorValue.get("number.unit")}` }); } else { console.log("Sensor could not be found"); @@ -133,7 +132,7 @@ function initButtonListener() { .get("state") .find({ type: "Control" }); - button.on("change:data", function(model) { + button.on("change:data", model => { let isButtonOn = model.get("data"); let userBrightnessOption = data.get("brightnessOption"); let setting = 0; @@ -159,7 +158,7 @@ function initBrightnessListener() { .find({ type: "Report" }); let userBrightnessOption = data.get("brightnessOption"); - brightnessState.on("change:data", function() { + brightnessState.on("change:data", () => { let currentBrightnessOption = brightnessState.get("data"); let currentButtonOption = button.get("data"); diff --git a/smart-home-sensors/foreground/index.html b/smart-home-sensors/foreground/index.html index f0264b4..c9e9aa3 100644 --- a/smart-home-sensors/foreground/index.html +++ b/smart-home-sensors/foreground/index.html @@ -2,7 +2,7 @@ - + Smart Home From 6895bfec11ede096b357b33e9a5e758a852e616b Mon Sep 17 00:00:00 2001 From: Maddie Date: Mon, 24 Feb 2020 10:10:37 +0100 Subject: [PATCH 4/6] Revert "implement suggested changes" This reverts commit 17c7f49b44ef43456b16dcd6f9d29b22f3954b3c. --- smart-home-sensors/background/main.js | 115 ++++++++++++----------- smart-home-sensors/foreground/index.html | 2 +- 2 files changed, 59 insertions(+), 58 deletions(-) diff --git a/smart-home-sensors/background/main.js b/smart-home-sensors/background/main.js index cd7013b..64ea9e7 100644 --- a/smart-home-sensors/background/main.js +++ b/smart-home-sensors/background/main.js @@ -1,68 +1,59 @@ -const Wappsto = require("wapp-api"); +let Wappsto = require("wapp-api"); -const wappsto = new Wappsto(); +let wappsto = new Wappsto(); -const wappstoConsole = require("wapp-api/console"); +let wappstoConsole = require("wapp-api/console"); wappstoConsole.start(); let device, sensors, led, data; -let getNetwork = async () => { - try { - const collection = await wappsto.get( - "network", - { - name: "Smart Home" - }, - { - quantity: 1, - expand: 5, - subscribe: true - } - ); +wappsto + .get( + "network", + { + name: "Smart Home" + }, + { + quantity: 1, + expand: 5, + subscribe: true + } + ) + .then(function(collection) { device = collection.get("0.device"); - } catch (error) { - console.log(error); - } -}; - -getNetwork().then(() => { - sensors = device.find({ name: "Sensors" }).get("value"); - led = device.find({ name: "LED" }).get("value"); - - initSensorListerners(); - initButtonListener(); - initBrightnessListener(); -}); - -let getData = async () => { - try { - const collection = await wappsto.get( - "data", - {}, - { - expand: 5, - subscribe: true - } - ); + sensors = device.find({ name: "Sensors" }).get("value"); + led = device.find({ name: "LED" }).get("value"); + + initSensorListerners(); + initButtonListener(); + initBrightnessListener(); + }) + .catch(console.error); + +wappsto + .get( + "data", + {}, + { + expand: 5, + subscribe: true + } + ) + .then(function(collection) { data = collection.first(); - } catch (error) { - console.log(error); - } -}; -getData().then(() => { - data.on("change", () => { - updateSensorData(data.get("sensorToUpdate")); - }); -}); + data.on("change", function() { + updateSensorData(data.get("sensorToUpdate")); + }); + }) + .catch(console.error); function initSensorListerners() { if (sensors) { sensors.each(sensorValue => { let sensorState = sensorValue.get("state").find({ type: "Report" }); - sensorState.on("change:data", () => { + sensorState.on("change:data", function() { let currentlySelectedSensor = data.get("sensorToUpdate"); let sensorName = sensorValue.get("name"); @@ -90,15 +81,21 @@ function updateSensorData(sensorValueToBeUpdated) { .find({ type: "Report" }) .get("data"); - let co2_value = sensors.find({ name: "CO2" }); - let co2_data = co2_value - .get("state") + let CO2_value = sensors.find({ name: "CO2" }); + let CO2_data = CO2_value.get("state") .find({ type: "Report" }) .get("data"); panel.save( { - data: `${+Number(temp_data).toFixed(2)} ${temp_value.get("number.unit")} ${+Number(co2_data).toFixed(2)} ${co2_value.get("number.unit")}` + data: + +Number(temp_data).toFixed(2) + + " " + + temp_value.get("number.unit") + + " " + + +Number(CO2_data).toFixed(2) + + " " + + CO2_value.get("number.unit") }, { patch: true } ); @@ -112,7 +109,11 @@ function updateSensorData(sensorValueToBeUpdated) { .get("data"); panel.save({ - data: `${+Number(data).toFixed(2)} ${foundSensorValue.get("number.unit")}` + data: + +Number(data).toFixed(2) + + " " + + foundSensorValue.get("number.unit") + + " " }); } else { console.log("Sensor could not be found"); @@ -132,7 +133,7 @@ function initButtonListener() { .get("state") .find({ type: "Control" }); - button.on("change:data", model => { + button.on("change:data", function(model) { let isButtonOn = model.get("data"); let userBrightnessOption = data.get("brightnessOption"); let setting = 0; @@ -158,7 +159,7 @@ function initBrightnessListener() { .find({ type: "Report" }); let userBrightnessOption = data.get("brightnessOption"); - brightnessState.on("change:data", () => { + brightnessState.on("change:data", function() { let currentBrightnessOption = brightnessState.get("data"); let currentButtonOption = button.get("data"); diff --git a/smart-home-sensors/foreground/index.html b/smart-home-sensors/foreground/index.html index c9e9aa3..f0264b4 100644 --- a/smart-home-sensors/foreground/index.html +++ b/smart-home-sensors/foreground/index.html @@ -2,7 +2,7 @@ - Smart Home + From ce718100cac728937ab884dee111ed2cc9f6e6bf Mon Sep 17 00:00:00 2001 From: Maddie Date: Mon, 24 Feb 2020 10:12:23 +0100 Subject: [PATCH 5/6] Revert "Revert "implement suggested changes"" This reverts commit 6895bfec11ede096b357b33e9a5e758a852e616b. --- smart-home-sensors/background/main.js | 115 +++++++++++------------ smart-home-sensors/foreground/index.html | 2 +- 2 files changed, 58 insertions(+), 59 deletions(-) diff --git a/smart-home-sensors/background/main.js b/smart-home-sensors/background/main.js index 64ea9e7..cd7013b 100644 --- a/smart-home-sensors/background/main.js +++ b/smart-home-sensors/background/main.js @@ -1,59 +1,68 @@ -let Wappsto = require("wapp-api"); +const Wappsto = require("wapp-api"); -let wappsto = new Wappsto(); +const wappsto = new Wappsto(); -let wappstoConsole = require("wapp-api/console"); +const wappstoConsole = require("wapp-api/console"); wappstoConsole.start(); let device, sensors, led, data; -wappsto - .get( - "network", - { - name: "Smart Home" - }, - { - quantity: 1, - expand: 5, - subscribe: true - } - ) - .then(function(collection) { +let getNetwork = async () => { + try { + const collection = await wappsto.get( + "network", + { + name: "Smart Home" + }, + { + quantity: 1, + expand: 5, + subscribe: true + } + ); device = collection.get("0.device"); - sensors = device.find({ name: "Sensors" }).get("value"); - led = device.find({ name: "LED" }).get("value"); - - initSensorListerners(); - initButtonListener(); - initBrightnessListener(); - }) - .catch(console.error); - -wappsto - .get( - "data", - {}, - { - expand: 5, - subscribe: true - } - ) - .then(function(collection) { + } catch (error) { + console.log(error); + } +}; + +getNetwork().then(() => { + sensors = device.find({ name: "Sensors" }).get("value"); + led = device.find({ name: "LED" }).get("value"); + + initSensorListerners(); + initButtonListener(); + initBrightnessListener(); +}); + +let getData = async () => { + try { + const collection = await wappsto.get( + "data", + {}, + { + expand: 5, + subscribe: true + } + ); data = collection.first(); + } catch (error) { + console.log(error); + } +}; - data.on("change", function() { - updateSensorData(data.get("sensorToUpdate")); - }); - }) - .catch(console.error); +getData().then(() => { + data.on("change", () => { + updateSensorData(data.get("sensorToUpdate")); + }); +}); function initSensorListerners() { if (sensors) { sensors.each(sensorValue => { let sensorState = sensorValue.get("state").find({ type: "Report" }); - sensorState.on("change:data", function() { + sensorState.on("change:data", () => { let currentlySelectedSensor = data.get("sensorToUpdate"); let sensorName = sensorValue.get("name"); @@ -81,21 +90,15 @@ function updateSensorData(sensorValueToBeUpdated) { .find({ type: "Report" }) .get("data"); - let CO2_value = sensors.find({ name: "CO2" }); - let CO2_data = CO2_value.get("state") + let co2_value = sensors.find({ name: "CO2" }); + let co2_data = co2_value + .get("state") .find({ type: "Report" }) .get("data"); panel.save( { - data: - +Number(temp_data).toFixed(2) + - " " + - temp_value.get("number.unit") + - " " + - +Number(CO2_data).toFixed(2) + - " " + - CO2_value.get("number.unit") + data: `${+Number(temp_data).toFixed(2)} ${temp_value.get("number.unit")} ${+Number(co2_data).toFixed(2)} ${co2_value.get("number.unit")}` }, { patch: true } ); @@ -109,11 +112,7 @@ function updateSensorData(sensorValueToBeUpdated) { .get("data"); panel.save({ - data: - +Number(data).toFixed(2) + - " " + - foundSensorValue.get("number.unit") + - " " + data: `${+Number(data).toFixed(2)} ${foundSensorValue.get("number.unit")}` }); } else { console.log("Sensor could not be found"); @@ -133,7 +132,7 @@ function initButtonListener() { .get("state") .find({ type: "Control" }); - button.on("change:data", function(model) { + button.on("change:data", model => { let isButtonOn = model.get("data"); let userBrightnessOption = data.get("brightnessOption"); let setting = 0; @@ -159,7 +158,7 @@ function initBrightnessListener() { .find({ type: "Report" }); let userBrightnessOption = data.get("brightnessOption"); - brightnessState.on("change:data", function() { + brightnessState.on("change:data", () => { let currentBrightnessOption = brightnessState.get("data"); let currentButtonOption = button.get("data"); diff --git a/smart-home-sensors/foreground/index.html b/smart-home-sensors/foreground/index.html index f0264b4..c9e9aa3 100644 --- a/smart-home-sensors/foreground/index.html +++ b/smart-home-sensors/foreground/index.html @@ -2,7 +2,7 @@ - + Smart Home From a6e9f743beaf28372616780c41660e89e97f71b1 Mon Sep 17 00:00:00 2001 From: Maddie Date: Mon, 24 Feb 2020 10:17:06 +0100 Subject: [PATCH 6/6] Revert "Added weather station" This reverts commit b2908bd2beb3f4abc3639518d85717013a877fa9. --- weather-station/background/config.js | 9 - weather-station/background/main.js | 358 -------------------- weather-station/background/networkInfo.json | 68 ---- weather-station/background/package.json | 12 - weather-station/foreground/index.html | 57 ---- weather-station/foreground/index.js | 203 ----------- weather-station/foreground/style.css | 23 -- 7 files changed, 730 deletions(-) delete mode 100644 weather-station/background/config.js delete mode 100644 weather-station/background/main.js delete mode 100644 weather-station/background/networkInfo.json delete mode 100644 weather-station/background/package.json delete mode 100644 weather-station/foreground/index.html delete mode 100644 weather-station/foreground/index.js delete mode 100644 weather-station/foreground/style.css diff --git a/weather-station/background/config.js b/weather-station/background/config.js deleted file mode 100644 index 7bab569..0000000 --- a/weather-station/background/config.js +++ /dev/null @@ -1,9 +0,0 @@ -const config = { - clientId: "your_client_id", - clientSecret: "your_client_secret", - deviceId: "your_device_id", - username: "your_username", - password: "your_password" -}; - -module.exports = config; diff --git a/weather-station/background/main.js b/weather-station/background/main.js deleted file mode 100644 index 68b1a13..0000000 --- a/weather-station/background/main.js +++ /dev/null @@ -1,358 +0,0 @@ -let Wappsto = require("wapp-api"); -let axios = require("axios"); -let config = require("./config"); -let networkInfo = require("./networkInfo.json"); - -let wappsto = new Wappsto({ - baseUrl: "https://wappsto.com/services", - session: "68c355e7-d255-4f4c-b912-d35b12f709c7" -}); - -let network, data; -let unreacheableDevices = []; -// Timer used for updating data -let updateTimer; -// 3 min -let timeInterval = 180000; - -let statusMessage = { - warning_user_login: - "Please login into your Netatmo account to grant permissions to this app", - success_user_granted_netatmo_data_access: "Permission granted", - error_user_denied_netatmo_data_access: - "Permission to access Netatmo account data denied", - success_retrieve_wappsto_data: "Succesfully retrieved Wappsto data", - error_retrieve_wappsto_data: "Failed to retrieve Wappsto data" -}; - -wappsto.get( - "data", - {}, - { - expand: 1, - subscribe: true, - success: function(collection) { - data = collection.first(); - - checkNetwork(); - }, - error: function(error) { - console.log(error); - } - } -); - -// Check if network exists; if not then create it -let checkNetwork = function() { - wappsto.get( - "network", - {}, - { - expand: 5, - subscribe: true, - success: function(collection) { - if (collection.length > 0) { - network = collection.first(); - - setUpdateTimer(); - } else { - network = createNetwork(); - // get the users station data with which to populate the network - getStationData(); - } - }, - error: function(error) { - console.log(error); - } - } - ); -}; - -// Update station data -let updateStationData = function() { - unreacheableDevices = []; - // Remove previous unreacheable devices - updateWappstoData({ lostDevices: unreacheableDevices }); - - if (updateTimer) { - clearInterval(updateTimer); - } - // Destroy existing network and then create new network using new data - network - .destroy() - .catch(function(error) { - // console.log(error); - }) - .then(function() { - network = createNetwork(); - - getStationData(); - }); -}; - -let setUpdateTimer = function() { - updateTimer = setInterval(function() { - updateStationData(); - }, timeInterval); -}; - -// Create and return network -let createNetwork = function() { - let newNetwork = new wappsto.models.Network(); - - newNetwork.set("name", networkInfo.name); - - return newNetwork; -}; - -// Create and return device -let createDevice = function(deviceData) { - let newDevice = new wappsto.models.Device(); - // If a device is not reachable it will be skipped - if (deviceData.reachable === false) { - unreacheableDevices.push(deviceData.module_name); - return null; - } - // Device type is used to differentiate between the Main Module and the other modules - // Thus the right attributes can be set for each case - if (deviceData.type === "NAMain") { - newDevice.set({ - name: deviceData.module_name, - description: networkInfo.device[0].description, - manufacturer: networkInfo.device[0].manufacturer, - communication: networkInfo.device[0].communication - }); - } else { - newDevice.set({ - name: deviceData.module_name, - description: "Module device", - manufacturer: networkInfo.device[0].manufacturer, - communication: networkInfo.device[0].communication - }); - } - return newDevice; -}; - -// Create and return device value -let createValue = function(dataType, device) { - let newValue = new wappsto.models.Value(); - - networkInfo.device[0].value.forEach(function(value) { - if (value.param === dataType) { - newValue.set({ - name: value.name, - type: value.type, - permission: value.permission, - dataType: value.dataType, - // all the values are of type number - number: { - min: value.min ? parseInt(value.min) : -999, - max: value.max ? parseInt(value.max) : 999, - step: value.step ? parseInt(value.step) : 1, - unit: value.unit - }, - description: value.description - }); - - if (newValue) { - // get the state data - let stateData = device.dashboard_data[value.param]; - // all the value permissions are of type Report - let reportState = createState("Report", stateData); - - newValue.get("state").push(reportState); - } - } - }); - return newValue; -}; - -// Create and return value state -let createState = function(type, data) { - let newState = new wappsto.models.State(); - - let timestamp = new Date().toISOString(); - - newState.set({ - type: type, - data: data + "", - timestamp: timestamp + "" - }); - - return newState; -}; - -// Save network and set update timer -let saveNetwork = function() { - network.save( - {}, - { - subscribe: true, - success: function() { - if (updateTimer) { - clearInterval(updateTimer); - } - - setUpdateTimer(); - }, - error: function(error) { - console.log(error); - } - } - ); -}; - -// Save and update data to wappsto data model -let updateWappstoData = function(dataToUpdate) { - data.set(dataToUpdate); - data.save(dataToUpdate, { - patch: true - }); -}; - -// By default, axios serializes JavaScript objects to JSON. -// To send data in the application/x-www-form-urlencoded format instead, use querystring to stringify nested objects! -const querystring = require("querystring"); - -// Get access token with client credentials grant type - use only for development and testing -let getAccessToken = function() { - axios({ - method: "POST", - headers: { - Host: "api.netatmo.com", - "Content-type": "application/x-www-form-urlencoded;charset=UTF-8" - }, - url: "/oauth2/token", - baseURL: "https://api.netatmo.com/", - data: querystring.stringify({ - grant_type: "password", - client_id: config.clientId, - client_secret: config.clientSecret, - username: config.username, - password: config.password, - scope: "read_station" - }) - }) - .then(function(response) { - updateWappstoData({ - accessToken: response.data.access_token, - refreshToken: response.data.refresh_token, - expiresIn: response.data.expires_in - }); - - getStationData(); - }) - .catch(function(error) { - console.log(error); - }); -}; - -// Send request to refresh token and update tokens with new values -let getRefreshToken = function() { - let refreshToken = data.get("refreshToken"); - - axios({ - method: "POST", - headers: { - Host: "api.netatmo.com", - "Content-type": "application/x-www-form-urlencoded;charset=UTF-8" - }, - url: "/oauth2/token", - baseURL: "https://api.netatmo.com/", - data: querystring.stringify({ - grant_type: "refresh_token", - refresh_token: refreshToken, - client_id: config.clientId, - client_secret: config.clientSecret - }) - }) - .then(function(response) { - updateWappstoData({ - accessToken: response.data.access_token, - refreshToken: response.data.refresh_token, - expiresIn: response.data.expires_in - }); - - getStationData(); - }) - .catch(function(error) { - console.log(error); - }); -}; - -// Use device data to create device, values and state and then add device to the network -let addDevicesToNetwork = function(deviceData) { - deviceData.forEach(function(device) { - let deviceToAdd = createDevice(device); - - if (deviceToAdd) { - let deviceDataTypes = device.data_type; - - deviceDataTypes.forEach(function(dataType) { - let valueToAdd = createValue(dataType, device); - - deviceToAdd.get("value").push(valueToAdd); - }); - network.get("device").push(deviceToAdd); - } - }); -}; - -// Get the users station data -let getStationData = function() { - let accessToken = data.get("accessToken"); - - if (!accessToken) { - getAccessToken(); - } - - axios({ - method: "GET", - headers: { - Host: "api.netatmo.com", - Authorization: "Bearer " + accessToken - }, - url: "/getstationsdata", - baseURL: "https://api.netatmo.com/api/", - data: querystring.stringify({ - device_id: config.deviceId, - get_favorites: false - }) - }) - .then(function(response) { - // Data of the Main Module - every station has this device - let deviceData = response.data.body.devices; - // Saving station name to display in the FG - let stationName = deviceData[0].station_name; - - if (data.get("stationName") !== stationName) { - updateWappstoData({ stationName: stationName }); - } - - addDevicesToNetwork(deviceData); - // Data of the modules associated with the Main Module - let moduleData = response.data.body.devices[0].modules; - - addDevicesToNetwork(moduleData); - // Saving network - saveNetwork(); - // Save unreachable devices if any - if (unreacheableDevices.length > 0) { - updateWappstoData({ - status_message: statusMessage.success_retrieve_wappsto_data, - lostDevices: unreacheableDevices - }); - } else { - updateWappstoData({ - status_message: statusMessage.success_retrieve_wappsto_data - }); - } - }) - .catch(function(error) { - updateWappstoData({ - status_message: statusMessage.error_retrieve_wappsto_data - }); - - getRefreshToken(); - }); -}; diff --git a/weather-station/background/networkInfo.json b/weather-station/background/networkInfo.json deleted file mode 100644 index 762c189..0000000 --- a/weather-station/background/networkInfo.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "name": "Netatmo Weather Station", - "device": [ - { - "name": "Indoor", - "description": "Main module - required module in all cases", - "manufacturer": "Netatmo", - "communication": "always", - "value": [ - { - "param": "Temperature", - "name": "Temperature", - "type": "temperature", - "permission": "r", - "dataType": "number", - "min": "-40", - "max": "65", - "step": "1", - "unit": "C" - }, - { - "param": "CO2", - "name": "CO2", - "type": "CO2", - "permission": "r", - "dataType": "number", - "min": "0", - "max": "5000", - "step": "1", - "unit": "ppm" - }, - { - "param": "Humidity", - "name": "Humidity", - "type": "humidity", - "permission": "r", - "dataType": "number", - "min": "0", - "max": "100", - "step": "1", - "unit": "%" - }, - { - "param": "Noise", - "name": "Noise", - "type": "noise", - "permission": "r", - "dataType": "number", - "min": "35", - "max": "120", - "step": "1", - "unit": "dB" - }, - { - "param": "Pressure", - "name": "Pressure", - "type": "pressure", - "permission": "r", - "dataType": "number", - "min": "260", - "max": "1260", - "step": "1", - "unit": "mbar" - } - ] - } - ] -} diff --git a/weather-station/background/package.json b/weather-station/background/package.json deleted file mode 100644 index b2e9ccb..0000000 --- a/weather-station/background/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "scripts": { - "start": "nodemon main.js" - }, - "dependencies": { - "axios": "0.18.0", - "wapp-api": "^1.0.5" - }, - "devDependencies": { - "nodemon": "^2.0.2" - } -} diff --git a/weather-station/foreground/index.html b/weather-station/foreground/index.html deleted file mode 100644 index 0da46bf..0000000 --- a/weather-station/foreground/index.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - -
-
-

Netatmo Weather Station Converter

-

- This Wapp converts your Netatmo Weather Station data into the Wappsto - unified data model. -

-
-
-

Options

- - - -
-
-

- Status -

-
Waiting for status update..
-
-
-

-
-
-
- - diff --git a/weather-station/foreground/index.js b/weather-station/foreground/index.js deleted file mode 100644 index 04bfc67..0000000 --- a/weather-station/foreground/index.js +++ /dev/null @@ -1,203 +0,0 @@ -let client_id = "your_client_id"; - -let wappsto = new Wappsto(); -let data, network; -let devices = []; - -let showStatus; -let stationName; -let stationData; - -$(document).ready(function() { - showStatus = document.getElementById("statusId"); - stationName = document.getElementById("stationId"); - stationData = document.getElementById("stationData"); - - wappsto.get( - "data", - {}, - { - expand: 1, - subscribe: true, - success: function(collection) { - data = collection.first(); - - getNetwork(); - - data.on("change", function() { - getNetwork(); - }); - }, - error: function(error) { - console.log(error); - } - } - ); -}); - -let getNetwork = function() { - wappsto.get( - "network", - { name: "Netatmo Weather Station" }, - { - expand: 5, - subscribe: true, - success: function(collection) { - if (collection.length > 0) { - network = collection.first(); - - devices = network.get("device"); - console.log("updating data"); - updateData(); - } else { - showStatus.innerHTML = - "

Could not find Netatmo Weather Station network

"; - stationName.innerHTML = ""; - stationData.innerHTML = ""; - } - }, - error: function(error) { - console.log(error); - } - } - ); -}; - -let deleteExistingData = function() { - wappsto.get( - "network", - { name: "Netatmo Weather Station" }, - { - expand: 5, - subscribe: true, - success: function(collection) { - if (collection.length > 0) { - network = collection.first(); - - network.destroy().catch(function(error) { - // console.log(error); - }); - - showStatus.innerHTML = - "

Succesfully deleted existing data

"; - stationName.innerHTML = ""; - stationData.innerHTML = ""; - } - }, - error: function(error) { - console.log(error); - } - } - ); -}; - -let restart = function() { - getNetwork(); -}; - -let updateData = function() { - showStatus.innerHTML = ""; - status = data.get("status_message"); - showStatus.innerHTML = status + ""; - - if (status === "Succesfully retrieved Wappsto data") { - showStatus.innerHTML = "

" + status + "

"; - stationName.innerHTML = ""; - stationName.innerHTML = data.get("stationName") + " Station"; - stationData.innerHTML = ""; - if (devices) { - devices.forEach(function(device) { - stationData.innerHTML += "

" + device.get("name") + "

"; - device.get("value").forEach(function(value) { - let state = value - .get("state") - .find({ type: "Report" }) - .get("data"); - - let valueName = value.get("name"); - switch (valueName) { - case "Temperature": - stationData.innerHTML += - "

" + - value.get("name") + - " : " + - state + - " " + - value.get("number").unit + - "

"; - break; - case "CO2": - stationData.innerHTML += - "

" + - value.get("name") + - " : " + - state + - " " + - value.get("number").unit + - "

"; - break; - case "Humidity": - stationData.innerHTML += - "

" + - value.get("name") + - " : " + - state + - " " + - value.get("number").unit + - "

"; - break; - case "Noise": - stationData.innerHTML += - "

" + - value.get("name") + - " : " + - state + - " " + - value.get("number").unit + - "

"; - break; - case "Pressure": - stationData.innerHTML += - "

" + - value.get("name") + - " : " + - state + - " " + - value.get("number").unit + - "

"; - break; - default: - stationData.innerHTML += - "

" + - value.get("name") + - " : " + - state + - " " + - value.get("number").unit + - "

"; - } - }); - }); - - if (data.get("lostDevices")) { - let lostDevices = data.get("lostDevices"); - lostDevices.forEach(function(lostDevice) { - stationData.innerHTML += - "

" + lostDevice + " is unreachable

"; - }); - } - } - } -}; - -let openLoginPage = function() { - // needs redirect uri - let url = - "https://api.netatmo.com/oauth2/authorize?client_id=" + - client_id + - "&redirect_uri=http://localhost:3000&scope=read_station&state=seluxit"; - - window.open(url); - - //window.location.replace(url); -}; diff --git a/weather-station/foreground/style.css b/weather-station/foreground/style.css deleted file mode 100644 index 0ddadec..0000000 --- a/weather-station/foreground/style.css +++ /dev/null @@ -1,23 +0,0 @@ -* { - font-family: sans-serif; -} - -body { - padding: 2rem; -} - -button { - margin-right: 1rem; -} - -.success { - border: solid thin green; - padding: 0.5rem; - color: darkgreen; -} - -.failure { - border: solid thin red; - padding: 0.5rem; - color: darkred; -}