├── .github
└── workflows
│ └── npmpublish.yml
├── .gitignore
├── README.md
├── config.html
├── config.js
├── device-node.html
├── device-node.js
├── package-lock.json
├── package.json
└── run-on-change.py
/.github/workflows/npmpublish.yml:
--------------------------------------------------------------------------------
1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
3 |
4 | name: Node.js Package
5 |
6 | on: push
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 | - uses: actions/setup-node@v1
14 | with:
15 | node-version: 12
16 | - name: Set Git Environment and Update package number
17 | run: |
18 | git config --global user.name 'GIT Package Updater'
19 | git config --global user.email 'razorRun@users.noreply.github.com'
20 | npm version patch
21 | git push
22 | - run: npm ci
23 |
24 | publish-npm:
25 | needs: build
26 | runs-on: ubuntu-latest
27 | steps:
28 | - uses: actions/checkout@v2
29 | - uses: actions/setup-node@v1
30 | with:
31 | node-version: 12
32 | registry-url: https://registry.npmjs.org/
33 | - run: npm ci
34 | - run: npm publish
35 | env:
36 | NODE_AUTH_TOKEN: ${{secrets.npm_token}}
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # README
2 |
3 | ### Quick summary -
4 |
5 | This npm module will allow you to link up Smartlife and Tuya devices with node red via a **cloud service without any firmware changes**.
6 |
7 | ### How do I get set up?
8 |
9 | ### Please DO NOT use support email in app store to reach for support. Log issues and support requests on github.
10 |
11 | 1. NOTE: This plugin is only conncted to our US DB, So please choose US (see the list below) on the app when you register. Enroll the devices to SmartLife AIR APP (This is a separate app but you can use all the devices that works with tuya and smart life. If your device is not working or does not show inputs/outputs, try updating the firmware from app-> device -> update firmware)
12 |
13 | - Download SmartLife AIR APP from Smartlife site, [One of the leading home automation companies in New Zealand](https://www.smartlife.nz/smartlife-labs).
14 |
15 | 2. Create an account under https://smartlife-air-plugins.smartlife.nz/ and add your SmartLife air credentials.
16 | 3. Install NodeRed node and add your https://smartlife-air-plugins.smartlife.nz/ Login credentials.
17 | 4. Choose the device, input and output. (Please change a status before you select the device, Backend has to receive at least one update in order to pop up in the list).
18 | 5. If you want to get last status without sending a command, you can do that by sending string "?" as a input
19 |
20 | ### More on Country limitation
21 |
22 | There is one limitation on this plugin due to the server location. This node-red plugin only talks to US DB. That’s mean you will have to **choose a country that belongs to US region when you sign up to the mobile app**. Unfortunately, if you have already created an account you will have to create a new one and enrol devices again.
23 |
24 | **Countries in the US region**
25 | Canada, The United States, Peru, Mexico, Argentina, Brazil, Chile, Colombia, Venezuela, Malaysia, Indonesia, Philippines, New Zealand, Thailand, Japan, Korea, Vietnam, Myanmar(Burma), Guatemala, Bolivia, Ecuador, Paraguay, Suriname, Uruguay, Hongkong SAR China, Macao SAR China, Taiwan China, Puerto Rico, Curacao
26 |
27 | ### Contribution guidelines
28 |
29 | - Writing tests TODO
30 | - Code review
31 | - Other guidelines
32 |
33 | * Author - Roshan Milinda -> [roshan.digital](https://roshan.digital/)
34 |
35 | ### license GPL (GPLv3)
36 |
--------------------------------------------------------------------------------
/config.html:
--------------------------------------------------------------------------------
1 |
14 |
15 |
29 |
30 |
45 |
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(RED) {
2 | "use strict";
3 | var firebase = require("firebase/app");
4 |
5 | // Add the Firebase
6 | require("firebase/auth");
7 | require("firebase/database");
8 |
9 | const firebaseConfig = {
10 | apiKey: "AIzaSyBnbSCjJ9eQtAMl_CHRSJxelF5XG8UlLjU",
11 | authDomain: "smartlife-air.firebaseapp.com",
12 | databaseURL: "https://smartlife-air.firebaseio.com",
13 | projectId: "smartlife-air",
14 | storageBucket: "smartlife-air.appspot.com",
15 | messagingSenderId: "121967033816",
16 | appId: "1:121967033816:web:222bbc68de21111ac0c6ad",
17 | measurementId: "G-YHBTVPG19R"
18 | };
19 |
20 | function loginNode(n) {
21 | RED.nodes.createNode(this, n);
22 |
23 | // Initialize Firebase
24 | if (!firebase.apps.length) {
25 | firebase.initializeApp(firebaseConfig);
26 | }
27 | if (n.email && n.password) {
28 | this.auth = firebase.auth();
29 | this.db = firebase.database();
30 | let retry = true;
31 | const login = ()=>{
32 | if(retry)
33 | this.auth.signInWithEmailAndPassword(n.email, n.password).catch(error => {
34 | // Handle Errors here.
35 | console.log("Smartlife Air: Auth Error From Config Node")
36 | console.log(error.message);
37 | retry = true;
38 | setTimeout(() => {
39 | login();
40 | }, 10000);
41 | });
42 | }
43 | this.auth.onAuthStateChanged(function(user) {
44 | if (user) {
45 | retry = false;
46 | } else {
47 | retry = true;
48 | login();
49 | }
50 | });
51 | }
52 | }
53 | RED.nodes.registerType("smartlifeair-login", loginNode);
54 | };
55 |
--------------------------------------------------------------------------------
/device-node.html:
--------------------------------------------------------------------------------
1 |
38 |
39 |
109 |
110 |
123 |
124 |
125 |
126 |
127 |
128 |
307 |
--------------------------------------------------------------------------------
/device-node.js:
--------------------------------------------------------------------------------
1 | module.exports = function(RED) {
2 | let lastOutPut = {};
3 | let auth = false;
4 | function deviceNode(config) {
5 | try {
6 | RED.nodes.createNode(this, config);
7 | const node = this;
8 | const configNode = RED.nodes.getNode(config.account);
9 |
10 | let outputPath = {};
11 |
12 | node.on("input", msg => {
13 | if (auth) {
14 | inputHandler(msg.payload);
15 | } else {
16 | node.warn("Authentication Pending or error in user credentials. - On User input");
17 | }
18 | });
19 |
20 | node.on("close", () => {
21 | if (outputPath) outputPath.off();
22 | });
23 |
24 | // Subscribe after 5 seconds
25 | setTimeout(() => {
26 | if (configNode && configNode.auth && configNode.db) {
27 | configNode.auth.onAuthStateChanged(user => {
28 | if (user) {
29 | // User is signed in.
30 | logWarn("User Authenticated as " + user.email);
31 | auth = true;
32 | if (config.selectedOutput) {
33 | if (config.selectedOutput == "root") {
34 | outputPath = configNode.db.ref(
35 | "/devices/" + config.selectedDevice + "/status"
36 | );
37 | } else {
38 | outputPath = configNode.db.ref(
39 | "/devices/" +
40 | config.selectedDevice +
41 | "/status/" +
42 | config.selectedOutput
43 | );
44 | }
45 | //subscribe to output path
46 | node.subscriber = outputPath.on("value", data => {
47 | let payload = {};
48 | if (config.selectedOutput == "root") {
49 | Object.keys(data.val()).forEach(code => {
50 | payload[code] = data.val()[code].value;
51 | });
52 | } else {
53 | payload = outputConverter(
54 | data.val().value,
55 | config.outputConversion
56 | );
57 | }
58 | lastOutPut = payload;
59 | node.send({
60 | payload: payload,
61 | completeData: data.val()
62 | });
63 | });
64 | }
65 | } else {
66 | // User is signed out.
67 | node.warn("Authentication Pending or error in user credentials. - On Startup");
68 | console.log("Smartlife Air: Auth Object")
69 | console.log(JSON.stringify(configNode.auth))
70 | auth = false;
71 | }
72 | });
73 | }
74 | }, 5000);
75 |
76 | function logWarn(msg){
77 | if(config.enabledDebugMsg === "true" || config.enabledDebugMsg === true){ // "WTF: NR keep passing string and Bool cov doesnt work To check later"
78 | node.warn(msg);
79 | }
80 | }
81 |
82 | function inputHandler(inputValue) {
83 | if (inputValue === "?"){
84 | node.send({
85 | payload:lastOutPut
86 | })
87 | }
88 | else {
89 | if (config.selectedDevice && config.selectedInput) {
90 | const inputPath = configNode.db.ref(
91 | "/devices/" + config.selectedDevice + "/commands"
92 | );
93 | console.log('Smartlife Air: Value Update Started'+ config.selectedDevice +' '+ config.selectedInput +' '+ inputValue);
94 | inputPath.set({
95 | commandCode: config.selectedInput,
96 | value: inputValue
97 | }).then(function() {
98 | console.log('Smartlife Air: Value Update Finished'+ config.selectedDevice +' '+ config.selectedInput +' '+ inputValue);
99 | }).catch(function(error) {
100 | console.log('Smartlife Air: Cannot connect to the server');
101 | console.log('Smartlife Air: Value Update Failed '+ config.selectedDevice +' '+ config.selectedInput +' '+ inputValue);
102 | });
103 |
104 | } else {
105 | logWarn("Please select a device and an input channel");
106 | }
107 | }
108 | }
109 | } catch (error) {
110 | node.warn(error);
111 | }
112 | }
113 |
114 | RED.nodes.registerType("device-node", deviceNode);
115 | };
116 |
117 | function outputConverter(input, enable) {
118 | if (!JSON.parse(enable)) {
119 | return input;
120 | }
121 | switch (input) {
122 | case true:
123 | return "ON";
124 | case false:
125 | return "OFF";
126 | default:
127 | return input;
128 | }
129 | }
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-red-contrib-smartlifeair",
3 | "version": "1.0.13",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@firebase/analytics": {
8 | "version": "0.2.12",
9 | "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.2.12.tgz",
10 | "integrity": "sha512-y/WwSNUOPJnAdYOxD+NCMwVbhfP/yrQrnGTn1zAAM1R8bBBmWay8HpHki7g5N3qQ+AQ+uMF6tKbp/JnJkini7A==",
11 | "requires": {
12 | "@firebase/analytics-types": "0.2.5",
13 | "@firebase/component": "0.1.4",
14 | "@firebase/installations": "0.4.1",
15 | "@firebase/util": "0.2.39",
16 | "tslib": "1.10.0"
17 | }
18 | },
19 | "@firebase/analytics-types": {
20 | "version": "0.2.5",
21 | "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.2.5.tgz",
22 | "integrity": "sha512-aa746gTiILMn9TPBJXaYhYqnCL4CQwd4aYTAZseI9RZ/hf117xJTNy9/ZTmG5gl2AqxV0LgtdHYqKAjRlNqPIQ=="
23 | },
24 | "@firebase/app": {
25 | "version": "0.5.3",
26 | "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.5.3.tgz",
27 | "integrity": "sha512-cQn8eQSJRMpyIRfBi2roIiw0weAorpdJ2Ssn7yDlkMo0ZSE56MgMSlX1mcDupXgNZtG+k3E+IEr+FfzD9SQBGg==",
28 | "requires": {
29 | "@firebase/app-types": "0.5.0",
30 | "@firebase/component": "0.1.4",
31 | "@firebase/logger": "0.1.34",
32 | "@firebase/util": "0.2.39",
33 | "dom-storage": "2.1.0",
34 | "tslib": "1.10.0",
35 | "xmlhttprequest": "1.8.0"
36 | }
37 | },
38 | "@firebase/app-types": {
39 | "version": "0.5.0",
40 | "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.5.0.tgz",
41 | "integrity": "sha512-8j+vCXTpAkYGcFk86mPZ90V6HMFmn196RIEW9Opi0PN+VrPFC1l/eW0gptM8v7VXaQhECOxws3TN2g+dDaeSYA=="
42 | },
43 | "@firebase/auth": {
44 | "version": "0.13.4",
45 | "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.13.4.tgz",
46 | "integrity": "sha512-dFDuLMHHmigs9ZH56h+UO78SMuvYkwRcPbEudaemYisGLXnYFa0pUKS2WfvA/9d/14X/VnzG8NGApAw5BylpvA==",
47 | "requires": {
48 | "@firebase/auth-types": "0.9.4"
49 | }
50 | },
51 | "@firebase/auth-interop-types": {
52 | "version": "0.1.1",
53 | "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.1.tgz",
54 | "integrity": "sha512-rNpCOyCspZvNDoQVQLQQgWAGBMB2ClCWKN1c8cEFgLNFgnMJrjVB+tcL7KW2q2UjKa7l8Mxgwys7szTiEDAcvA=="
55 | },
56 | "@firebase/auth-types": {
57 | "version": "0.9.4",
58 | "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.9.4.tgz",
59 | "integrity": "sha512-06ZrpYz1GaUfIJs7C3Yf4lARH8+2kzgKfgG/9B3FaGHFYLa5U7rLBGGaca4oiVI12jmhe9CV3+M8e3U2CRCr2w=="
60 | },
61 | "@firebase/component": {
62 | "version": "0.1.4",
63 | "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.1.4.tgz",
64 | "integrity": "sha512-k3JZFUyHnSWC/7v+x+pIHLDNJPYA6xd7nqrQASOXH5TXhCR9meg0VsnJb+knD18491iRMKJnQWNSHdqPK9AX5w==",
65 | "requires": {
66 | "@firebase/util": "0.2.39",
67 | "tslib": "1.10.0"
68 | }
69 | },
70 | "@firebase/database": {
71 | "version": "0.5.20",
72 | "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.5.20.tgz",
73 | "integrity": "sha512-31dNLqMW4nGrGzIDS5hh+1LFzkr/m1Kb+EcftQGC3NaGC3zHwGyG7ijn+Xo7gIWtXukvJvm1cFC7R+eOCPEejw==",
74 | "requires": {
75 | "@firebase/auth-interop-types": "0.1.1",
76 | "@firebase/component": "0.1.4",
77 | "@firebase/database-types": "0.4.10",
78 | "@firebase/logger": "0.1.34",
79 | "@firebase/util": "0.2.39",
80 | "faye-websocket": "0.11.3",
81 | "tslib": "1.10.0"
82 | }
83 | },
84 | "@firebase/database-types": {
85 | "version": "0.4.10",
86 | "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.4.10.tgz",
87 | "integrity": "sha512-66puLsckt5HASgRN3CfhLn2iuGrgCjfH3u17OL0f5MtEweYLx+yW2QW5d539Wx30xD4B+INEdaRetw6xEa9t7g==",
88 | "requires": {
89 | "@firebase/app-types": "0.5.0"
90 | }
91 | },
92 | "@firebase/firestore": {
93 | "version": "1.10.0",
94 | "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-1.10.0.tgz",
95 | "integrity": "sha512-m7RjiEmACg7BHZdAgWKEE5NGbXrc56cQeVqh1ptRNTjPZG0W0Ygtp4hmU5k9FuN9JogIZqBqcXRmRQcq9qOkNw==",
96 | "requires": {
97 | "@firebase/component": "0.1.4",
98 | "@firebase/firestore-types": "1.9.0",
99 | "@firebase/logger": "0.1.34",
100 | "@firebase/util": "0.2.39",
101 | "@firebase/webchannel-wrapper": "0.2.35",
102 | "@grpc/proto-loader": "^0.5.0",
103 | "grpc": "1.24.2",
104 | "tslib": "1.10.0"
105 | }
106 | },
107 | "@firebase/firestore-types": {
108 | "version": "1.9.0",
109 | "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-1.9.0.tgz",
110 | "integrity": "sha512-UOtneGMUTLr58P56Y0GT/c4ZyC39vOoRAzgwad4PIsyc7HlKShbHKJpyys/LdlUYIsQdy/2El3Qy1veiBQ+ZJg=="
111 | },
112 | "@firebase/functions": {
113 | "version": "0.4.31",
114 | "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.4.31.tgz",
115 | "integrity": "sha512-6AiAoABRaFb0M0MMIHDXfbvNVduKNdLAGG+6FtMNuUzFbWiLLKsPgFy0jMkF874z7eIDT4XhZLNAasFVor/alw==",
116 | "requires": {
117 | "@firebase/component": "0.1.4",
118 | "@firebase/functions-types": "0.3.13",
119 | "@firebase/messaging-types": "0.4.1",
120 | "isomorphic-fetch": "2.2.1",
121 | "tslib": "1.10.0"
122 | }
123 | },
124 | "@firebase/functions-types": {
125 | "version": "0.3.13",
126 | "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.3.13.tgz",
127 | "integrity": "sha512-wD075tCmZo+t/GHs5xMhi3irwjbc6SWnjXAIHjuNhVDKic5gQNkHH5QnxX930WPrPhD0RV9wuSP15fy9T1mvjw=="
128 | },
129 | "@firebase/installations": {
130 | "version": "0.4.1",
131 | "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.4.1.tgz",
132 | "integrity": "sha512-rZj3dce54YkKHLJpSvxtf+OWF1/yaQe3jRWt5Fy70XTTYv60RM5DkjbbjL7ItflBMzXZCVUTiBUogFWN4Y1LVg==",
133 | "requires": {
134 | "@firebase/component": "0.1.4",
135 | "@firebase/installations-types": "0.3.0",
136 | "@firebase/util": "0.2.39",
137 | "idb": "3.0.2",
138 | "tslib": "1.10.0"
139 | }
140 | },
141 | "@firebase/installations-types": {
142 | "version": "0.3.0",
143 | "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.3.0.tgz",
144 | "integrity": "sha512-1W82H1F4WfuWjftMiWLNUTy1w2SD7svn8/U8k6T/CJSnzkET6m+3pPt3Q4FDI6E2zOgU8ZVGWm9IZ4DK84mP/A=="
145 | },
146 | "@firebase/logger": {
147 | "version": "0.1.34",
148 | "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.1.34.tgz",
149 | "integrity": "sha512-J2h6ylpd1IcuonRM3HBdXThitds6aQSIeoPYRPvApSFy82NhFPKRzJlflAhlQWjJOh59/jyQBGWJNxCL6fp4hw=="
150 | },
151 | "@firebase/messaging": {
152 | "version": "0.6.3",
153 | "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.6.3.tgz",
154 | "integrity": "sha512-PgkKsJwErLDsCqrcFSaLgFH/oXzMcwBbNN7HyAXTsWxUwhBbG7c5xtGRGd21F82EEmXcFl3VU/Y/kyYuXskrbQ==",
155 | "requires": {
156 | "@firebase/component": "0.1.4",
157 | "@firebase/installations": "0.4.1",
158 | "@firebase/messaging-types": "0.4.1",
159 | "@firebase/util": "0.2.39",
160 | "idb": "3.0.2",
161 | "tslib": "1.10.0"
162 | }
163 | },
164 | "@firebase/messaging-types": {
165 | "version": "0.4.1",
166 | "resolved": "https://registry.npmjs.org/@firebase/messaging-types/-/messaging-types-0.4.1.tgz",
167 | "integrity": "sha512-z2ki1nIE8TYH9LiXdozEzrPi9Cfckh9/x7HbDfj5KoVFYYvwLndUczstpKm2hvAUD3GuJF0JRUuxMHpJ6pwqzQ=="
168 | },
169 | "@firebase/performance": {
170 | "version": "0.2.31",
171 | "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.2.31.tgz",
172 | "integrity": "sha512-vg60k0wnMeTRhW8myOiJPn8vuVHlusnhg2YlN0PlH2epvzUPUv9bAeYVC6/XESORxmBmqUzfMsAaD3oCAQ8boQ==",
173 | "requires": {
174 | "@firebase/component": "0.1.4",
175 | "@firebase/installations": "0.4.1",
176 | "@firebase/logger": "0.1.34",
177 | "@firebase/performance-types": "0.0.8",
178 | "@firebase/util": "0.2.39",
179 | "tslib": "1.10.0"
180 | }
181 | },
182 | "@firebase/performance-types": {
183 | "version": "0.0.8",
184 | "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.0.8.tgz",
185 | "integrity": "sha512-3RK15Bct9PRdr0YBdbZV2KTi5yrPzscJVEsdnsLHuwXBntqlQaouQqwlCNFn25dtCMY0cgV/yiaFmuo13mbJ8A=="
186 | },
187 | "@firebase/polyfill": {
188 | "version": "0.3.31",
189 | "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.3.31.tgz",
190 | "integrity": "sha512-7XItMz50tdba57tCOTCSH8REvHYbrTU7MBOksnNZ3td/J9W/RkCPcLVSSnFWNmn0Jv1aufpUevryX1J4DZ/oiw==",
191 | "requires": {
192 | "core-js": "3.6.2",
193 | "promise-polyfill": "8.1.3",
194 | "whatwg-fetch": "2.0.4"
195 | },
196 | "dependencies": {
197 | "whatwg-fetch": {
198 | "version": "2.0.4",
199 | "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz",
200 | "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng=="
201 | }
202 | }
203 | },
204 | "@firebase/remote-config": {
205 | "version": "0.1.12",
206 | "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.1.12.tgz",
207 | "integrity": "sha512-L8Qr2waj5NjgWYsjjhvMmSnUaav4LPdrshdz/l/QPBCkNpF5+hIxbeG2f8609D5brcQVnG7uR4lcWfkzl+zsfQ==",
208 | "requires": {
209 | "@firebase/component": "0.1.4",
210 | "@firebase/installations": "0.4.1",
211 | "@firebase/logger": "0.1.34",
212 | "@firebase/remote-config-types": "0.1.5",
213 | "@firebase/util": "0.2.39",
214 | "tslib": "1.10.0"
215 | }
216 | },
217 | "@firebase/remote-config-types": {
218 | "version": "0.1.5",
219 | "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.1.5.tgz",
220 | "integrity": "sha512-1JR0XGVN0dNKJlu5sMYh0qL0jC85xNgXfUquUGNHhy9lH3++t1gD91MeiDBgxI73oFQR7PEPeu+CTeDS0g8lWQ=="
221 | },
222 | "@firebase/storage": {
223 | "version": "0.3.25",
224 | "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.3.25.tgz",
225 | "integrity": "sha512-0dgfrbPuwFDMOsC3VeENSt4L5bTsLq/5spdDn/Cjj57T0lVRaXipmgD09y8CJ0/SYPfiRoxmMWKCMnVNeSDL/g==",
226 | "requires": {
227 | "@firebase/component": "0.1.4",
228 | "@firebase/storage-types": "0.3.8",
229 | "@firebase/util": "0.2.39",
230 | "tslib": "1.10.0"
231 | }
232 | },
233 | "@firebase/storage-types": {
234 | "version": "0.3.8",
235 | "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.3.8.tgz",
236 | "integrity": "sha512-F0ED2WZaGjhjEdOk85c/1ikDQdWM1NiATFuTmRsaGYZyoERiwh/Mr6FnjqnLIeiJZqa6v2hk/aUgKosXjMWH/Q=="
237 | },
238 | "@firebase/util": {
239 | "version": "0.2.39",
240 | "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.2.39.tgz",
241 | "integrity": "sha512-hxbQJ9TkFzd6g8/ZcWBjdrxjxS0jYnR1EN3i1ah7i3KtvuxAtwNJ04YRf0QhKhCoitTkJ1Yn3cGb0kFnGtJVRA==",
242 | "requires": {
243 | "tslib": "1.10.0"
244 | }
245 | },
246 | "@firebase/webchannel-wrapper": {
247 | "version": "0.2.35",
248 | "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.2.35.tgz",
249 | "integrity": "sha512-7njiGBbFW0HCnuKNEJLcQt9EjfOzG8EJiXlFJwA3XfgiFxPVHmXrcF4d5yold2wfiwCwrXpeNTGZ854oRr6Hcw=="
250 | },
251 | "@grpc/proto-loader": {
252 | "version": "0.5.3",
253 | "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.3.tgz",
254 | "integrity": "sha512-8qvUtGg77G2ZT2HqdqYoM/OY97gQd/0crSG34xNmZ4ZOsv3aQT/FQV9QfZPazTGna6MIoyUd+u6AxsoZjJ/VMQ==",
255 | "requires": {
256 | "lodash.camelcase": "^4.3.0",
257 | "protobufjs": "^6.8.6"
258 | }
259 | },
260 | "@protobufjs/aspromise": {
261 | "version": "1.1.2",
262 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
263 | "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78="
264 | },
265 | "@protobufjs/base64": {
266 | "version": "1.1.2",
267 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
268 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
269 | },
270 | "@protobufjs/codegen": {
271 | "version": "2.0.4",
272 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
273 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
274 | },
275 | "@protobufjs/eventemitter": {
276 | "version": "1.1.0",
277 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
278 | "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A="
279 | },
280 | "@protobufjs/fetch": {
281 | "version": "1.1.0",
282 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
283 | "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
284 | "requires": {
285 | "@protobufjs/aspromise": "^1.1.1",
286 | "@protobufjs/inquire": "^1.1.0"
287 | }
288 | },
289 | "@protobufjs/float": {
290 | "version": "1.0.2",
291 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
292 | "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E="
293 | },
294 | "@protobufjs/inquire": {
295 | "version": "1.1.0",
296 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
297 | "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik="
298 | },
299 | "@protobufjs/path": {
300 | "version": "1.1.2",
301 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
302 | "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0="
303 | },
304 | "@protobufjs/pool": {
305 | "version": "1.1.0",
306 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
307 | "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q="
308 | },
309 | "@protobufjs/utf8": {
310 | "version": "1.1.0",
311 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
312 | "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
313 | },
314 | "@types/bytebuffer": {
315 | "version": "5.0.40",
316 | "resolved": "https://registry.npmjs.org/@types/bytebuffer/-/bytebuffer-5.0.40.tgz",
317 | "integrity": "sha512-h48dyzZrPMz25K6Q4+NCwWaxwXany2FhQg/ErOcdZS1ZpsaDnDMZg8JYLMTGz7uvXKrcKGJUZJlZObyfgdaN9g==",
318 | "requires": {
319 | "@types/long": "*",
320 | "@types/node": "*"
321 | }
322 | },
323 | "@types/long": {
324 | "version": "4.0.1",
325 | "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
326 | "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
327 | },
328 | "@types/node": {
329 | "version": "10.17.14",
330 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.14.tgz",
331 | "integrity": "sha512-G0UmX5uKEmW+ZAhmZ6PLTQ5eu/VPaT+d/tdLd5IFsKRPcbe6lPxocBtcYBFSaLaCW8O60AX90e91Nsp8lVHCNw=="
332 | },
333 | "ansi-regex": {
334 | "version": "2.1.1",
335 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
336 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
337 | },
338 | "ascli": {
339 | "version": "1.0.1",
340 | "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz",
341 | "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=",
342 | "requires": {
343 | "colour": "~0.7.1",
344 | "optjs": "~3.2.2"
345 | }
346 | },
347 | "bytebuffer": {
348 | "version": "5.0.1",
349 | "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz",
350 | "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=",
351 | "requires": {
352 | "long": "~3"
353 | },
354 | "dependencies": {
355 | "long": {
356 | "version": "3.2.0",
357 | "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
358 | "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s="
359 | }
360 | }
361 | },
362 | "camelcase": {
363 | "version": "2.1.1",
364 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
365 | "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
366 | },
367 | "cliui": {
368 | "version": "3.2.0",
369 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
370 | "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
371 | "requires": {
372 | "string-width": "^1.0.1",
373 | "strip-ansi": "^3.0.1",
374 | "wrap-ansi": "^2.0.0"
375 | }
376 | },
377 | "code-point-at": {
378 | "version": "1.1.0",
379 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
380 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
381 | },
382 | "colour": {
383 | "version": "0.7.1",
384 | "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz",
385 | "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g="
386 | },
387 | "core-js": {
388 | "version": "3.6.2",
389 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.2.tgz",
390 | "integrity": "sha512-hIE5dXkRzRvnZ5vhkRfQxUvDxQZmD9oueA08jDYRBKJHx+VIl/Pne/e0A4x9LObEEthC/TqiZybUoNM4tRgnKg=="
391 | },
392 | "decamelize": {
393 | "version": "1.2.0",
394 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
395 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
396 | },
397 | "dom-storage": {
398 | "version": "2.1.0",
399 | "resolved": "https://registry.npmjs.org/dom-storage/-/dom-storage-2.1.0.tgz",
400 | "integrity": "sha512-g6RpyWXzl0RR6OTElHKBl7nwnK87GUyZMYC7JWsB/IA73vpqK2K6LT39x4VepLxlSsWBFrPVLnsSR5Jyty0+2Q=="
401 | },
402 | "encoding": {
403 | "version": "0.1.12",
404 | "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
405 | "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
406 | "requires": {
407 | "iconv-lite": "~0.4.13"
408 | }
409 | },
410 | "faye-websocket": {
411 | "version": "0.11.3",
412 | "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz",
413 | "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==",
414 | "requires": {
415 | "websocket-driver": ">=0.5.1"
416 | }
417 | },
418 | "firebase": {
419 | "version": "7.8.0",
420 | "resolved": "https://registry.npmjs.org/firebase/-/firebase-7.8.0.tgz",
421 | "integrity": "sha512-oOXb8asc9mF+xN3nHUnt8cFDo4sDkSuTSLRmPM3Jpz6yriBHJYuO1k7G6sbJOtCVl+nkj0maIyByNVQxt2eBlA==",
422 | "requires": {
423 | "@firebase/analytics": "0.2.12",
424 | "@firebase/app": "0.5.3",
425 | "@firebase/app-types": "0.5.0",
426 | "@firebase/auth": "0.13.4",
427 | "@firebase/database": "0.5.20",
428 | "@firebase/firestore": "1.10.0",
429 | "@firebase/functions": "0.4.31",
430 | "@firebase/installations": "0.4.1",
431 | "@firebase/messaging": "0.6.3",
432 | "@firebase/performance": "0.2.31",
433 | "@firebase/polyfill": "0.3.31",
434 | "@firebase/remote-config": "0.1.12",
435 | "@firebase/storage": "0.3.25",
436 | "@firebase/util": "0.2.39"
437 | }
438 | },
439 | "grpc": {
440 | "version": "1.24.2",
441 | "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.24.2.tgz",
442 | "integrity": "sha512-EG3WH6AWMVvAiV15d+lr+K77HJ/KV/3FvMpjKjulXHbTwgDZkhkcWbwhxFAoTdxTkQvy0WFcO3Nog50QBbHZWw==",
443 | "requires": {
444 | "@types/bytebuffer": "^5.0.40",
445 | "lodash.camelcase": "^4.3.0",
446 | "lodash.clone": "^4.5.0",
447 | "nan": "^2.13.2",
448 | "node-pre-gyp": "^0.14.0",
449 | "protobufjs": "^5.0.3"
450 | },
451 | "dependencies": {
452 | "abbrev": {
453 | "version": "1.1.1",
454 | "bundled": true
455 | },
456 | "ansi-regex": {
457 | "version": "2.1.1",
458 | "bundled": true
459 | },
460 | "aproba": {
461 | "version": "1.2.0",
462 | "bundled": true
463 | },
464 | "are-we-there-yet": {
465 | "version": "1.1.5",
466 | "bundled": true,
467 | "requires": {
468 | "delegates": "^1.0.0",
469 | "readable-stream": "^2.0.6"
470 | }
471 | },
472 | "balanced-match": {
473 | "version": "1.0.0",
474 | "bundled": true
475 | },
476 | "brace-expansion": {
477 | "version": "1.1.11",
478 | "bundled": true,
479 | "requires": {
480 | "balanced-match": "^1.0.0",
481 | "concat-map": "0.0.1"
482 | }
483 | },
484 | "chownr": {
485 | "version": "1.1.3",
486 | "bundled": true
487 | },
488 | "code-point-at": {
489 | "version": "1.1.0",
490 | "bundled": true
491 | },
492 | "concat-map": {
493 | "version": "0.0.1",
494 | "bundled": true
495 | },
496 | "console-control-strings": {
497 | "version": "1.1.0",
498 | "bundled": true
499 | },
500 | "core-util-is": {
501 | "version": "1.0.2",
502 | "bundled": true
503 | },
504 | "debug": {
505 | "version": "3.2.6",
506 | "bundled": true,
507 | "requires": {
508 | "ms": "^2.1.1"
509 | }
510 | },
511 | "deep-extend": {
512 | "version": "0.6.0",
513 | "bundled": true
514 | },
515 | "delegates": {
516 | "version": "1.0.0",
517 | "bundled": true
518 | },
519 | "detect-libc": {
520 | "version": "1.0.3",
521 | "bundled": true
522 | },
523 | "fs-minipass": {
524 | "version": "1.2.7",
525 | "bundled": true,
526 | "requires": {
527 | "minipass": "^2.6.0"
528 | }
529 | },
530 | "fs.realpath": {
531 | "version": "1.0.0",
532 | "bundled": true
533 | },
534 | "gauge": {
535 | "version": "2.7.4",
536 | "bundled": true,
537 | "requires": {
538 | "aproba": "^1.0.3",
539 | "console-control-strings": "^1.0.0",
540 | "has-unicode": "^2.0.0",
541 | "object-assign": "^4.1.0",
542 | "signal-exit": "^3.0.0",
543 | "string-width": "^1.0.1",
544 | "strip-ansi": "^3.0.1",
545 | "wide-align": "^1.1.0"
546 | }
547 | },
548 | "glob": {
549 | "version": "7.1.4",
550 | "bundled": true,
551 | "requires": {
552 | "fs.realpath": "^1.0.0",
553 | "inflight": "^1.0.4",
554 | "inherits": "2",
555 | "minimatch": "^3.0.4",
556 | "once": "^1.3.0",
557 | "path-is-absolute": "^1.0.0"
558 | }
559 | },
560 | "has-unicode": {
561 | "version": "2.0.1",
562 | "bundled": true
563 | },
564 | "iconv-lite": {
565 | "version": "0.4.24",
566 | "bundled": true,
567 | "requires": {
568 | "safer-buffer": ">= 2.1.2 < 3"
569 | }
570 | },
571 | "ignore-walk": {
572 | "version": "3.0.3",
573 | "bundled": true,
574 | "requires": {
575 | "minimatch": "^3.0.4"
576 | }
577 | },
578 | "inflight": {
579 | "version": "1.0.6",
580 | "bundled": true,
581 | "requires": {
582 | "once": "^1.3.0",
583 | "wrappy": "1"
584 | }
585 | },
586 | "inherits": {
587 | "version": "2.0.4",
588 | "bundled": true
589 | },
590 | "ini": {
591 | "version": "1.3.5",
592 | "bundled": true
593 | },
594 | "is-fullwidth-code-point": {
595 | "version": "1.0.0",
596 | "bundled": true,
597 | "requires": {
598 | "number-is-nan": "^1.0.0"
599 | }
600 | },
601 | "isarray": {
602 | "version": "1.0.0",
603 | "bundled": true
604 | },
605 | "minimatch": {
606 | "version": "3.0.4",
607 | "bundled": true,
608 | "requires": {
609 | "brace-expansion": "^1.1.7"
610 | }
611 | },
612 | "minimist": {
613 | "version": "1.2.0",
614 | "bundled": true
615 | },
616 | "minipass": {
617 | "version": "2.9.0",
618 | "bundled": true,
619 | "requires": {
620 | "safe-buffer": "^5.1.2",
621 | "yallist": "^3.0.0"
622 | }
623 | },
624 | "minizlib": {
625 | "version": "1.3.3",
626 | "bundled": true,
627 | "requires": {
628 | "minipass": "^2.9.0"
629 | }
630 | },
631 | "mkdirp": {
632 | "version": "0.5.1",
633 | "bundled": true,
634 | "requires": {
635 | "minimist": "0.0.8"
636 | },
637 | "dependencies": {
638 | "minimist": {
639 | "version": "0.0.8",
640 | "bundled": true
641 | }
642 | }
643 | },
644 | "ms": {
645 | "version": "2.1.2",
646 | "bundled": true
647 | },
648 | "needle": {
649 | "version": "2.4.0",
650 | "bundled": true,
651 | "requires": {
652 | "debug": "^3.2.6",
653 | "iconv-lite": "^0.4.4",
654 | "sax": "^1.2.4"
655 | }
656 | },
657 | "node-pre-gyp": {
658 | "version": "0.14.0",
659 | "bundled": true,
660 | "requires": {
661 | "detect-libc": "^1.0.2",
662 | "mkdirp": "^0.5.1",
663 | "needle": "^2.2.1",
664 | "nopt": "^4.0.1",
665 | "npm-packlist": "^1.1.6",
666 | "npmlog": "^4.0.2",
667 | "rc": "^1.2.7",
668 | "rimraf": "^2.6.1",
669 | "semver": "^5.3.0",
670 | "tar": "^4.4.2"
671 | }
672 | },
673 | "nopt": {
674 | "version": "4.0.1",
675 | "bundled": true,
676 | "requires": {
677 | "abbrev": "1",
678 | "osenv": "^0.1.4"
679 | }
680 | },
681 | "npm-bundled": {
682 | "version": "1.0.6",
683 | "bundled": true
684 | },
685 | "npm-packlist": {
686 | "version": "1.4.6",
687 | "bundled": true,
688 | "requires": {
689 | "ignore-walk": "^3.0.1",
690 | "npm-bundled": "^1.0.1"
691 | }
692 | },
693 | "npmlog": {
694 | "version": "4.1.2",
695 | "bundled": true,
696 | "requires": {
697 | "are-we-there-yet": "~1.1.2",
698 | "console-control-strings": "~1.1.0",
699 | "gauge": "~2.7.3",
700 | "set-blocking": "~2.0.0"
701 | }
702 | },
703 | "number-is-nan": {
704 | "version": "1.0.1",
705 | "bundled": true
706 | },
707 | "object-assign": {
708 | "version": "4.1.1",
709 | "bundled": true
710 | },
711 | "once": {
712 | "version": "1.4.0",
713 | "bundled": true,
714 | "requires": {
715 | "wrappy": "1"
716 | }
717 | },
718 | "os-homedir": {
719 | "version": "1.0.2",
720 | "bundled": true
721 | },
722 | "os-tmpdir": {
723 | "version": "1.0.2",
724 | "bundled": true
725 | },
726 | "osenv": {
727 | "version": "0.1.5",
728 | "bundled": true,
729 | "requires": {
730 | "os-homedir": "^1.0.0",
731 | "os-tmpdir": "^1.0.0"
732 | }
733 | },
734 | "path-is-absolute": {
735 | "version": "1.0.1",
736 | "bundled": true
737 | },
738 | "process-nextick-args": {
739 | "version": "2.0.1",
740 | "bundled": true
741 | },
742 | "protobufjs": {
743 | "version": "5.0.3",
744 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz",
745 | "integrity": "sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==",
746 | "requires": {
747 | "ascli": "~1",
748 | "bytebuffer": "~5",
749 | "glob": "^7.0.5",
750 | "yargs": "^3.10.0"
751 | }
752 | },
753 | "rc": {
754 | "version": "1.2.8",
755 | "bundled": true,
756 | "requires": {
757 | "deep-extend": "^0.6.0",
758 | "ini": "~1.3.0",
759 | "minimist": "^1.2.0",
760 | "strip-json-comments": "~2.0.1"
761 | }
762 | },
763 | "readable-stream": {
764 | "version": "2.3.6",
765 | "bundled": true,
766 | "requires": {
767 | "core-util-is": "~1.0.0",
768 | "inherits": "~2.0.3",
769 | "isarray": "~1.0.0",
770 | "process-nextick-args": "~2.0.0",
771 | "safe-buffer": "~5.1.1",
772 | "string_decoder": "~1.1.1",
773 | "util-deprecate": "~1.0.1"
774 | }
775 | },
776 | "rimraf": {
777 | "version": "2.7.1",
778 | "bundled": true,
779 | "requires": {
780 | "glob": "^7.1.3"
781 | }
782 | },
783 | "safe-buffer": {
784 | "version": "5.1.2",
785 | "bundled": true
786 | },
787 | "safer-buffer": {
788 | "version": "2.1.2",
789 | "bundled": true
790 | },
791 | "sax": {
792 | "version": "1.2.4",
793 | "bundled": true
794 | },
795 | "semver": {
796 | "version": "5.7.1",
797 | "bundled": true
798 | },
799 | "set-blocking": {
800 | "version": "2.0.0",
801 | "bundled": true
802 | },
803 | "signal-exit": {
804 | "version": "3.0.2",
805 | "bundled": true
806 | },
807 | "string-width": {
808 | "version": "1.0.2",
809 | "bundled": true,
810 | "requires": {
811 | "code-point-at": "^1.0.0",
812 | "is-fullwidth-code-point": "^1.0.0",
813 | "strip-ansi": "^3.0.0"
814 | }
815 | },
816 | "string_decoder": {
817 | "version": "1.1.1",
818 | "bundled": true,
819 | "requires": {
820 | "safe-buffer": "~5.1.0"
821 | }
822 | },
823 | "strip-ansi": {
824 | "version": "3.0.1",
825 | "bundled": true,
826 | "requires": {
827 | "ansi-regex": "^2.0.0"
828 | }
829 | },
830 | "strip-json-comments": {
831 | "version": "2.0.1",
832 | "bundled": true
833 | },
834 | "tar": {
835 | "version": "4.4.13",
836 | "bundled": true,
837 | "requires": {
838 | "chownr": "^1.1.1",
839 | "fs-minipass": "^1.2.5",
840 | "minipass": "^2.8.6",
841 | "minizlib": "^1.2.1",
842 | "mkdirp": "^0.5.0",
843 | "safe-buffer": "^5.1.2",
844 | "yallist": "^3.0.3"
845 | }
846 | },
847 | "util-deprecate": {
848 | "version": "1.0.2",
849 | "bundled": true
850 | },
851 | "wide-align": {
852 | "version": "1.1.3",
853 | "bundled": true,
854 | "requires": {
855 | "string-width": "^1.0.2 || 2"
856 | }
857 | },
858 | "wrappy": {
859 | "version": "1.0.2",
860 | "bundled": true
861 | },
862 | "yallist": {
863 | "version": "3.1.1",
864 | "bundled": true
865 | }
866 | }
867 | },
868 | "http-parser-js": {
869 | "version": "0.4.10",
870 | "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz",
871 | "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q="
872 | },
873 | "iconv-lite": {
874 | "version": "0.4.24",
875 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
876 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
877 | "requires": {
878 | "safer-buffer": ">= 2.1.2 < 3"
879 | }
880 | },
881 | "idb": {
882 | "version": "3.0.2",
883 | "resolved": "https://registry.npmjs.org/idb/-/idb-3.0.2.tgz",
884 | "integrity": "sha512-+FLa/0sTXqyux0o6C+i2lOR0VoS60LU/jzUo5xjfY6+7sEEgy4Gz1O7yFBXvjd7N0NyIGWIRg8DcQSLEG+VSPw=="
885 | },
886 | "invert-kv": {
887 | "version": "1.0.0",
888 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
889 | "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
890 | },
891 | "is-fullwidth-code-point": {
892 | "version": "1.0.0",
893 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
894 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
895 | "requires": {
896 | "number-is-nan": "^1.0.0"
897 | }
898 | },
899 | "is-stream": {
900 | "version": "1.1.0",
901 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
902 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
903 | },
904 | "isomorphic-fetch": {
905 | "version": "2.2.1",
906 | "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
907 | "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
908 | "requires": {
909 | "node-fetch": "^1.0.1",
910 | "whatwg-fetch": ">=0.10.0"
911 | }
912 | },
913 | "lcid": {
914 | "version": "1.0.0",
915 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
916 | "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
917 | "requires": {
918 | "invert-kv": "^1.0.0"
919 | }
920 | },
921 | "lodash.camelcase": {
922 | "version": "4.3.0",
923 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
924 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
925 | },
926 | "lodash.clone": {
927 | "version": "4.5.0",
928 | "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz",
929 | "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y="
930 | },
931 | "long": {
932 | "version": "4.0.0",
933 | "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
934 | "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
935 | },
936 | "nan": {
937 | "version": "2.14.0",
938 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
939 | "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
940 | },
941 | "node-fetch": {
942 | "version": "1.7.3",
943 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
944 | "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
945 | "requires": {
946 | "encoding": "^0.1.11",
947 | "is-stream": "^1.0.1"
948 | }
949 | },
950 | "number-is-nan": {
951 | "version": "1.0.1",
952 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
953 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
954 | },
955 | "optjs": {
956 | "version": "3.2.2",
957 | "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz",
958 | "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4="
959 | },
960 | "os-locale": {
961 | "version": "1.4.0",
962 | "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
963 | "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
964 | "requires": {
965 | "lcid": "^1.0.0"
966 | }
967 | },
968 | "promise-polyfill": {
969 | "version": "8.1.3",
970 | "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz",
971 | "integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g=="
972 | },
973 | "protobufjs": {
974 | "version": "6.8.8",
975 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz",
976 | "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==",
977 | "requires": {
978 | "@protobufjs/aspromise": "^1.1.2",
979 | "@protobufjs/base64": "^1.1.2",
980 | "@protobufjs/codegen": "^2.0.4",
981 | "@protobufjs/eventemitter": "^1.1.0",
982 | "@protobufjs/fetch": "^1.1.0",
983 | "@protobufjs/float": "^1.0.2",
984 | "@protobufjs/inquire": "^1.1.0",
985 | "@protobufjs/path": "^1.1.2",
986 | "@protobufjs/pool": "^1.1.0",
987 | "@protobufjs/utf8": "^1.1.0",
988 | "@types/long": "^4.0.0",
989 | "@types/node": "^10.1.0",
990 | "long": "^4.0.0"
991 | }
992 | },
993 | "safe-buffer": {
994 | "version": "5.2.0",
995 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
996 | "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
997 | },
998 | "safer-buffer": {
999 | "version": "2.1.2",
1000 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1001 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
1002 | },
1003 | "string-width": {
1004 | "version": "1.0.2",
1005 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
1006 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
1007 | "requires": {
1008 | "code-point-at": "^1.0.0",
1009 | "is-fullwidth-code-point": "^1.0.0",
1010 | "strip-ansi": "^3.0.0"
1011 | }
1012 | },
1013 | "strip-ansi": {
1014 | "version": "3.0.1",
1015 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
1016 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
1017 | "requires": {
1018 | "ansi-regex": "^2.0.0"
1019 | }
1020 | },
1021 | "tslib": {
1022 | "version": "1.10.0",
1023 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
1024 | "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
1025 | },
1026 | "websocket-driver": {
1027 | "version": "0.7.3",
1028 | "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz",
1029 | "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==",
1030 | "requires": {
1031 | "http-parser-js": ">=0.4.0 <0.4.11",
1032 | "safe-buffer": ">=5.1.0",
1033 | "websocket-extensions": ">=0.1.1"
1034 | }
1035 | },
1036 | "websocket-extensions": {
1037 | "version": "0.1.4",
1038 | "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
1039 | "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="
1040 | },
1041 | "whatwg-fetch": {
1042 | "version": "3.0.0",
1043 | "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
1044 | "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
1045 | },
1046 | "window-size": {
1047 | "version": "0.1.4",
1048 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz",
1049 | "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY="
1050 | },
1051 | "wrap-ansi": {
1052 | "version": "2.1.0",
1053 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
1054 | "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
1055 | "requires": {
1056 | "string-width": "^1.0.1",
1057 | "strip-ansi": "^3.0.1"
1058 | }
1059 | },
1060 | "xmlhttprequest": {
1061 | "version": "1.8.0",
1062 | "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
1063 | "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw="
1064 | },
1065 | "y18n": {
1066 | "version": "3.2.1",
1067 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
1068 | "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
1069 | },
1070 | "yargs": {
1071 | "version": "3.32.0",
1072 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz",
1073 | "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=",
1074 | "requires": {
1075 | "camelcase": "^2.0.1",
1076 | "cliui": "^3.0.3",
1077 | "decamelize": "^1.1.1",
1078 | "os-locale": "^1.4.0",
1079 | "string-width": "^1.0.1",
1080 | "window-size": "^0.1.4",
1081 | "y18n": "^3.2.0"
1082 | }
1083 | }
1084 | }
1085 | }
1086 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-red-contrib-smartlifeair",
3 | "version": "1.0.13",
4 | "description": "NO NEED of Firmware changes or COMMAND LINE configurations. A simple NR node that works with almost any Smartlife and Tuya device. Read the instructions properly to save time. ",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "test"
8 | },
9 | "keywords": [
10 | "smartlifeair",
11 | "node-red",
12 | "smartlife",
13 | "tuya",
14 | "smart life",
15 | "smart homes",
16 | "smart things"
17 | ],
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/razorRun/node-red-contrib-smartlifeair.git/"
21 | },
22 | "author": "Roshan Milinda",
23 | "license": "GPL (GPLv3)",
24 | "homepage": "https://github.com/razorRun/node-red-contrib-smartlifeair.git/#readme",
25 | "dependencies": {
26 | "firebase": "^7.8.0"
27 | },
28 | "node-red": {
29 | "nodes": {
30 | "smartlife-air-login": "config.js",
31 | "smartlife-air-device": "device-node.js"
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/run-on-change.py:
--------------------------------------------------------------------------------
1 | # detect file changes from inside vigrant-
2 | import os.path
3 | import time
4 | import subprocess
5 | import glob
6 |
7 |
8 | def get_last_modefied_time(folderPath):
9 | files = glob.glob(folderPath)
10 | date_list = []
11 | # print(files)
12 | for file in files:
13 | date_list.append(os.path.getmtime(file))
14 | # print(date_list)
15 | return max(date_list)
16 |
17 | path = "/home/pi/node-red-contrib-smartlifeair/*"
18 | lmt = ""
19 | cmd1 = "npm install /home/pi/node-red-contrib-smartlifeair/"
20 | cmd2 = "sudo systemctl restart nodered.service"
21 | cmd1 = "npm uninstall /home/pi/node-red-contrib-smartlifeair/"
22 |
23 | while True:
24 | nmt = get_last_modefied_time(path)
25 | if nmt != lmt:
26 | print("Change Detected")
27 | subprocess.Popen(cmd3.split(), cwd='/root/.node-red')
28 | subprocess.Popen(cmd1.split(), cwd='/root/.node-red')
29 | subprocess.Popen(cmd2.split())
30 | lmt = nmt
31 | time.sleep(2)
32 |
--------------------------------------------------------------------------------