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