-
Notifications
You must be signed in to change notification settings - Fork 1
/
Netatmo_Unified_Sensor-CO2.fqa
178 lines (178 loc) · 32 KB
/
Netatmo_Unified_Sensor-CO2.fqa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
{
"name": "Fibaro CO2 sensor",
"type": "com.fibaro.multilevelSensor",
"apiVersion": "1.2",
"initialProperties": {
"viewLayout": {
"$jason": {
"body": {
"header": {
"style": {
"height": "0"
},
"title": "fibaro-unified-sensor"
},
"sections": {
"items": [
{
"components": [
{
"name": "label",
"style": {
"weight": "1.2"
},
"text": "Netatmo Unified Sensor",
"type": "label",
"visible": true
},
{
"style": {
"weight": "0.5"
},
"type": "space"
}
],
"style": {
"weight": "1.2"
},
"type": "vertical"
},
{
"components": [
{
"components": [
{
"name": "button2_1",
"style": {
"weight": "0.50"
},
"text": "Szukaj urządzeń",
"type": "button",
"visible": true
},
{
"name": "button2_2",
"style": {
"weight": "0.50"
},
"text": "Odśwież dane",
"type": "button",
"visible": true
}
],
"style": {
"weight": "1.2"
},
"type": "horizontal"
},
{
"style": {
"weight": "0.5"
},
"type": "space"
}
],
"style": {
"weight": "1.2"
},
"type": "vertical"
}
]
}
},
"head": {
"title": "fibaro-unified-sensor"
}
}
},
"uiCallbacks": [
{
"callback": "searchEvent",
"eventType": "onReleased",
"name": "button2_1"
},
{
"callback": "refreshEvent",
"eventType": "onReleased",
"name": "button2_2"
}
],
"quickAppVariables": [
{
"name": "ClientID",
"type": "string",
"value": ""
},
{
"name": "ClientSecret",
"type": "password",
"value": ""
},
{
"name": "RefreshToken",
"type": "password",
"value": ""
},
{
"name": "Type",
"type": "string",
"value": "CO2"
},
{
"name": "ModuleID",
"type": "string",
"value": ""
},
{
"name": "DeviceID",
"type": "string",
"value": ""
}
],
"typeTemplateInitialized": true
},
"files": [
{
"name": "main",
"isMain": true,
"isOpen": false,
"content": "--[[\nNetatmo Unified Sensor\n@author ikubicki\n@version 1.1.0\n]]\nfunction QuickApp:onInit()\n self.config = Config:new(self)\n self.i18n = i18n:new(api.get(\"/settings/info\").defaultLanguage)\n self.netatmo = Netatmo:new(self.config)\n self:trace('')\n self:trace('Netatmo Unified Sensor - ' .. self:getTypeLabel())\n self:updateProperty('manufacturer', 'Netatmo')\n self:updateProperty('model', 'Weather Station')\n self:updateView(\"button2_1\", \"text\", self.i18n:get('search-devices'))\n self:updateView(\"button2_2\", \"text\", self.i18n:get('refresh'))\n \n self.interfaces = api.get(\"/devices/\" .. self.id).interfaces\n self:run()\nend\n\nfunction QuickApp:run()\n self:pullNetatmoData()\n local interval = self.config:getTimeoutInterval()\n if (interval > 0) then\n fibaro.setTimeout(interval, function() self:run() end)\n end\nend\n\nfunction QuickApp:pullNetatmoData()\n self:updateView(\"button2_2\", \"text\", self.i18n:get('refreshing'))\n local getSensorDataCallback = function(sensorData)\n local property = self:getProperty()\n self:updateProperty(\"value\", sensorData.data[property])\n if sensorData.battery ~= nil then\n self:updateProperty(\"batteryLevel\", sensorData.battery)\n if not utils:contains(self.interfaces, \"battery\") then\n api.put(\"/devices/\" .. self.id, { interfaces = {\"quickApp\", \"battery\"} })\n end\n else\n self:updateProperty(\"batteryLevel\", sensorData.battery)\n if not utils:contains(self.interfaces, \"power\") then\n api.put(\"/devices/\" .. self.id, { interfaces = {\"quickApp\", \"power\"} })\n end\n end\n self:updateView(\"label\", \"text\", string.format(self.i18n:get('last-update'), os.date('%Y-%m-%d %H:%M:%S')))\n self:updateView(\"button2_2\", \"text\", self.i18n:get('refresh'))\n end\n local types = self:getType()\n local id = self.config:getModuleID()\n self.netatmo:getSensorData(types, id, getSensorDataCallback)\nend\n\nfunction QuickApp:refreshEvent()\n self:pullNetatmoData()\nend\n\nfunction QuickApp:searchEvent()\n self:debug(self.i18n:get('searching-devices'))\n self:updateView(\"button2_1\", \"text\", self.i18n:get('searching-devices'))\n local searchDevicesCallback = function(stations)\n -- QuickApp:debug(json.encode(stations))\n -- printing results\n for _, station in pairs(stations) do\n QuickApp:trace(string.format(self.i18n:get('search-row-station'), station.name, station.id))\n QuickApp:trace(string.format(self.i18n:get('search-row-station-modules'), #station.modules))\n for __, module in ipairs(station.modules) do\n QuickApp:trace(string.format(self.i18n:get('search-row-module'), module.name, module.id, module.type))\n QuickApp:trace(string.format(self.i18n:get('search-row-module_types'), table.concat(module.data_type, ', ')))\n end\n end\n self:updateView(\"button2_1\", \"text\", self.i18n:get('search-devices'))\n self:updateView(\"label\", \"text\", string.format(self.i18n:get('check-logs'), 'QUICKAPP' .. self.id))\n end\n local types = self:getType()\n self.netatmo:searchDevices(types, searchDevicesCallback)\nend\n\nfunction QuickApp:getType()\n if self.type == \"com.fibaro.temperatureSensor\" or self.config:getDeviceType() == \"Temperature\" then \n return { \"NAMain\", \"NAModule1\", \"NAModule4\" } \n end\n if self.type == \"com.fibaro.windSensor\" or self.config:getDeviceType() == \"Wind\" or self.config:getDeviceType() == \"Gusts\" or self.config:getDeviceType() == \"Gust\" then \n self:updateProperty(\"unit\", \"km/h\")\n return { \"NAModule2\" } \n end\n if self.type == \"com.fibaro.rainSensor\" or self.config:getDeviceType() == \"Rain\" then \n self:updateProperty(\"unit\", \"mm/h\")\n return { \"NAModule3\" } \n end\n if self.type == \"com.fibaro.humiditySensor\" or self.config:getDeviceType() == \"Humidity\" then \n return { \"NAMain\", \"NAModule1\", \"NAModule4\" } \n end\n if self.config:getDeviceType() == \"Pressure\" then\n return { \"NAMain\" }\n end\n if self.config:getDeviceType() == \"Noise\" then\n return { \"NAMain\" }\n end\n if self.config:getDeviceType() == \"CO2\" then\n return { \"NAMain\", \"NAModule4\" } \n end\nend\n\nfunction QuickApp:getTypeLabel()\n if self.type == \"com.fibaro.temperatureSensor\" or self.config:getDeviceType() == \"Temperature\" then \n return \"Temperature\"\n end\n if self.type == \"com.fibaro.windSensor\" or self.config:getDeviceType() == \"Wind\" then\n return \"Wind\"\n end\n if self.config:getDeviceType() == \"Gusts\" or self.config:getDeviceType() == \"Gust\" then \n return \"Gusts\"\n end\n if self.type == \"com.fibaro.rainSensor\" or self.config:getDeviceType() == \"Rain\" then \n return \"Rain\"\n end\n if self.type == \"com.fibaro.humiditySensor\" or self.config:getDeviceType() == \"Humidity\" then \n return \"Humidity\"\n end\n if self.config:getDeviceType() == \"Pressure\" then\n return \"Pressure\"\n end\n if self.config:getDeviceType() == \"Noise\" then\n return \"Noise\"\n end\n if self.config:getDeviceType() == \"CO2\" then\n return \"CO2\"\n end\nend\n\nfunction QuickApp:getProperty()\n if self.type == \"com.fibaro.temperatureSensor\" or self.config:getDeviceType() == \"Temperature\" then \n return \"Temperature\"\n end\n if self.config:getDeviceType() == \"Gusts\" or self.config:getDeviceType() == \"Gust\" then \n return \"max_wind_str\"\n -- return \"GustStrength\"\n end\n if self.type == \"com.fibaro.windSensor\" or self.config:getDeviceType() == \"Wind\" then\n return \"WindStrength\"\n end\n if self.config:getDeviceType() == \"Rain1h\" or self.config:getDeviceType() == \"Rain1\" then \n return \"sum_rain_1\"\n end\n if self.config:getDeviceType() == \"Rain24h\" or self.config:getDeviceType() == \"Rain24\" then \n return \"sum_rain_24\"\n end\n if self.type == \"com.fibaro.rainSensor\" or self.config:getDeviceType() == \"Rain\" then \n return \"Rain\"\n end\n if self.type == \"com.fibaro.humiditySensor\" or self.config:getDeviceType() == \"Humidity\" then \n return \"Humidity\"\n end\n if self.config:getDeviceType() == \"Pressure\" then\n return \"Pressure\"\n end\n if self.config:getDeviceType() == \"Noise\" then\n return \"Noise\"\n end\n if self.config:getDeviceType() == \"CO2\" then\n return \"CO2\"\n end\nend\n"
},
{
"name": "Globals",
"isMain": false,
"isOpen": false,
"content": "--[[\nGlobal variables handler\n@author ikubicki\n]]\nclass 'Globals'\n\nfunction Globals:get(name, alternative)\n local response = api.get('/globalVariables/' .. name)\n if response then\n local char = string.sub(response.value, 1, 1)\n if char == '{' or char == '\"' then\n return json.decode(response.value)\n end\n return response.value\n end\n return alternative\nend\n\nfunction Globals:set(name, value)\n local response = api.put('/globalVariables/' .. name, {\n name = name,\n value = json.encode(value)\n })\n if not response then\n response = api.post('/globalVariables', {\n name = name,\n value = json.encode(value)\n })\n \n end\n if response ~= nil then\n if response.type == 'ERROR' then\n QuickApp:error('GLOBALS ERROR[' .. response.reason .. ']:', response.message)\n end\n end\nend"
},
{
"name": "Config",
"isMain": false,
"isOpen": false,
"content": "--[[\nConfiguration handler\n@author ikubicki\n]]\nclass 'Config'\n\nfunction Config:new(app)\n self.app = app\n self:init()\n return self\nend\n\nfunction Config:getClientID()\n return self.clientID\nend\n\nfunction Config:getClientSecret()\n return self.clientSecret\nend\n\nfunction Config:getUsername()\n return self.username\nend\n\nfunction Config:getPassword()\n return self.password\nend\n\nfunction Config:getDeviceID()\n return self.deviceID\nend\n\nfunction Config:setDeviceID(deviceID)\n self.deviceID = deviceID\n self.app:setVariable('DeviceID', deviceID)\nend\n\nfunction Config:getModuleID()\n return self.moduleID\nend\n\nfunction Config:setModuleID(moduleID)\n self.moduleID = moduleID\n self.app:setVariable('ModuleID', moduleID)\nend\n\nfunction Config:getAccessToken()\n return self.token\nend\n\nfunction Config:setAccessToken(token)\n self.app:setVariable(\"AccessToken\", token)\n self.token = token\nend\n\nfunction Config:getRefreshToken()\n return self.rtoken\nend\n\nfunction Config:getTimeoutInterval()\n return tonumber(self.interval) * 60000\nend\n\nfunction Config:getDeviceType()\n return self.type\nend\n\n--[[\nThis function takes variables and sets as global variables if those are not set already.\nThis way, adding other devices might be optional and leaves option for users, \nwhat they want to add into HC3 virtual devices.\n]]\nfunction Config:init()\n self.clientID = self.app:getVariable('ClientID')\n self.clientSecret = self.app:getVariable('ClientSecret')\n self.username = self.app:getVariable('Username')\n self.password = self.app:getVariable('Password')\n self.deviceID = tostring(self.app:getVariable('DeviceID'))\n self.moduleID = tostring(self.app:getVariable('ModuleID'))\n self.type = tostring(self.app:getVariable('Type'))\n self.interval = self.app:getVariable('Interval')\n self.token = self.app:getVariable('AccessToken')\n self.rtoken = self.app:getVariable('RefreshToken')\n\n local storedClientID = Globals:get('netatmo_client_id')\n local storedClientSecret = Globals:get('netatmo_client_secret')\n local storedUsername = Globals:get('netatmo_username')\n local storedPassword = Globals:get('netatmo_password')\n local storedInterval = Globals:get('netatmo_interval')\n\n -- handling clientID\n if string.len(self.clientID) < 4 and string.len(storedClientID) > 3 then\n self.app:setVariable(\"ClientID\", storedClientID)\n self.clientID = storedClientID\n elseif (storedClientID == nil and self.clientID) then -- or storedClientID ~= self.clientID then\n Globals:set('netatmo_client_id', self.clientID)\n end\n -- handling client secret\n if string.len(self.clientSecret) < 4 and string.len(storedClientSecret) > 3 then\n self.app:setVariable(\"ClientSecret\", storedClientSecret)\n self.clientSecret = storedClientSecret\n elseif (storedClientSecret == nil and self.clientSecret) then -- or storedClientSecret ~= self.clientSecret then\n Globals:set('netatmo_client_secret', self.clientSecret)\n end\n -- handling username\n if string.len(self.username) < 4 and string.len(storedUsername) > 3 then\n self.app:setVariable(\"Username\", storedUsername)\n self.username = storedUsername\n elseif (storedUsername == nil and self.username) then -- or storedUsername ~= self.username then\n Globals:set('netatmo_username', self.username)\n end\n -- handling password\n if string.len(self.password) < 4 and string.len(storedPassword) > 3 then\n self.app:setVariable(\"Password\", storedPassword)\n self.password = storedPassword\n elseif (storedPassword == nil and self.password) then -- or storedPassword ~= self.password then\n Globals:set('netatmo_password', self.password)\n end\n -- handling interval\n if not self.interval or self.interval == \"\" then\n if storedInterval and storedInterval ~= \"\" then\n self.app:setVariable(\"Interval\", storedInterval)\n self.interval = storedInterval\n else\n self.interval = \"5\"\n end\n end\n if (storedInterval == \"\" and self.interval ~= \"\") then -- or storedInterval ~= self.interval then\n Globals:set('netatmo_interval', self.interval)\n end\nend"
},
{
"name": "HTTPClient",
"isMain": false,
"isOpen": false,
"content": "--[[\nHTTPClient wrapper\n@author ikubicki\n]]\nclass 'HTTPClient'\n\nfunction HTTPClient:new(options)\n if not options then\n options = {}\n end\n self.options = options\n return self\nend\n\nfunction HTTPClient:get(url, success, error, headers)\n local client = net.HTTPClient({timeout = 10000})\n if not headers then\n headers = {}\n end\n client:request(self:url(url), self:requestOptions(success, error, 'GET', nil, headers)) \nend\n\nfunction HTTPClient:post(url, data, success, error, headers)\n local client = net.HTTPClient({timeout = 10000})\n if not headers then\n headers = {}\n end\n client:request(self:url(url), self:requestOptions(success, error, 'POST', data, headers)) \nend\n\nfunction HTTPClient:postForm(url, data, success, error, headers)\n local client = net.HTTPClient({timeout = 10000})\n if not headers then\n headers = {}\n end\n headers[\"Content-Type\"] = 'application/x-www-form-urlencoded;charset=UTF-8'\n client:request(self:url(url), self:requestOptions(success, error, 'POST', data, headers, true)) \nend\n\nfunction HTTPClient:put(url, data, success, error, headers)\n local client = net.HTTPClient({timeout = 10000})\n client:request(self:url(url), self:requestOptions(success, error, 'PUT', data, headers)) \nend\n\nfunction HTTPClient:delete(url, success, error, headers)\n local client = net.HTTPClient({timeout = 10000})\n if not headers then\n headers = {}\n end\n client:request(self:url(url), self:requestOptions(success, error, 'DELETE', nil, headers)) \nend\n\nfunction HTTPClient:url(url)\n if (string.sub(url, 0, 4) == 'http') then\n return url\n end\n if not self.options.baseUrl then\n self.options.baseUrl = 'http://localhost'\n end\n return self.options.baseUrl .. tostring(url)\nend\n\nfunction HTTPClient:requestOptions(success, error, method, data, headers, isFormData)\n if error == nil then\n error = function (error)\n QuickApp:error(json.encode(error))\n end\n end\n if method == nil then\n method = 'GET'\n end\n local options = {\n checkCertificate = false,\n method = method,\n headers = headers,\n }\n if data ~= nil then\n if isFormData then\n options.data = ''\n for key, value in pairs(data) do\n if string.len(options.data) > 0 then \n options.data = options.data .. '&'\n end\n options.data = options.data .. key .. '=' .. value\n end\n elseif type(data) == 'string' then\n options.data = data\n else\n options.data = json.encode(data)\n end\n end\n return {\n options = options,\n success = success,\n error = error\n }\nend"
},
{
"name": "i18n",
"isMain": false,
"isOpen": false,
"content": "--[[\nInternationalization tool\n@author ikubicki\n]]\nclass 'i18n'\n\nfunction i18n:new(langCode)\n self.phrases = phrases[langCode]\n return self\nend\n\nfunction i18n:get(key)\n if self.phrases[key] then\n return self.phrases[key]\n end\n return key\nend\n\nphrases = {\n pl = {\n ['search-devices'] = 'Szukaj urządzeń',\n ['searching-devices'] = 'Szukam...',\n ['refresh'] = 'Odśwież dane',\n ['refreshing'] = 'Odświeżam...',\n ['last-update'] = 'Ostatnia aktualizacja: %s',\n ['check-logs'] = 'Zakończono wyszukiwanie. Sprawdź logi tego urządzenia: %s',\n ['search-row-station'] = '__ STACJA POGODOWA %s',\n ['search-row-station-modules'] = '__ Wykryto %d modułów',\n ['search-row-module'] = '____ MODUŁ %s (ID: %s, typ: %s)',\n ['search-row-module_types'] = '____ Typy danych: %s',\n },\n en = {\n ['search-devices'] = 'Search devices',\n ['searching-devices'] = 'Searching...',\n ['refresh'] = 'Update data',\n ['refreshing'] = 'Updating...',\n ['last-update'] = 'Last update at %s',\n ['check-logs'] = 'Check device logs (%s) for search results',\n ['search-row-station'] = '__ WEATHER STATION %s',\n ['search-row-station-modules'] = '__ %d modules detected',\n ['search-row-module'] = '____ MODULE %s (ID: %s, type: %s)',\n ['search-row-module_types'] = '____ Data types: %s',\n },\n de = {\n ['search-devices'] = 'Geräte suchen',\n ['searching-devices'] = 'Suchen...',\n ['refresh'] = 'Aktualisieren',\n ['refreshing'] = 'Aktualisieren...',\n ['last-update'] = 'Letztes update: %s',\n ['check-logs'] = 'Überprüfen Sie die Geräteprotokolle (%s) auf Suchergebnisse',\n ['search-row-station'] = '__ WETTERSTATION %s',\n ['search-row-station-modules'] = '__ %d module erkannt',\n ['search-row-module'] = '____ MODULE %s (ID: %s, typ: %s)',\n ['search-row-module_types'] = '____ Datentypen: %s',\n }\n}"
},
{
"name": "Netatmo",
"isMain": false,
"isOpen": false,
"content": "--[[\nNetatmo SDK\n@author ikubicki\n]]\nclass 'Netatmo'\n\nfunction Netatmo:new(config)\n self.config = config\n self.user = config:getUsername()\n self.pass = config:getPassword()\n self.client_id = config:getClientID()\n self.client_secret = config:getClientSecret()\n self.device_id = config:getDeviceID()\n self.module_id = config:getModuleID()\n self.access_token = config:getAccessToken()\n self.token = Globals:get('netatmo_atoken', '')\n self.refresh_token = config:getRefreshToken()\n self.http = HTTPClient:new({})\n return self\nend\n\nfunction Netatmo:searchDevices(types, callback)\n if #types < 1 then types = nil end\n local buildModule = function(module)\n return {\n id = module._id,\n name = module.module_name,\n type = module.type,\n data_type = module.data_type,\n }\n end\n local buildStation = function(data)\n local station = {\n id = data._id,\n home_id = data.home_id,\n name = data.station_name,\n modules = {},\n }\n table.insert(station.modules, buildModule(data))\n return station\n end\n local getStationsDataCallback = function(devices)\n local stations = {}\n for _, device in ipairs(devices) do\n local station = buildStation(device)\n local add = false\n for _, module in ipairs(device.modules) do\n if not types or utils:contains(types, module.type) then\n table.insert(station.modules, buildModule(module))\n add = true\n end\n end\n if add == true then\n table.insert(stations, station)\n end\n end\n if callback ~= nil then\n callback(stations)\n end\n end\n local authCallback = function(response)\n self:getStationsData(getStationsDataCallback)\n end\n self:auth(authCallback)\nend\n\nfunction Netatmo:getSensorData(types, moduleID, callback)\n if callback == nil then\n callback = function() end\n end\n local getStationsDataCallback = function(devices)\n for _, device in ipairs(devices) do\n if device._id == moduleID then\n return callback({\n id = device._id,\n type = device.type,\n data = device.dashboard_data,\n name = device.module_name,\n battery = nil,\n dead = device.reachable ~= true,\n })\n end\n for _, module in ipairs(device.modules) do\n if module._id == moduleID then\n return callback({\n id = module._id,\n type = module.type,\n data = module.dashboard_data,\n name = module.module_name,\n battery = math.ceil((tonumber(module.battery_vp) - 4300) / 17),\n dead = module.reachable,\n })\n end\n end\n end\n if callback ~= nil then\n callback({})\n end\n end\n local authCallback = function(response)\n self:getStationsData(getStationsDataCallback)\n end\n local searchDevicesCallback = function(stations)\n if self.device_id == \"\" then\n -- QuickApp:debug(json.encode(stations))\n QuickApp:trace('Assigning DeviceID: ' .. stations[1].id)\n self:setDeviceID(stations[1].id)\n end\n if moduleID == nil or string.len(moduleID) < 10 then\n moduleID = \"\"\n for _, module in pairs(stations[1].modules) do\n if utils:contains(types, module.type) then\n moduleID = module.id\n end\n end\n if moduleID ~= nil then\n QuickApp:trace('Assigning ModuleID: ' .. moduleID)\n self:setModuleID(moduleID)\n end\n end\n if string.len(moduleID) > 10 then\n self:auth(authCallback)\n elseif (callback ~= nil) then\n callback({})\n end\n end\n if string.len(moduleID) < 10 or self.device_id == \"\" then\n self:searchDevices(types, searchDevicesCallback)\n else\n searchDevicesCallback({{modules = {{id = moduleID}}}})\n end\nend\n\nfunction Netatmo:getWeatherData(callback)\n local getStationsDataCallback = function(devices)\n local device = devices[1]\n local weatherData = {\n _id = device._id,\n temp = tonumber(device.dashboard_data[\"Temperature\"]),\n humi = tonumber(device.dashboard_data[\"Humidity\"]),\n rain = 0,\n wind = 0, \n }\n for _, module in pairs(device.modules) do\n if module.type == \"NAModule1\" then\n weatherData.temp = tonumber(module.dashboard_data.Temperature)\n weatherData.humi = tonumber(module.dashboard_data.Humidity)\n end\n if module.type == \"NAModule2\" then\n weatherData.wind = tonumber(module.dashboard_data.WindStrength)\n end\n if module.type == \"NAModule3\" then\n weatherData.rain = tonumber(module.dashboard_data.Rain)\n end\n end\n if callback ~= nil then\n callback(weatherData)\n end\n end\n local authCallback = function(response)\n self:getStationsData(getStationsDataCallback)\n end\n self:auth(authCallback)\nend\n\nfunction Netatmo:getStationsData(callback, attempt)\n if attempt == nil then\n attempt = 0\n end\n local fail = function(response)\n QuickApp:error('Unable to pull devices')\n -- QuickApp:debug(json.encode(response.data))\n Netatmo:setToken('')\n if attempt < 3 then\n attempt = attempt + 1\n fibaro.setTimeout(3000, function()\n QuickApp:debug('Netatmo:getStationData - Retry attempt #' .. attempt)\n local authCallback = function(response)\n self:getStationsData(callback, attempt)\n end\n Netatmo:auth(authCallback)\n end)\n end\n end\n local success = function(response)\n if response.status > 299 then\n fail(response)\n return\n end\n local data = json.decode(response.data)\n if callback ~= nil then\n callback(data.body.devices)\n end\n end\n local url = 'https://api.netatmo.com/api/getstationsdata'\n if string.len(self.device_id) > 1 then\n url = url .. '?device_id=' .. self.device_id\n end\n local headers = {\n Authorization = \"Bearer \" .. self:getToken()\n }\n self.http:get(url, success, fail, headers)\nend\n\nfunction Netatmo:auth(callback)\n if string.len(self:getToken()) > 10 then\n -- QuickApp:debug('Already authenticated')\n if callback ~= nil then\n callback({})\n end\n return\n end\n if string.len(self.access_token) > 10 then\n if callback ~= nil then\n Netatmo:setToken(self.access_token)\n callback({})\n end\n return\n end\n local data = {\n [\"grant_type\"] = 'password',\n [\"scope\"] = 'read_station',\n [\"client_id\"] = self.client_id,\n [\"client_secret\"] = self.client_secret,\n [\"username\"] = self.user,\n [\"password\"] = self.pass,\n }\n if string.len(self.refresh_token) > 10 then\n data = {\n [\"grant_type\"] = 'refresh_token',\n [\"refresh_token\"] = self.refresh_token,\n [\"client_id\"] = self.client_id,\n [\"client_secret\"] = self.client_secret,\n }\n end\n local fail = function(response)\n QuickApp:error('Unable to authenticate')\n if self.access_token == self.token then\n QuickApp:error('Removing configured AccessToken')\n self.config:setAccessToken('')\n end\n Netatmo:setToken('')\n end\n local success = function(response)\n if response.status > 299 then\n fail(response)\n return\n end\n local data = json.decode(response.data)\n Netatmo:setToken(data.access_token)\n if callback ~= nil then\n callback(data)\n end\n end\n self.http:postForm('https://api.netatmo.net/oauth2/token', data, success, fail)\nend\n\nfunction Netatmo:setDeviceID(deviceID)\n self.device_id = deviceID\n self.config:setDeviceID(deviceID)\nend\n\nfunction Netatmo:setModuleID(moduleID)\n self.module_id = moduleID\n self.config:setModuleID(moduleID)\nend\n\nfunction Netatmo:setToken(token)\n self.token = token\n Globals:set('netatmo_atoken', token)\nend\n\nfunction Netatmo:getToken()\n if not self.token and self.access_token ~= nil then\n self.token = self.access_token\n end\n if string.len(self.token) > 10 then\n return self.token\n elseif string.len(Globals:get('netatmo_atoken', '')) > 10 then\n return Globals:get('netatmo_atoken', '')\n end\n return \"\"\nend"
},
{
"name": "utils",
"isMain": false,
"isOpen": false,
"content": "--[[\nLUA utilities\n@author ikubicki\n]]\nclass 'utils'\n\nfunction utils:new()\n return self\nend\n\nfunction utils:contains(a, n)\n for k, v in pairs(a) do\n if v == n then\n return k\n end\n end\n return false\nend"
}
]
}