Advertisement
tjntomas

Firefox fix for HADasboard graph widget

Nov 13th, 2020 (edited)
1,328
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. function basehagraph(widget_id, url, skin, parameters)
  2. {
  3.     self = this
  4.     self.widget_id = widget_id
  5.     self.parameters = parameters
  6.     self.OnStateAvailable = OnStateAvailable
  7.     self.OnStateUpdate = OnStateUpdate
  8.     self.states = {}
  9.     var l = Object.keys(self.parameters.entities).length
  10.     var callbacks = []
  11.     var monitored_entities =  []
  12.  
  13.     for (entity of self.parameters.entities){
  14.         monitored_entities.push({"entity": entity, "initial": self.OnStateAvailable, "update": self.OnStateUpdate})
  15.     }
  16.    
  17.         // Some default values
  18.     self.NUMBER_OF_DECIMALS = 2
  19.     self.PAPER_BACKGROUND_COLOR = 'rgba(200,200,200,0)'
  20.     self.X_GRID_COLOR = "rgba(255,255,255,0)"
  21.     self.CANVAS_HEIGHT = 215
  22.     self.TITLE_COLOR = self.parameters.css.title_color
  23.     self.X_AXIS_TEXT_COLOR = self.parameters.css.x_axis_text_color
  24.     self.Y_AXIS_LEGEND_COLOR = self.parameters.css.y_axis_legend_color
  25.     self.Y_AXIS_TEXT_COLOR = self.parameters.css.y_axis_text_color
  26.     // Some default colors for the traces.
  27.     self.TRACE_COLORS =
  28.                         ["rgba(50,50,220,0.7)",  "rgba(220,70,220,0.7)", "rgba(40,200,40,0.7)",  "rgba(220,20,20,0.7)",
  29.                          "rgba(220,220,40,0.7)", "rgba(40,220,220,0.7)", "rgba(220,70,120,0.7)", "rgba(200,100,20,0.7)",
  30.                          "rgba(100,220,40,0.7)", "rgba(100,20,220,0.7)", "rgba(120,70,120,0.7)", "rgba(100,100,200,0.7)"
  31.                          ]
  32.  
  33.     self.FILL_COLORS = [ "rgba(50,50,220,0.4)",  "rgba(220,70,220,0.4)", "rgba(40,120,40,0.6)",  "rgba(220,20,20,0.4)",
  34.                          "rgba(220,220,40,0.4)", "rgba(40,220,220,0.4)", "rgba(220,70,120,0.4)", "rgba(200,100,20,0.4)",
  35.                          "rgba(100,220,40,0.4)", "rgba(100,20,220,0.4)", "rgba(120,70,120,0.4)", "rgba(100,100,200,0.4)"
  36.                          ]
  37.     self.BAR_COLORS =   ["rgba(50,50,220,0.4)",  "rgba(220,70,220,0.4)", "rgba(40,120,40,0.6)",  "rgba(220,20,20,0.4)",
  38.                          "rgba(220,220,40,0.4)", "rgba(40,220,220,0.4)", "rgba(220,70,120,0.4)", "rgba(200,100,20,0.4)",
  39.                          "rgba(100,220,40,0.4)", "rgba(100,20,220,0.4)", "rgba(120,70,120,0.4)", "rgba(100,100,200,0.4)"
  40.                          ]
  41.     // If colors are passed as parameters, we use these instead of the default colors.
  42.     self.TRACE_COLORS = css(self,"trace_colors", self.TRACE_COLORS)
  43.     self.FILL_COLORS = css(self,"fill_colors", self.FILL_COLORS)
  44.     self.BAR_COLORS = css(self,"bar_colors", self.BAR_COLORS)
  45.    
  46.     self.HA_Data = HA_Data
  47.  
  48.     self.TIME_ZONE = Settings(self, 'time_zone','Europe/Stockholm')
  49.     self.LOCALE = Settings(self, 'locale','sv')
  50.     console.log("time zone:", self.TIME_ZONE)
  51.     var now = new Date()
  52.     console.log("Local time is", now)
  53.    
  54.     self.PLOT_BG_COLOR = 'rgba(40,40,40,0)'
  55.     self.TRACE_NAME_COLOR = '#888888'
  56.     self.CANVAS_HTML_BODY_START = '<div id="GRAPH_CANVAS" class="canvasclass" data-bind="attr:{style: graph_style}" width="100%" height="100%" style="'
  57.     self.CANVAS_HTML_BODY_END = '"></div>'
  58.                                
  59.     WidgetBase.call(self, widget_id, url, skin, parameters, monitored_entities, callbacks)
  60.     self.DataSeriesArray = new Array ()
  61.     self.index = 1
  62.     FireFoxFix = true
  63.     draw(self)
  64.  
  65.  
  66.     function OnStateUpdate(self, state){
  67.         // Log that new data has been received.
  68.         Logger(self,"New value for " + self.parameters.entities[0] + ": " + state.state)
  69.         draw(self)
  70.     }
  71.  
  72.     function OnStateAvailable(self, state){
  73.     }
  74.    
  75.     function MultiPlot(self,DataSeries)
  76.     {  
  77.         var GRAPH_CANVAS = element(self, 'canvasclass')
  78.         GRAPH_CANVAS.outerHTML = self.CANVAS_HTML_BODY_START + self.css.background_style + self.CANVAS_HTML_BODY_END
  79.         GRAPH_CANVAS = element(self, 'canvasclass') // Yes, this duplicate line is needed since the line
  80.                                                     // above destroys the element by modifying outerHTML.
  81.  
  82.         var y_axis_title = decodeURI(self.parameters.units)
  83.         switch (decodeURI(self.parameters.units))
  84.         {
  85.             case "°C":
  86.                 y_axis_title = CSS_Settings(self, "degrees_celsius_text", "Degrees Celsius")
  87.                 break;
  88.             case "°F":
  89.                 y_axis_title = CSS_Settings(self,"degrees_fahrenheit_text","Degrees Fahrenheit")
  90.                 break;
  91.  
  92.             case "W":
  93.                 y_axis_title = "Watt"
  94.                 break;
  95.  
  96.             case "%":
  97.                 y_axis_title = CSS_Settings(self, "percent_text", "Percent")
  98.                 break;
  99.         }
  100.  
  101.         var d_width = document.getElementById(self.widget_id).offsetWidth
  102.         var min = self.parameters.min
  103.         var max = self.parameters.max
  104.         self.parameters.plot = GRAPH_CANVAS
  105.         var canvas_height = Settings(self, 'height', self.CANVAS_HEIGHT)
  106.  
  107.         // Adjust the legend y position if more than one legend should be displayed
  108.         if(DataSeries.length >2)
  109.         {
  110.             var legend_y = -0.2
  111.         }
  112.         else
  113.         {
  114.             var legend_y = -0.1
  115.         }
  116.         var x_grid_color = self.css.grid_color
  117.         var y_grid_color = self.css.grid_color
  118.  
  119.         // If the graph type is "bar", we hide the x-axis by changing its color to the background color
  120.         if(Settings(self, 'type', "scatter") == "bar")
  121.         {
  122.             x_grid_color = self.X_GRID_COLOR
  123.             y_grid_color = self.X_GRID_COLOR
  124.         }
  125.         var x_axis = {
  126.                     showgrid: false,
  127.                     zeroline: true,
  128.                     showline: true,
  129.                     mirror: 'ticks',
  130.                     gridcolor: x_grid_color,
  131.                     gridwidth: 1,
  132.                     zerolinecolor: self.css.grid_color,
  133.                     linecolor: self.css.grid_color,
  134.                     zerolinewidth: 1,
  135.                     linewidth: 1,
  136.                     tickfont: {
  137.            
  138.                         size: 10,
  139.                         color: self.X_AXIS_TEXT_COLOR
  140.                       }
  141.                  }
  142.            
  143.         var y_axis = {
  144.                     title: y_axis_title,
  145.                     titlefont: {
  146.            
  147.                         size: 12,
  148.                         color: self.Y_AXIS_LEGEND_COLOR
  149.                       },
  150.                     range: [min,max],
  151.                     showgrid: true,
  152.                     zeroline: true,
  153.                     showline: false,
  154.                     mirror: 'ticks',
  155.                     gridcolor: y_grid_color,
  156.                     gridwidth: 1,
  157.                     zerolinecolor: self.css.grid_color,
  158.                     linecolor: self.css.grid_color,
  159.                     zerolinewidth: 1,
  160.                     linewidth: 1,
  161.                     tickfont: {
  162.            
  163.                         size: 10,
  164.                         color: self.Y_AXIS_TEXT_COLOR
  165.                       },
  166.                  }
  167.  
  168.  
  169.         var display = {
  170.                     margin: { t:32,l: 35, r: 21 , b: 32 },
  171.                     titlefont: {"size": 14,"color": self.TITLE_COLOR, "font-weight":500},
  172.                     title: self.parameters.title,
  173.                     paper_bgcolor: self.PAPER_BACKGROUND_COLOR,
  174.                     plot_bgcolor: self.PLOT_BG_COLOR,
  175.                     width: d_width, height: canvas_height,
  176.                     legend: {
  177.                                 x: -0.05,
  178.                                 y: legend_y,traceorder: 'normal',orientation: 'h',
  179.                                 font:   {
  180.                                             family: 'sans-serif',
  181.                                             size: 12,
  182.                                             color: self.css.legend_text_color
  183.                                         }
  184.                             },
  185.                     xaxis: x_axis,
  186.                     yaxis: y_axis
  187.         }
  188.        
  189.         var traces = new Array()
  190.         var traceColors =   self.TRACE_COLORS
  191.         var fillColors =    self.FILL_COLORS
  192.         var barColors =     self.BAR_COLORS
  193.         var i = 0
  194.         var colorIndex = 0
  195.         var d_fill = ""
  196.  
  197.         while ( i < (DataSeries.length/2) ){
  198.             if ( "titles" in self.parameters ){
  199.                 if ("value_in_legend" in self.parameters){
  200.                     var value = " " + parseFloat(DataSeries[i * 2 + 1].pop()).toFixed(1) + " " + self.parameters.units
  201.                 }else{
  202.                     var value = ""}
  203.                 var d_title =self.parameters.titles[i] + value
  204.                
  205.                 colorIndex =  i + Settings(self,"colorIndex",0)
  206.             }
  207.             else{
  208.                 colorIndex = Settings(self,'colorIndex',7)
  209.             }
  210.  
  211.             d_shape = Settings(self,'shape','spline')
  212.             d_fill = Settings(self,'fill','')
  213.  
  214.             if ( Settings(self,'type', "scatter") == "bar" ){
  215.                 traces[i] = {
  216.                     type: Settings(self,'type',"scatter"),
  217.                     text:  DataSeries[i * 2 + 1],
  218.                     textposition: 'auto',
  219.                       hoverinfo: 'none',
  220.                       textfont: {
  221.                        
  222.                         size: 10,
  223.                         color: self.TRACE_NAME_COLOR
  224.                       },
  225.                     marker: {
  226.                         color: barColors[colorIndex],
  227.                         line: {
  228.                           color: traceColors[colorIndex],
  229.                           width: 1
  230.                         }
  231.                       },
  232.                    
  233.                     x: DataSeries[i * 2],
  234.                     y: DataSeries[i * 2 + 1],
  235.                    
  236.                     mode: 'lines', line:{
  237.                                             color: traceColors[colorIndex],
  238.                                             width: 2,
  239.                                             shape: d_shape
  240.                                         },
  241.                                         name: d_title,
  242.                                         fill: d_fill,
  243.                                         fillcolor: fillColors[colorIndex]
  244.                 }
  245.                 i = i + 1
  246.             }
  247.             else{
  248.                 traces[i] = {
  249.                     type: Settings(self,'type',"scatter"),
  250.                     x: DataSeries[i * 2],
  251.                     y: DataSeries[i * 2 + 1],
  252.                     connectgaps: true,
  253.                     mode: 'lines', line:{
  254.                                             color: traceColors[colorIndex],
  255.                                             width: 2,shape: d_shape},
  256.                                             name: d_title,
  257.                                             fill: d_fill,
  258.                                             fillcolor: fillColors[colorIndex]
  259.                                         }
  260.                 i = i + 1
  261.             }
  262.         }
  263.    
  264.         try {
  265.             Plotly.plot( GRAPH_CANVAS, traces,display, {displayModeBar: false})
  266.         }
  267.         catch(err) {}
  268.     }
  269.  
  270.     function HA_Data(self, values, index){
  271.         values = values[0]
  272.         self.DataSeriesArray[index*2] =  new Array ()
  273.         self.DataSeriesArray[index*2+1] =  new Array ()
  274.         for (data in values){
  275.             self.DataSeriesArray[index*2+1].push(parseFloat(values[data]['state']))
  276.             self.DataSeriesArray[index*2].push(values[data]['last_changed'])
  277.         }
  278.         if (index == self.number_of_entities){
  279.             MultiPlot(self, self.DataSeriesArray)
  280.         }
  281.     }
  282.  
  283.     function draw(self)
  284.     {
  285.         // Get number of entities to process
  286.         var number_of_entities = Object.keys(self.parameters.entities).length
  287.         var current_entity_index = 0
  288.         var time_filter = self.parameters.time
  289.         self.number_of_entities = number_of_entities-1
  290.        
  291.         for (current_entity_index in self.parameters.entities){
  292.             get_history(self, self.parameters.entities[current_entity_index],time_filter,current_entity_index, self.HA_Data )
  293.         }
  294.         if (FireFoxFix){
  295.             FireFoxFix = false
  296.             draw(self)
  297.         }
  298.     }
  299.  
  300.     // Helper function to return either a default value or a value passed in parameters.
  301.     function Settings(self,parameter,default_value)
  302.     {
  303.         if(parameter in self.parameters){
  304.             return self.parameters[parameter]
  305.         }
  306.         else{
  307.             return default_value
  308.         }
  309.     }
  310.  
  311.     function CSS_Settings(self,parameter,default_value)
  312.     {
  313.         if(parameter in self.parameters.css){
  314.             return self.parameters.css[parameter]
  315.         }
  316.         else{
  317.             return default_value
  318.         }
  319.     }
  320.     //  Helper function to adjust color and opacity of the traces.
  321.     function css(self, parameter, default_value)
  322.     {
  323.         if(parameter in self.parameters.css){
  324.             var m = self.parameters.css[parameter]
  325.             var n = self.parameters.css.multi
  326.             var colors = []
  327.             for (i in self[parameter.toUpperCase()]){
  328.                 arr = self[parameter.toUpperCase()][i].slice(5).slice(0, -1).split(",")
  329.                 rgb = []
  330.                 for(v = 0; v < 3; v++){
  331.                     rgb.push(parseInt(parseFloat(arr[v])/m))
  332.                 }
  333.                 color = "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + arr[v]*n +")"
  334.                 colors.push(color)
  335.             }
  336.             return colors
  337.         }
  338.         else{
  339.             return default_value
  340.         }
  341.     }
  342.  
  343.     // Helper function to return an element from class name
  344.     function element(self, class_name)
  345.     {
  346.         return document.getElementById(self.widget_id).getElementsByClassName(class_name)[0]
  347.     }
  348.    
  349.     function Logger(self,message){
  350.    
  351.         if ("log" in self.parameters){
  352.             console.log(message)
  353.         }
  354.     }
  355.  
  356.     async function get_history(self, entity, time_filter, index, callback){
  357.        
  358.         var start_time = GetTimeDiff(time_filter)
  359.         var HISTORY_API_URL = "/api/history/period/"
  360.         var END_TIME_URL = "&end_time="
  361.         var ENTITY_FILTER_URL = "&filter_entity_id="
  362.         BASE_URL = self.parameters.css.ha_url.split("://") [1]
  363.         BASE_SCHEME = self.parameters.css.ha_url.split("://") [0]
  364.         console.log("Scheme", BASE_SCHEME)
  365.         if (BASE_SCHEME == "https"){
  366.             self.http_scheme = "https"
  367.             self.ws_scheme = "wss://"
  368.         }
  369.         else
  370.         {
  371.             self.http_scheme = "http"
  372.             self.ws_scheme = "ws://"
  373.         }
  374.         self.TOKEN = self.parameters.css.token
  375.         var websocket_url = self.ws_scheme + BASE_URL + "/api/websocket"
  376.         var request = HISTORY_API_URL + start_time
  377.         var filter =  ENTITY_FILTER_URL + entity
  378.         var auth_ok = false
  379.         var ws = new WebSocket(websocket_url)
  380.    
  381.          ws.onmessage = function(event) {
  382.              var msg = JSON.parse(event.data)
  383.              if (!auth_ok ){
  384.                  switch (msg['type']){
  385.    
  386.                  case "auth_required":
  387.                      ws.send(JSON.stringify({"type": "auth","access_token": self.TOKEN}))
  388.                      break
  389.    
  390.                  case "auth_ok":
  391.                      auth_ok = true
  392.                      ws.send(JSON.stringify({"id": self.index, "type": "auth/sign_path", "path": request,  "expires": 20}))
  393.                      self.index = self.index + 1
  394.                      break
  395.                  }
  396.              }
  397.              else{
  398.                 if (msg['success'] == true){
  399.                     var path = msg['result']['path']
  400.                     var url = self.http_scheme  + "://" + BASE_URL + path + filter
  401.                     var xhr = new XMLHttpRequest()
  402.                     Logger(self, url)
  403.                     xhr.open("GET", url, false)
  404.                     xhr.send()
  405.                     var res = JSON.parse(xhr.response)
  406.                     Logger(self, res)
  407.                     callback(self, res, index)
  408.                 }
  409.              }
  410.          }
  411.          ws.onclose = function() {
  412.              console.log('Connection to Home Assistant closed')
  413.              self.auth_ok = false
  414.          }
  415.          ws.onopen = function() {
  416.              console.log('Connected to Home Assistant')
  417.          }
  418.     }
  419.    
  420.      function GetTimeDiff(time){
  421.         var today = new Date()
  422.         var sec = 60000
  423.         var hour = sec * 60
  424.         var day = hour * 24
  425.    
  426.         var w = time.split("w")
  427.         if (w.length > 1){
  428.             time = w[1]
  429.             today.setTime(today.getTime() - day * w[0] * 7)
  430.         }
  431.         var d = time.split("d")
  432.         if (d.length>1){
  433.             time = d[1]
  434.             today.setTime(today.getTime() - day * d[0])
  435.         }
  436.         var h = time.split("h")
  437.         if (h.length>1){
  438.             time = h[1]
  439.             today.setTime(today.getTime() - hour * h[0])
  440.         }
  441.         var m = time.split("m")
  442.         if (m.length>1){
  443.             time = m[1]
  444.             today.setTime(today.getTime() - sec * m[0])
  445.         }
  446.         console.log(today.toLocaleDateString(self.LOCALE), today.toLocaleTimeString(self.LOCALE))
  447.         return today.toLocaleDateString(self.LOCALE) + "T" + today.toLocaleTimeString(self.LOCALE)
  448.     }
  449. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement