Advertisement
Guest User

Untitled

a guest
Dec 29th, 2018
998
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. var request = require('request');
  2.  
  3. // enable debugging mode
  4. //request.debug = true;
  5.  
  6. var lastEventTime = new Date().getTime();
  7.  
  8. var rawDeviceData = false;
  9. var tahomaJar = request.jar();
  10. var baseRequest = request.defaults({
  11.   headers: {
  12.         'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36'},
  13.   jar: tahomaJar
  14. });
  15.  
  16. var tahomaDevices = {};
  17. var Map_DeviceURL2StateName = {};
  18.  
  19. var userName = "BITTE EINTRAGEN";
  20. var passWord = "BITTE EINTRAGEN";
  21.  
  22. var baseURL = 'https://www.tahomalink.com/enduser-mobile-web/externalAPI/json/';
  23. //var baseURL = 'https://www.tahomalink.com/enduser-mobile-web/enduserAPI/';
  24. var baseURLApply = 'https://www.tahomalink.com/enduser-mobile-web/enduserAPI/';
  25.  
  26. var isConnectedInternal = false;
  27. let loginInProgress = false;
  28.  
  29. createState('tahoma.connected', false, {
  30.   read: true,
  31.   write: true,
  32.   name: "connection status of tahoma",
  33.   type: "boolean",
  34.   def: false
  35. });
  36.  
  37. createOrUpdateState('tahoma.update', false, {
  38.     read: true,
  39.     write: true,
  40.     role: "button"
  41. });
  42. on('javascript.0.tahoma.update', function(obj)
  43. {
  44.     log("tahoma refresh states requested: " + obj);
  45.    
  46.     if (obj.newState.val)
  47.     {
  48.         getAllStates();  
  49.     }
  50. });
  51.  
  52. function isConnected()
  53. {
  54.     return isConnectedInternal;
  55. }
  56. function setConnected(connected)
  57. {
  58.     isConnectedInternal = connected;
  59.    
  60.     setState('javascript.0.tahoma.connected', connected, true);
  61. }
  62.  
  63. function getCreateStateOptions4Widget(widget)
  64. {
  65.     if (widget === 'PositionableRollerShutter')
  66.     {
  67.         return {
  68.                 "role":  "blind",
  69.         };
  70.     }
  71.    
  72.     if (widget === 'LuminanceSensor')
  73.     {
  74.         return {
  75.             "role":  "sensor"
  76.         };
  77.     }
  78.    
  79.     return {
  80.                 read: true,
  81.                 write: false,
  82.                 role: "state"
  83.             };
  84. }
  85.  
  86. function getCreateStateOptions4State(widget, stateName)
  87. {
  88.     if (stateName === "core:ClosureState" || stateName === "core:TargetClosureState")
  89.     {
  90.         return {
  91.                 "type":  "number",               // optional,  default "number"
  92.                 "read":  true,                   // mandatory, default true
  93.                 "write": true,                   // mandatory, default true
  94.                 "min":   0,                      // optional,  default 0
  95.                 "max":   100,                    // optional,  default 100
  96.                 "unit":  "%",                    // optional,  default %
  97.                 "role":  "level.blind"           // mandatory
  98.            };
  99.     }
  100.     if (stateName === "core:SlateOrientationState")
  101.     {
  102.         return {
  103.                 "type":  "number",               // optional,  default "number"
  104.                 "read":  true,                   // mandatory, default true
  105.                 "write": true,                   // mandatory, default true
  106.                 "min":   0,                      // optional,  default 0
  107.                 "max":   100,                    // optional,  default 100
  108.                 "unit":  "%",                    // optional,  default %
  109.                 "role":  "level.blind.orientation"           // mandatory
  110.            };
  111.     }
  112.     if (stateName === "core:LuminanceState")
  113.     {
  114.         return {
  115.                 "type":  "number",               // optional,  default "number"
  116.                 "read":  true,                   // mandatory, default true
  117.                 "write": false,                   // mandatory, default true
  118.                 "min":   0,                      // optional,  default 0
  119.                 "max":   100000,                    // optional,  default 100
  120.                 "unit":  "Lux",                    // optional,  default %
  121.                 "role":  "level.color.luminance"           // mandatory
  122.            };
  123.     }
  124.    
  125.     return {
  126.                 read: true,
  127.                 write: false,
  128.                 role: "state"
  129.             };
  130. }
  131.  
  132. function send(requestPath, payload, callback)
  133. {
  134.     login(function(err,data)
  135.     {
  136.         if (err)
  137.         {
  138.             return callback(err, data);
  139.         }
  140.        
  141.         sendInternal(requestPath, payload, callback);
  142.     });
  143. }
  144.  
  145. function sendInternal(requestPath, payload, callback)
  146. {
  147.     var url = baseURL + requestPath;
  148.    
  149.     if (requestPath.endsWith("apply"))
  150.     {
  151.         url = baseURLApply + requestPath + "/highPriority";
  152.     }
  153.     else if (requestPath === '/setup/devices/states/refresh')
  154.     {
  155.         url = baseURLApply + requestPath;
  156.     }
  157.    
  158.     var formPayload = null;
  159.     var jsonPayload = null;
  160.    
  161.     if (requestPath === 'login')
  162.     {
  163.         formPayload = payload;
  164.     }
  165.     else
  166.     {
  167.         jsonPayload = payload;
  168.     }
  169.    
  170.     log("perform " + requestPath + " with payload:" + JSON.stringify(payload),'debug');
  171.  
  172.     baseRequest.post(
  173.         {
  174.             url:    url,
  175.             json:   jsonPayload,
  176.             form:   formPayload
  177.         },
  178.         function(error, response, body)
  179.         {
  180.             if (!error && response.statusCode === 200)
  181.             {
  182.                 if (requestPath === 'login')
  183.                 {
  184.                     callback(false, JSON.parse(body));
  185.                 }
  186.                 else
  187.                 {
  188.                     callback(false, body);
  189.                 }
  190.             }
  191.             else if (response && requestPath !== 'logout'
  192.                 && (response.statusCode === 401 || response.statusCode === 403))
  193.             {
  194.                 log("error during tahomalink request: " + response.statusText + " ->" + response.statusCode + " retry "  + requestPath, 'error');
  195.                
  196.                 // session expired?
  197.                 setConnected(false);
  198.                 loginInProgress = false;
  199.  
  200.                 // perform login and send again
  201.                 send(requestPath, payload, callback);
  202.             }
  203.             else
  204.             {
  205.                 log("error during tahomalink request: " + response.statusCode + ": " + error +
  206.                     ", request path: " + requestPath + " with payload:" + JSON.stringify(payload), 'error');
  207.                
  208.                 var result = {};        
  209.                 result.error = error;
  210.                
  211.                 if(typeof response !== "undefined")
  212.                 {
  213.                     log("response status: " + response.statusCode + " " + response.statusText,'debug');
  214.                    
  215.                     result.responseStatusCode = response.statusCode;
  216.                     result.responseStatusText = response.statusText;
  217.                 }
  218.                
  219.                 callback(true, result);
  220.             }
  221.     });
  222. }
  223.  
  224. function logout(callback)
  225. {
  226.     var performLogout = isConnected();
  227.     setConnected(false);
  228.    
  229.     if (performLogout)
  230.     {
  231.         sendInternal("logout", {}, function (err, data)
  232.         {
  233.             callback(err, data);
  234.         });
  235.     }
  236.     else
  237.     {
  238.         callback(false, {});
  239.     }
  240. }
  241.  
  242. function login(callback)
  243. {
  244.     if (isConnected())
  245.     {
  246.          callback(false, {});
  247.          return;
  248.     }
  249.  
  250.     // check for login already started but not yet finished
  251.     if (loginInProgress)
  252.     {
  253.         log("login in progress, wait 100 msec");
  254.  
  255.         setTimeout(function()
  256.         {
  257.             login(callback);
  258.         }, 1000);
  259.         return;
  260.     }
  261.  
  262.     loginInProgress = true;
  263.     //setConnected(true);
  264.  
  265.     var payload = {'userId': userName, 'userPassword': passWord};
  266.        
  267.     var result = sendInternal("login", payload, function (err, data)
  268.     {
  269.         if (err || !data.success)
  270.         {
  271.             loginInProgress = false;
  272.             return callback(true, data);
  273.         }
  274.        
  275.         lastEventTime = new Date().getTime();
  276.         setConnected(true);
  277.         loginInProgress = false;
  278.        
  279.         getUserInfo(function (err,data)
  280.         {
  281.             if (!err)
  282.             {
  283.                 return getSetup(callback);
  284.             }
  285.            
  286.             callback(err, data);
  287.         });
  288.        
  289.     });
  290.      
  291. }
  292.  
  293. function getUserInfo(callback)
  294. {
  295.     send('getEndUser', {},function (err, data)
  296.     {
  297.         if (!err)
  298.         {
  299.             updateData('userdata', data.endUser);
  300.            
  301.             callback(false, data);
  302.         }
  303.         else
  304.         {
  305.             log("getUserInfo failed!", 'error');
  306.         }
  307.        
  308.     });
  309. }
  310.  
  311. function updateGateWayData(gateways)
  312. {  
  313.     for (var i in gateways)
  314.     {
  315.         var gateway = gateways[i];
  316.        
  317.         updateData(gateway.gatewayId, gateway);
  318.     }
  319. }
  320.  
  321. function updateDevices(devices)
  322. {  
  323.     tahomaDevices = devices;
  324.    
  325.     for (var i in devices)
  326.     {
  327.         var device = devices[i];
  328.        
  329.         // just set the raw data from tahoma
  330.         updateDevice('devices.' + device.label, device);
  331.     }
  332. }
  333.  
  334. function updateDevice(name, deviceData)
  335. {  
  336.     createOrUpdateState('tahoma.' + name, '',
  337.         getCreateStateOptions4Widget(deviceData.widget));
  338.    
  339.     // device URL
  340.     createOrUpdateState('tahoma.' + name + '.deviceURL', deviceData.deviceURL);
  341.    
  342.     // states
  343.     for (var stateKey in deviceData.states)
  344.     {
  345.         var state = deviceData.states[stateKey];
  346.        
  347.         createOrUpdateState('tahoma.' + name + '.states.' + state.name,
  348.             mapValueTahoma2ioBroker(state.name, state.value),
  349.             getCreateStateOptions4State(deviceData.widget, state.name));
  350.     }
  351.    
  352.      // commands
  353.     for (var commandKey in deviceData.definition.commands)
  354.     {
  355.         var command = deviceData.definition.commands[commandKey];
  356.        
  357.         if (command.nparams === 0)
  358.         {
  359.             createOrUpdateState('tahoma.' + name + '.commands.' + command.commandName, false, {
  360.                 read: true,
  361.                 write: true,
  362.                 role: "button"
  363.             });
  364.         }
  365.     }
  366.            
  367.     // raw data
  368.     if (rawDeviceData)
  369.     {
  370.         for (var p in deviceData)
  371.         {
  372.             var value = deviceData[p];
  373.            
  374.             if (typeof(value) === 'object')
  375.             {
  376.                 updateData('raw.' + name + '.' + p, value);
  377.             }
  378.             else
  379.             {
  380.                 createOrUpdateState('tahoma.raw.' + name + '.' + p, value);
  381.             }
  382.         }
  383.     }
  384. }
  385.  
  386. function mapValueTahoma2ioBroker(stateName, stateValue)
  387. {
  388.     if (stateName === 'core:ClosureState' ||
  389.         stateName === 'core:TargetClosureState' ||
  390.         stateName === "core:SlateOrientationState" ||
  391.         stateName === "core:LuminanceState"
  392.         )
  393.     {
  394.         stateValue = parseInt(stateValue,10);
  395.     }
  396.  
  397.     return stateValue;
  398. }      
  399.  
  400. function mapValueioBroker2Tahoma(stateName, stateValue)
  401. {
  402.     if (stateName === 'core:ClosureState' || stateName === 'core:TargetClosureState')
  403.     {
  404.         //stateValue = parseInt(stateValue,10);
  405.     }
  406.  
  407.     return stateValue;
  408. }      
  409.  
  410. function updateData(type, data)
  411. {  
  412.     for (var p in data)
  413.     {
  414.         var value = data[p];
  415.        
  416.         if (typeof(value) === 'object')
  417.         {
  418.             updateData(type + '.' + p, value);
  419.         }
  420.         else
  421.         {
  422.             createOrUpdateState('tahoma.' + type + '.' + p, value);
  423.         }
  424.     }
  425. }
  426.  
  427. function createOrUpdateState(key, value, options)
  428. {
  429.     key = key.replace(' ' , '_');
  430.     var state = getState("javascript.0." + key);
  431.     var typeName = "string";
  432.    
  433.     if (value === "true" || value === "false")
  434.     {
  435.         value = value == "true";
  436.         typeName = "boolean";
  437.     }
  438.     else if (Number.isInteger(value))
  439.     {
  440.         value = parseInt(value,10);
  441.         typeName = "number";
  442.     }
  443.     else if (!isNaN(value))
  444.     {
  445.         value = Number(value);
  446.         typeName="number";
  447.     }
  448.    
  449.     if (state.notExist)
  450.     {
  451.        
  452.         if (typeof(options) === 'undefined')
  453.         {
  454.             options = {
  455.                 read: true,
  456.                 write: false,
  457.                 type: typeName
  458.             };
  459.         }
  460.  
  461.         // create state
  462.         createState(key, value, 0, options);
  463.     }
  464.     else
  465.     {
  466.         setState("javascript.0." + key, value, true);
  467.     }
  468. }
  469.  
  470. function getSetup(callback)
  471. {
  472.     send('getSetup', {},function (err, data)
  473.     {
  474.         if (!err)
  475.         {
  476.             updateGateWayData(data.setup.gateways);
  477.             updateData('location', data.setup.location);
  478.             updateDevices(data.setup.devices);
  479.            
  480.             // delete old devices
  481.             deleteOldDevices();
  482.            
  483.             // update mapping table device URL to state key with label
  484.             $('javascript.0.tahoma.devices.*.deviceURL').each(function (id, i)
  485.             {
  486.                 var state = getState(id);
  487.                 Map_DeviceURL2StateName[state.val] = id.substr(0, id.indexOf(".deviceURL"));
  488.                
  489.                 //log("set mapping deviceURL to name: " + state.val + " -> " + id.substr(0, id.indexOf(".deviceURL")),'debug');
  490.             });
  491.            
  492.             // refresh
  493.             send('/setup/devices/states/refresh', {}, function (err,data)
  494.             {
  495.                 if (err)
  496.                 {
  497.                     log("refresh device state failed");
  498.                 }
  499.                 callback(err, {});  
  500.             });
  501.            
  502.         }
  503.         else
  504.         {
  505.             log("getSetup failed!",'error');
  506.            
  507.             callback(err, {});
  508.         }
  509.        
  510.     });
  511. }
  512.  
  513. function getAllStates()
  514. {
  515.     login(function (err, data)
  516.     {
  517.         if (err)
  518.         {
  519.             return;
  520.         }
  521.  
  522.         send("getEvents", {}, function (err,data)
  523.         {
  524.             if (err)
  525.             {
  526.                 return;
  527.             }
  528.            
  529.             updateDeviceStateFromEvent(data);
  530.         });
  531.     });
  532. }
  533.  
  534. function updateDeviceStateFromEvent(events)
  535. {
  536.     for (var i in events)
  537.     {
  538.         lastEventTime = new Date().getTime();
  539.         var event = events[i];
  540.    
  541.         if (event.name === 'DeviceStateChangedEvent')
  542.         {
  543.             updateDeviceState(event);
  544.         }
  545.     }
  546. }
  547.  
  548. function updateDeviceState(event)
  549. {
  550.     var deviceURL = event.deviceURL;
  551.     var states = event.deviceStates;
  552.    
  553.     var devicePath = Map_DeviceURL2StateName[deviceURL];
  554.     // tahoma.devices.XXX
  555.     // tahoma.devices.XXX.states.Y
  556.    
  557.     log("got event for device " + devicePath);
  558.  
  559.     for (var i in event.deviceStates)
  560.     {
  561.         var state = event.deviceStates[i];
  562.         var name = state.name;
  563.         var value = mapValueTahoma2ioBroker(name, state.value);
  564.        
  565.         log("found " + devicePath + '.states.' + name + " -> " + value);
  566.         setState(devicePath + '.states.' + name, value, true);
  567.     }
  568. }
  569.  
  570. function deleteOldDevices()
  571. {
  572.     var currentTime = new Date().getTime();
  573.    
  574.     $('javascript.0.tahoma.devices.*.lastUpdateTime').each(function (id, i)
  575.     {
  576.         var state = getState(id);
  577.         var device = id.substr(0, id.indexOf(".lastUpdateTime"));
  578.        
  579.         if (currentTime - state.ts > 5 * 60 * 1000)
  580.         {
  581.             // update older than 1 minute -> drop
  582.             log ("found old " + device + " -> " + new Date(state.ts),'debug');
  583.            
  584.             $(device + '.*').each(function (id, i)
  585.             {
  586.                 //log("delete state:" + id);
  587.                 deleteState(id);
  588.             });
  589.         }
  590.     });
  591. }
  592.  
  593. on(/^javascript\.0\.tahoma\.devices.*\.commands/,function(obj)
  594. {
  595.     if (obj.state.val)
  596.     {
  597.         var id = obj.id;
  598.       log("button pressed: " + id + " -> " + JSON.stringify(obj));
  599.      
  600.       var commandName = id.substr(id.lastIndexOf(".")+1);
  601.       var deviceURL = getState(id.substr(0, id.indexOf(".commands.")) + ".deviceURL").val;
  602.      
  603.       var payload = {'label':'command ' + commandName + ' from ioBroker',
  604.           'actions':[{
  605.             'deviceURL': deviceURL,
  606.             'commands': [{
  607.                 'name': commandName,
  608.                 'parameters': []
  609.             }]}]};
  610.    
  611.         send("exec/apply", payload, function(err, data)
  612.         {
  613.             // reset state
  614.             setState(obj.id, !obj.state.val);
  615.         });
  616.     }
  617. });
  618.  
  619. on(/^javascript\.0\.tahoma\.devices.*\.states.core:ClosureState/,function(obj)
  620. {
  621.     onClosureStateChange(obj);
  622. });
  623. on(/^javascript\.0\.tahoma\.devices.*\.states.core:TargetClosureState/,function(obj)
  624. {
  625.     onClosureStateChange(obj);
  626. });
  627.  
  628. function onClosureStateChange(obj)
  629. {
  630.     if (!obj.newState.ack)
  631.     {
  632.         var id = obj.id;
  633.           log("closure state changed: " + id + " -> " + JSON.stringify(obj));
  634.          
  635.           var commandName = "setClosure";
  636.           var deviceURL = getState(id.substr(0, id.indexOf(".states.")) + ".deviceURL").val;
  637.           var stateValue = obj.newState.val;
  638.           var roomName = id.substr(id.indexOf('.devices.')+9);
  639.          
  640.           roomName = roomName.substr(0,roomName.indexOf('.states'));
  641.          
  642.           var payload = {'label': roomName + ' - Positioniere auf ' + stateValue + ' % - ioBroker',
  643.               'actions':[{
  644.                 'deviceURL': deviceURL,
  645.                 'commands': [{
  646.                     'name': commandName,
  647.                     'parameters': [ mapValueioBroker2Tahoma('core:ClosureState', stateValue) ]
  648.                 }]}]};
  649.    
  650.             send("exec/apply", payload, function(err, data)
  651.             {
  652.                 // reset state
  653.                 //setState(obj.id, !obj.state.val);
  654.             });
  655.     }
  656. };
  657.  
  658. on(/^javascript\.0\.tahoma\.devices.*\.states.core:SlateOrientationState/,function(obj)
  659. {
  660.     if (!obj.newState.ack)
  661.     {
  662.         var id = obj.id;
  663.      
  664.       var commandName = "setOrientation";
  665.       var deviceURL = getState(id.substr(0, id.indexOf(".states.")) + ".deviceURL").val;
  666.       var stateValue = obj.newState.val;
  667.       var roomName = id.substr(id.indexOf('.devices.')+9);
  668.          
  669.         roomName = roomName.substr(0,roomName.indexOf('.states'));
  670.      
  671.       log("slate orientation changed: " + roomName + " -> " + stateValue);
  672.      
  673.      
  674.       var payload = {'label':roomName + ' - Ausrichtung ' + stateValue + ' % - ioBroker',
  675.           'actions':[{
  676.             'deviceURL': deviceURL,
  677.             'commands': [{
  678.                 'name': commandName,
  679.                 'parameters': [ mapValueioBroker2Tahoma('core:SlateOrientationState', stateValue) ]
  680.             }]}]};
  681.    
  682.         send("exec/apply", payload, function(err, data)
  683.         {
  684.             // reset state
  685.             //setState(obj.id, !obj.state.val);
  686.         });
  687.        
  688.     }
  689. });
  690.  
  691. // if connected, update state all 5 seconds
  692. schedule("*/5 * * * * *", function ()
  693. {
  694.     // if connected, update states
  695.     if (isConnected())
  696.     {
  697.         getAllStates();
  698.        
  699.         if (new Date().getTime() - lastEventTime > 5 * 60 * 1000)
  700.         {
  701.             // no events within last 60 seconds
  702.             logout(function () {});
  703.         }
  704.     }
  705. });
  706.  
  707. // update state all 10 minutes
  708. schedule("*/10 * * * *", function ()
  709. {
  710.     if (new Date().getTime() - lastEventTime > 9 * 60 * 1000)
  711.     {
  712.         log("update tahoma all 10 minutes");
  713.  
  714.         getAllStates();
  715.     }
  716. });
  717.  
  718. onStop(function (callback) {
  719.     logout(function (err, data)
  720.         {
  721.             callback();
  722.         });
  723. }, 10000 /*ms*/);
  724.  
  725. // start script
  726. getAllStates();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement