github.com/simpleiot/simpleiot@v0.18.3/frontend/src/Components/NodeShellyIO.elm (about)

     1  module Components.NodeShellyIO exposing (view)
     2  
     3  import Api.Point as Point exposing (Point)
     4  import Components.NodeOptions exposing (NodeOptions, oToInputO)
     5  import Dict exposing (Dict)
     6  import Element exposing (..)
     7  import Element.Background as Background
     8  import Element.Border as Border
     9  import Element.Font as Font
    10  import FormatNumber exposing (format)
    11  import FormatNumber.Locales exposing (Decimals(..), usLocale)
    12  import List
    13  import Round
    14  import Time
    15  import UI.Icon as Icon
    16  import UI.NodeInputs as NodeInputs
    17  import UI.Style as Style
    18  import UI.ViewIf exposing (viewIf)
    19  import Utils.Iso8601 as Iso8601
    20  
    21  
    22  isSettable : List Point -> Bool
    23  isSettable pts =
    24      List.any (\a -> String.contains "Set" a.typ) pts
    25  
    26  
    27  view : NodeOptions msg -> Element msg
    28  view o =
    29      let
    30          disabled =
    31              Point.getBool o.node.points Point.typeDisabled ""
    32  
    33          offline =
    34              Point.getBool o.node.points Point.typeOffline ""
    35  
    36          summaryBackground =
    37              if disabled || offline then
    38                  Style.colors.ltgray
    39  
    40              else
    41                  Style.colors.none
    42  
    43          typ =
    44              Point.getText o.node.points Point.typeType ""
    45  
    46          desc =
    47              Point.getText o.node.points Point.typeDescription ""
    48  
    49          summary =
    50              "(" ++ typ ++ ")  " ++ desc
    51  
    52          valueElement =
    53              case
    54                  typ
    55              of
    56                  "PlusI4" ->
    57                      i4ValueSummary o.node.points
    58  
    59                  _ ->
    60                      defaultSummary o.node.points
    61      in
    62      column
    63          [ width fill
    64          , Border.widthEach { top = 2, bottom = 0, left = 0, right = 0 }
    65          , Border.color Style.colors.black
    66          , spacing 6
    67          ]
    68      <|
    69          wrappedRow [ spacing 10, Background.color summaryBackground ]
    70              [ Icon.io
    71              , text summary
    72              , valueElement
    73              , viewIf disabled <| text "(disabled)"
    74              , viewIf offline <| text "(offline)"
    75              ]
    76              :: (if o.expDetail then
    77                      let
    78                          labelWidth =
    79                              150
    80  
    81                          opts =
    82                              oToInputO o labelWidth
    83  
    84                          textInput =
    85                              NodeInputs.nodeTextInput opts "0"
    86  
    87                          checkboxInput =
    88                              NodeInputs.nodeCheckboxInput opts "0"
    89  
    90                          onOffInput =
    91                              NodeInputs.nodeOnOffInput opts
    92  
    93                          deviceID =
    94                              Point.getText o.node.points Point.typeDeviceID ""
    95  
    96                          ip =
    97                              Point.getText o.node.points Point.typeIP ""
    98  
    99                          controlled =
   100                              Point.getBool o.node.points Point.typeControlled ""
   101  
   102                          latestPointTime =
   103                              case Point.getLatest o.node.points of
   104                                  Just point ->
   105                                      point.time
   106  
   107                                  Nothing ->
   108                                      Time.millisToPosix 0
   109                      in
   110                      [ textDisplay "ID" deviceID
   111                      , textLinkDisplay "IP" ip ("http://" ++ ip)
   112                      , textInput Point.typeDescription "Description" ""
   113                      , viewIf controlled <| displayControls onOffInput o.node.points
   114                      , viewIf (isSettable o.node.points) <| checkboxInput Point.typeControlled "Enable Control"
   115                      , checkboxInput Point.typeDisabled "Disabled"
   116                      , NodeInputs.nodeKeyValueInput opts Point.typeTag "Tags" "Add Tag"
   117                      , text ("Last update: " ++ Iso8601.toDateTimeString o.zone latestPointTime)
   118                      , viewPoints o.zone <| Point.filterSpecialPoints <| List.sortWith Point.sort o.node.points
   119                      ]
   120  
   121                  else
   122                      []
   123                 )
   124  
   125  
   126  defaultSummary : List Point -> Element msg
   127  defaultSummary points =
   128      let
   129          switches =
   130              Point.getAll points Point.switch |> List.sortBy .key
   131  
   132          lights =
   133              Point.getAll points Point.light
   134  
   135          inputs =
   136              Point.getAll points Point.input
   137      in
   138      row []
   139          [ displayOnOffArray "S:" switches
   140          , displayOnOffArray "L:" lights
   141          , displayOnOffArray "I:" inputs
   142          ]
   143  
   144  
   145  i4ValueSummary : List Point -> Element msg
   146  i4ValueSummary points =
   147      let
   148          valuePoints =
   149              List.filter (\p -> p.typ == Point.typeValue) points |> List.sortBy .key
   150  
   151          valueElements =
   152              List.foldl
   153                  (\p ret ->
   154                      List.append ret [ displayOnOff p ]
   155                  )
   156                  []
   157                  valuePoints
   158      in
   159      row [ spacing 8 ] valueElements
   160  
   161  
   162  displayOnOffArray : String -> List Point -> Element msg
   163  displayOnOffArray label pts =
   164      if List.length pts > 0 then
   165          row [] <| text label :: List.map displayOnOff pts
   166  
   167      else
   168          none
   169  
   170  
   171  displayOnOff : Point -> Element msg
   172  displayOnOff p =
   173      let
   174          v =
   175              if p.value == 0 then
   176                  "off"
   177  
   178              else
   179                  "on"
   180  
   181          vBackgroundColor =
   182              if v == "on" then
   183                  Style.colors.blue
   184  
   185              else
   186                  Style.colors.none
   187  
   188          vTextColor =
   189              if v == "on" then
   190                  Style.colors.white
   191  
   192              else
   193                  Style.colors.black
   194      in
   195      el [ paddingXY 7 0, Background.color vBackgroundColor, Font.color vTextColor ] <|
   196          text <|
   197              v
   198  
   199  
   200  displayControls : (String -> String -> String -> String -> Element msg) -> List Point -> Element msg
   201  displayControls onOffInput pts =
   202      let
   203          controlTypes =
   204              [ Point.light, Point.switch ]
   205      in
   206      column [] <|
   207          List.map
   208              (\t ->
   209                  let
   210                      tSet =
   211                          t ++ "Set"
   212  
   213                      ptsFiltered =
   214                          List.filter (\p -> p.typ == tSet) pts |> List.sortBy .key
   215                  in
   216                  column
   217                      [ spacing 6
   218                      ]
   219                  <|
   220                      List.indexedMap
   221                          (\i _ ->
   222                              let
   223                                  key =
   224                                      String.fromInt i
   225  
   226                                  label =
   227                                      t ++ " " ++ String.fromInt (i + 1)
   228                              in
   229                              onOffInput key t tSet label
   230                          )
   231                          ptsFiltered
   232              )
   233              controlTypes
   234  
   235  
   236  textDisplay : String -> String -> Element msg
   237  textDisplay label value =
   238      el [ paddingEach { top = 0, right = 0, bottom = 0, left = 70 } ] <|
   239          text <|
   240              label
   241                  ++ ": "
   242                  ++ value
   243  
   244  
   245  textLinkDisplay : String -> String -> String -> Element msg
   246  textLinkDisplay label value uri =
   247      el [ paddingEach { top = 0, right = 0, bottom = 0, left = 70 } ] <|
   248          row []
   249              [ text <|
   250                  label
   251                      ++ ": "
   252              , newTabLink [ Font.underline ] { url = uri, label = text value }
   253              ]
   254  
   255  
   256  viewPoints : Time.Zone -> List Point.Point -> Element msg
   257  viewPoints z pts =
   258      if List.length pts <= 0 then
   259          Element.none
   260  
   261      else
   262          let
   263              formaters =
   264                  metricFormaters z
   265  
   266              fm =
   267                  formatMetric formaters
   268          in
   269          table [ padding 7 ]
   270              { data = List.map fm pts
   271              , columns =
   272                  let
   273                      cell =
   274                          el [ paddingXY 15 5, Border.width 1 ]
   275                  in
   276                  [ { header = cell <| el [ Font.bold, centerX ] <| text "Point"
   277                    , width = fill
   278                    , view = \m -> cell <| text m.desc
   279                    }
   280                  , { header = cell <| el [ Font.bold, centerX ] <| text "Value"
   281                    , width = fill
   282                    , view = \m -> cell <| el [ alignRight ] <| text m.value
   283                    }
   284                  ]
   285              }
   286  
   287  
   288  formatMetric : Dict String MetricFormat -> Point.Point -> { desc : String, value : String }
   289  formatMetric formaters p =
   290      case Dict.get p.typ formaters of
   291          Just f ->
   292              { desc = f.desc p, value = f.vf p }
   293  
   294          Nothing ->
   295              Point.renderPoint2 p
   296  
   297  
   298  type alias MetricFormat =
   299      { desc : Point.Point -> String
   300      , vf : Point.Point -> String
   301      }
   302  
   303  
   304  metricFormaters : Time.Zone -> Dict String MetricFormat
   305  metricFormaters _ =
   306      Dict.fromList
   307          [ ( "voltage", { desc = descS "Voltage", vf = \p -> Round.round 1 p.value } )
   308          , ( "temp", { desc = descS "Temperature (C)", vf = \p -> Round.round 1 p.value } )
   309          , ( "power", { desc = descS "Power", vf = \p -> Round.round 2 p.value } )
   310          , ( "current", { desc = descS "Current", vf = \p -> Round.round 2 p.value } )
   311          , ( "brightness", { desc = descS "Brightness", vf = toWhole } )
   312          , ( "lightTemp", { desc = descS "Light Temperature", vf = toWhole } )
   313          , ( "transition", { desc = descS "Transition", vf = toWhole } )
   314          , ( "white", { desc = descS "White", vf = toWhole } )
   315          ]
   316  
   317  
   318  descS : String -> Point.Point -> String
   319  descS d _ =
   320      d
   321  
   322  
   323  toWhole : Point.Point -> String
   324  toWhole p =
   325      format { usLocale | decimals = Exact 0 } p.value