Repository: JanDeDobbeleer/oh-my-posh
Pull request #1292 (2021-11-23 15:53:33)

update: theme night-owl
- Added sysinfo & php segment
- Changed java background color
- Small fixes

### Prerequisites

- [x] I have read and understood the `CONTRIBUTING` guide
- [x] The commit message follows the [conventional commits][cc] guidelines
- [x] Tests for the changes have been added (for bug fixes/features)
- [x] Docs have been added / updated (for bug fixes/features)

### Description

[Description of the change]

<!---

Tips:

If you're not comfortable with working with Git, we're working a guide (https://ohmyposh.dev/docs/contributing_git) to help you out.
Oh My Posh advises GitKraken (https://www.gitkraken.com/invite/nQmDPR9D) as your preferred cross platform Git GUI power tool.

-->

[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary



Patch:
diff --git a/docs/docs/segment-wifi.md b/docs/docs/segment-wifi.md
new file mode 100644
index 00000000..5e986f5e
--- /dev/null
+++ b/docs/docs/segment-wifi.md
@@ -0,0 +1,52 @@
+---
+id: wifi
+title: WiFi
+sidebar_label: WiFi
+---
+
+## What
+
+Show details about the currently connected WiFi network.
+
+::info
+Currently only supports Windows and WSL. Pull requests for Darwin and Linux support are welcome :)
+:::
+
+## Sample Configuration
+
+```json
+{
+  "type": "wifi",
+  "style": "powerline",
+  "background": "#8822ee",
+  "foreground": "#222222",
+  "background_templates": [
+    "{{ if (not .Connected) }}#FF1111{{ end }}"
+    "{{ if (lt .Signal 60) }}#DDDD11{{ else if (lt .Signal 90) }}#DD6611{{ else }}#11CC11{{ end }}"
+  ],
+  "powerline_symbol": "\uE0B0",
+  "properties": {
+    "template": "{{ if .Connected }}\uFAA8{{ else }}\uFAA9{{ end }}
+    {{ if .Connected }}{{ .SSID }} {{ .Signal }}% {{ .ReceiveRate }}Mbps{{ else }}{{ .State }}{{ end }}"
+  }
+}
+```
+
+## Properties
+
+- template: `string` - A go [text/template][go-text-template]  extended with [sprig][sprig] using the properties below
+
+## Template Properties
+
+- `.Connected`: `bool` - if WiFi is currently connected
+- `.State`: `string` - WiFi connection status - _e.g. connected or disconnected_
+- `.SSID`: `string` - the SSID of the current wifi network
+- `.RadioType`: `string` - the radio type - _e.g. 802.11ac, 802.11ax, 802.11n, etc._
+- `.Authentication`: `string` - the authentication type - _e.g. WPA2-Personal, WPA2-Enterprise, etc._
+- `.Channel`: `int` - the current channel number
+- `.ReceiveRate`: `int` - the receive rate (Mbps)
+- `.TransmitRate`: `int` - the transmit rate (Mbps)
+- `.Signal`: `int` - the signal strength (%)
+
+[go-text-template]: https://golang.org/pkg/text/template/
+[sprig]: https://masterminds.github.io/sprig/
diff --git a/docs/docs/segment_nightscout.md b/docs/docs/segment_nightscout.md
new file mode 100644
index 00000000..54456408
--- /dev/null
+++ b/docs/docs/segment_nightscout.md
@@ -0,0 +1,88 @@
+---
+id: nightscout
+title: Nightscout
+sidebar_label: Nightscout
+---
+
+## What
+
+[Nightscout][nightscout] (CGM in the Cloud) is an open source,
+DIY project that allows real time access to a CGM data via an HTTP REST API. It
+is used for secure remote viewing of blood sugar data from anywhere...including
+OhMyPosh segments on the command line!
+
+## Sample Configuration
+
+This example is using mg/dl, you'll want change the numbers for mmol. Your idea
+of "high" or "low" is different from others. You'll also want to think about
+your background and foreground colors. Don't use white text on a yellow
+background, for example.
+
+The foreground_templates example below could be set to just a single color,
+if that color is visible against any of your backgrounds.
+
+```json
+{
+  "type": "nightscout",
+  "style": "diamond",
+  "foreground": "#ffffff",
+  "background": "#ff0000",
+  "background_templates": [
+    "{{ if gt .Sgv 150 }}#FFFF00{{ end }}",
+    "{{ if lt .Sgv 60 }}#FF0000{{ end }}",
+    "#00FF00"
+  ],
+  "foreground_templates": [
+    "{{ if gt .Sgv 150 }}#000000{{ end }}",
+    "{{ if lt .Sgv 60 }}#000000{{ end }}",
+    "#000000"
+  ],
+
+  "leading_diamond": "",
+  "trailing_diamond": "\uE0B0",
+  "properties": {
+    "url": "https://YOURNIGHTSCOUTAPP.herokuapp.com/api/v1/entries.json?count=1&token=APITOKENFROMYOURADMIN",
+    "http_timeout": 1500,
+    "template": " {{.Sgv}}{{.TrendIcon}}"
+  }
+},
+```
+
+## Properties
+
+- url: `string` - Your Nightscout URL, inclding the full path to entries.json
+  AND count=1 AND token. Example above. You'll know this works if you can curl
+  it yourself and get a single value. - defaults to ``
+- http_timeout: `int` - How long do you want to wait before you want to see
+  your prompt more than your sugar? I figure a half second is a good default -
+  defaults to 500ms
+- template: `string` - a go [text/template][go-text-template] template extended
+  with [sprig][sprig] utilizing the properties below.
+  See the example above where I added a syringe.
+  You can change the icon, put the trend elsewhere, add text, however you like!
+  Make sure your NerdFont has the glyph you want or search for one
+  at nerdfonts.com
+- NSCacheTimeout: `int` in minutes - How long do you want your numbers cached? -
+  defaults to 5 min
+
+- NOTE: You can change the icons for trend, put the trend elsewhere, add text,
+  however you like!
+  Make sure your NerdFont has the glyph you want or search for one at
+  nerdfonts.com
+- DoubleUpIcon - defaults to ↑↑
+- SingleUpIcon - defaults to ↑
+- FortyFiveUpIcon - defaults to ↗
+- FlatIcon - defaults to →
+- FortyFiveDownIcon - defaults to ↘
+- SingleDownIcon - defaults to ↓
+- DoubleDownIcon - defaults to ↓↓
+
+## Template Properties
+
+- .Sgv: `int` - Your Serum Glucose Value (your sugar)
+- .TrendIcon: `string` - By default, this will be something like ↑↑ or ↘ etc but you can
+  override them with any glpyh as seen above
+
+[go-text-template]: https://golang.org/pkg/text/template/
+[sprig]: https://masterminds.github.io/sprig/
+[nightscout]: http://www.nightscout.info/
diff --git a/docs/sidebars.js b/docs/sidebars.js
index 693f61c9..b7d808ad 100644
--- a/docs/sidebars.js
+++ b/docs/sidebars.js
@@ -55,6 +55,7 @@ module.exports = {
         "julia",
         "kubectl",
         "nbgv",
+        "nightscout",
         "node",
         "os",
         "owm",
@@ -71,6 +72,7 @@ module.exports = {
         "terraform",
         "text",
         "time",
+        "wifi",
         "ytm",
       ],
     },
diff --git a/src/segment.go b/src/segment.go
index 2b0dfd58..3c2bfe09 100644
--- a/src/segment.go
+++ b/src/segment.go
@@ -130,6 +130,10 @@ const (
 	Angular SegmentType = "angular"
 	// PHP writes which php version is currently active
 	PHP SegmentType = "php"
+	// Nightscout is an open source diabetes system
+	Nightscout SegmentType = "nightscout"
+	// WiFi writes details about the current WiFi connection
+	WiFi SegmentType = "wifi"
 )
 
 func (segment *Segment) string() string {
@@ -263,6 +267,8 @@ func (segment *Segment) mapSegmentWithWriter(env environmentInfo) error {
 		SysInfo:       &sysinfo{},
 		Angular:       &angular{},
 		PHP:           &php{},
+		Nightscout:    &nightscout{},
+		WiFi:          &wifi{},
 	}
 	if writer, ok := functions[segment.Type]; ok {
 		props := &properties{
diff --git a/src/segment_nightscout.go b/src/segment_nightscout.go
new file mode 100644
index 00000000..1047486b
--- /dev/null
+++ b/src/segment_nightscout.go
@@ -0,0 +1,134 @@
+package main
+
+import (
+	"encoding/json"
+)
+
+// segment struct, makes templating easier
+type nightscout struct {
+	props *properties
+	env   environmentInfo
+	// array of nightscoutData (is often just 1, and we will pick the ZEROeth)
+
+	Sgv       int64
+	Direction string
+
+	TrendIcon string
+}
+
+const (
+	// Your complete Nightscout URL and APIKey like this
+	URL Property = "url"
+
+	DoubleUpIcon      Property = "doubleup_icon"
+	SingleUpIcon      Property = "singleup_icon"
+	FortyFiveUpIcon   Property = "fortyfiveup_icon"
+	FlatIcon          Property = "flat_icon"
+	FortyFiveDownIcon Property = "fortyfivedown_icon"
+	SingleDownIcon    Property = "singledown_icon"
+	DoubleDownIcon    Property = "doubledown_icon"
+
+	NSCacheTimeout Property = "cache_timeout"
+)
+
+type nightscoutData struct {
+	Sgv       int64  `json:"sgv"`
+	Direction string `json:"direction"`
+}
+
+func (ns *nightscout) enabled() bool {
+	data, err := ns.getResult()
+	if err != nil {
+		return false
+	}
+	ns.Sgv = data.Sgv
+	ns.Direction = data.Direction
+
+	ns.TrendIcon = ns.getTrendIcon()
+
+	return true
+}
+
+func (ns *nightscout) getTrendIcon() string {
+	switch ns.Direction {
+	case "DoubleUp":
+		return ns.props.getString(DoubleUpIcon, "↑↑")
+	case "SingleUp":
+		return ns.props.getString(SingleUpIcon, "↑")
+	case "FortyFiveUp":
+		return ns.props.getString(FortyFiveUpIcon, "↗")
+	case "Flat":
+		return ns.props.getString(FlatIcon, "→")
+	case "FortyFiveDown":
+		return ns.props.getString(FortyFiveDownIcon, "↘")
+	case "SingleDown":
+		return ns.props.getString(SingleDownIcon, "↓")
+	case "DoubleDown":
+		return ns.props.getString(DoubleDownIcon, "↓↓")
+	default:
+		return ""
+	}
+}
+
+func (ns *nightscout) string() string {
+	segmentTemplate := ns.props.getString(SegmentTemplate, "{{.Sgv}}")
+	template := &textTemplate{
+		Template: segmentTemplate,
+		Context:  ns,
+		Env:      ns.env,
+	}
+	text, err := template.render()
+	if err != nil {
+		return err.Error()
+	}
+
+	return text
+}
+
+func (ns *nightscout) getResult() (*nightscoutData, error) {
+	url := ns.props.getString(URL, "")
+	// natural and understood NS timeout is 5, anything else is unusual
+	cacheTimeout := ns.props.getInt(NSCacheTimeout, 5)
+	response := &nightscoutData{}
+	if cacheTimeout > 0 {
+		// check if data stored in cache
+		val, found := ns.env.cache().get(url)
+		// we got something from the cache
+		if found {
+			err := json.Unmarshal([]byte(val), response)
+			if err != nil {
+				return nil, err
+			}
+			return response, nil
+		}
+	}
+
+	httpTimeout := ns.props.getInt(HTTPTimeout, DefaultHTTPTimeout)
+
+	body, err := ns.env.doGet(url, httpTimeout)
+	if err != nil {
+		return &nightscoutData{}, err
+	}
+	var arr []*nightscoutData
+	err = json.Unmarshal(body, &arr)
+	if err != nil {
+		return &nightscoutData{}, err
+	}
+
+	firstelement := arr[0]
+	firstData, err := json.Marshal(firstelement)
+	if err != nil {
+		return &nightscoutData{}, err
+	}
+
+	if cacheTimeout > 0 {
+		// persist new sugars in cache
+		ns.env.cache().set(url, string(firstData), cacheTimeout)
+	}
+	return firstelement, nil
+}
+
+func (ns *nightscout) init(props *properties, env environmentInfo) {
+	ns.props = props
+	ns.env = env
+}
diff --git a/src/segment_wifi.go b/src/segment_wifi.go
new file mode 100644
index 00000000..10e10f49
--- /dev/null
+++ b/src/segment_wifi.go
@@ -0,0 +1,114 @@
+package main
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+type wifi struct {
+	props          *properties
+	env            environmentInfo
+	Connected      bool
+	State          string
+	SSID           string
+	RadioType      string
+	Authentication string
+	Channel        int
+	ReceiveRate    int
+	TransmitRate   int
+	Signal         int
+}
+
+const (
+	defaultTemplate = "{{ if .Connected }}\uFAA8{{ else }}\uFAA9{{ end }}{{ if .Connected }}{{ .SSID }} {{ .Signal }}% {{ .ReceiveRate }}Mbps{{ else }}{{ .State }}{{ end }}"
+)
+
+func (w *wifi) enabled() bool {
+	// This segment only supports Windows/WSL for now
+	if w.env.getPlatform() != windowsPlatform && !w.env.isWsl() {
+		return false
+	}
+
+	// Bail out of no netsh command found
+	cmd := "netsh.exe"
+	if !w.env.hasCommand(cmd) {
+		return false
+	}
+
+	// Attempt to retrieve output from netsh command
+	cmdResult, err := w.env.runCommand(cmd, "wlan", "show", "interfaces")
+	displayError := w.props.getBool(DisplayError, false)
+	if err != nil && displayError {
+		w.State = fmt.Sprintf("WIFI ERR: %s", err)
+		return true
+	}
+	if err != nil {
+		return false
+	}
+
+	// Extract data from netsh cmdResult
+	parseNetshCmdResult(cmdResult, w)
+
+	return true
+}
+
+func (w *wifi) string() string {
+	segmentTemplate := w.props.getString(SegmentTemplate, defaultTemplate)
+	template := &textTemplate{
+		Template: segmentTemplate,
+		Context:  w,
+		Env:      w.env,
+	}
+	text, err := template.render()
+	if err != nil {
+		return err.Error()
+	}
+
+	return text
+}
+
+func (w *wifi) init(props *properties, env environmentInfo) {
+	w.props = props
+	w.env = env
+}
+
+func parseNetshCmdResult(netshCmdResult string, w *wifi) {
+	lines := strings.Split(netshCmdResult, "\n")
+	for _, line := range lines {
+		matches := strings.Split(line, " : ")
+		if len(matches) != 2 {
+			continue
+		}
+		name := strings.TrimSpace(matches[0])
+		value := strings.TrimSpace(matches[1])
+
+		switch name {
+		case "State":
+			w.State = value
+			w.Connected = value == "connected"
+		case "SSID":
+			w.SSID = value
+		case "Radio type":
+			w.RadioType = value
+		case "Authentication":
+			w.Authentication = value
+		case "Channel":
+			if intValue, err := strconv.Atoi(value); err == nil {
+				w.Channel = intValue
+			}
+		case "Receive rate (Mbps)":
+			if intValue, err := strconv.Atoi(strings.Split(value, ".")[0]); err == nil {
+				w.ReceiveRate = intValue
+			}
+		case "Transmit rate (Mbps)":
+			if intValue, err := strconv.Atoi(strings.Split(value, ".")[0]); err == nil {
+				w.TransmitRate = intValue
+			}
+		case "Signal":
+			if intValue, err := strconv.Atoi(strings.TrimRight(value, "%")); err == nil {
+				w.Signal = intValue
+			}
+		}
+	}
+}
diff --git a/themes/atomic.omp.json b/themes/atomic.omp.json
index deed7fec..e2b90d86 100644
--- a/themes/atomic.omp.json
+++ b/themes/atomic.omp.json
@@ -38,9 +38,9 @@
           "background": "#FF9248",
           "properties": {
             "home_icon": "",
-            "folder_icon": " \uf413 ",
+            "folder_icon": " \uf07b ",
             "style": "folder",
-            "prefix": " \ufc6e\uf553",
+            "prefix": " \uf07b\uf553",
             "enable_hyperlink": true
           }
         },
@@ -252,10 +252,10 @@
           "foreground": "#222222",
           "background": "#b2bec3",
           "properties": {
-            "postfix": "<#262626>\uE0B2</>",
-            "windows": "\ue27f ",
-            "macos": "\ue27f ",
-            "linux": "\ue27f "
+            "postfix": "<#262626> \uE0B2</>",
+            "windows": "\ue27f",
+            "macos": "\ue27f",
+            "linux": "\ue27f"
           }
         },
         {
@@ -264,17 +264,17 @@
           "invert_powerline": true,
           "leading_diamond": "\uE0B2",
           "trailing_diamond": "",
-          "foreground": "#ffffff",
+          "foreground": "#262626",
           "background": "#f36943",
           "properties": {
             "battery_icon": "",
-            "discharging_icon": " ",
-            "charging_icon": " ",
-            "charged_icon": " ",
+            "discharging_icon": " ",
+            "charging_icon": "\uf1e6 ",
+            "charged_icon": "\uf58e ",
             "color_background": true,
             "charged_color": "#33DD2D",
-            "charging_color": "lightYellow",
-            "discharging_color": "#ff5722",
+            "charging_color": "#b8e994",
+            "discharging_color": "#fff34e",
             "postfix": " <#262626>\uE0B2</>"
           }
         },
diff --git a/themes/atomicBit.omp.json b/themes/atomicBit.omp.json
new file mode 100644
index 00000000..4647c2c4
--- /dev/null
+++ b/themes/atomicBit.omp.json
@@ -0,0 +1,250 @@
+{
+  "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
+  "final_space": false,
+  "osc99": true,
+  "blocks": [
+    {
+      "type": "prompt",
+      "alignment": "left",
+      "segments": [
+        {
+          "type": "session",
+          "style": "plain",
+          "properties": {
+            "user_info_separator": "<#ff5555>@</>",
+            "prefix": "╭─[",
+            "postfix": "]─",
+            "user_color": "#ffff55",
+            "host_color": "#55ff55"
+          }
+        },
+        {
+          "type": "path",
+          "style": "plain",
+          "foreground": "#ff5555",
+          "properties": {
+            "prefix": "<#ffffff>[</>",
+            "postfix": "<#ffffff>]</>",
+            "style": "folder",
+            "enable_hyperlink": true
+          }
+        },
+        {
+          "type": "root",
+          "style": "plain",
+          "foreground": "#FF9248",
+          "properties": {
+            "prefix": "<#ffffff>─(</>",
+            "postfix": "<#ffffff>)</>",
+            "root_icon": "#"
+          }
+        }
+      ]
+    },
+    {
+      "type": "prompt",
+      "alignment": "right",
+      "segments": [
+        {
+          "type": "node",
+          "style": "plain",
+          "foreground": "#3C873A",
+          "properties": {
+            "prefix": "<#ffffff>(</>",
+            "postfix": "<#ffffff>)</>",
+            "display_package_manager": true,
+            "yarn_icon": " <#348cba></>",
+            "npm_icon": " <#cc3a3a></> "
+          }
+        },
+        {
+          "type": "python",
+          "style": "plain",
+          "foreground": "#FFE873",
+          "properties": {
+            "prefix": "<#ffffff>(</>",
+            "postfix": "<#ffffff>)</>"
+          }
+        },
+        {
+          "type": "java",
+          "style": "plain",
+          "foreground": "#ec2729",
+          "properties": {
+            "prefix": "<#ffffff>(</>",
+            "postfix": "<#ffffff>)</>"
+          }
+        },
+        {
+          "type": "dotnet",
+          "style": "plain",
+          "foreground": "#0d6da8",
+          "properties": {
+            "prefix": "<#ffffff>(</>",
+            "postfix": "<#ffffff>)</>"
+          }
+        },
+        {
+          "type": "go",
+          "style": "plain",
+          "foreground": "#06aad5",
+          "properties": {
+            "prefix": "<#ffffff>(</>",
+            "postfix": "<#ffffff>)</>"
+          }
+        },
+        {
+          "type": "rust",
+          "style": "plain",
+          "foreground": "#925837",
+          "properties": {
+            "prefix": "<#ffffff>(</>",
+            "postfix": "<#ffffff>)</>"
+          }
+        },
+        {
+          "type": "dart",
+          "style": "plain",
+          "foreground": "#055b9c",
+          "properties": {
+            "prefix": "<#ffffff>(</>",
+            "postfix": "<#ffffff>)</>"
+          }
+        },
+        {
+          "type": "angular",
+          "style": "plain",
+          "foreground": "#ce092f",
+          "properties": {
+            "prefix": "<#ffffff>(</>",
+            "postfix": "<#ffffff>)</>"
+          }
+        },
+        {
+          "type": "julia",
+          "style": "plain",
+          "foreground": "#359a25",
+          "properties": {
+            "prefix": "<#ffffff>(</>",
+            "postfix": "<#ffffff>)</>"
+          }
+        },
+        {
+          "type": "ruby",
+          "style": "plain",
+          "foreground": "#9c1006",
+          "properties": {
+            "prefix": "<#ffffff>(</>",
+            "postfix": "<#ffffff>)</>"
+          }
+        },
+        {
+          "type": "azfunc",
+          "style": "plain",
+          "foreground": "#5398c2",
+          "properties": {
+            "prefix": "<#ffffff>(</>",
+            "postfix": "<#ffffff>)</>"
+          }
+        },
+        {
+          "type": "aws",
+          "style": "plain",
+          "foreground": "#faa029",
+          "properties": {
+            "prefix": "<#ffffff>(</>",
+            "postfix": "<#ffffff>)</>",
+            "template": "{{.Profile}}{{if .Region}}@{{.Region}}{{end}}"
+          }
+        },
+        {
+          "type": "kubectl",
+          "style": "plain",
+          "foreground": "#316ce4",
+          "properties": {
+            "prefix": "<#ffffff>(</>",
+            "postfix": "<#ffffff>)</>",
+            "template": "{{.Context}}{{if .Namespace}} :: {{.Namespace}}{{end}}"
+          }
+        },
+        {
+          "type": "os",
+          "style": "plain",
+          "foreground": "#ffffff",
+          "properties": {
+            "prefix": "<#ffffff>(</>",
+            "postfix": "<#ffffff>)─</>",
+            "windows": "<#ffffff>\ue27f</>",
+            "macos": "<#ffffff>\ue27f</>",
+            "linux": "<#ffffff>\ue27f</>"
+          }
+        },
+        {
+          "type": "battery",
+          "style": "plain",
+          "properties": {
+            "discharging_icon": " ",
+            "charging_icon": "\uf1e6 ",
+            "charged_icon": "\uf58e ",
+            "color_foreground": true,
+            "charged_color": "#33DD2D",
+            "charging_color": "#40c4ff",
+            "discharging_color": "#FFFB38",
+            "prefix": "<#ffffff>[</>",
+            "postfix": "%<#ffffff>]─</>"
+          }
+        },
+        {
+          "type": "time",
+          "style": "plain",
+          "foreground": "#55ffff",
+          "properties": {
+            "prefix": "<#ffffff>[</>",
+            "postfix": "<#ffffff>]</>",
+            "time_format": "_2,15:04"
+        }
+        }
+      ]
+    },
+    {
+      "type": "prompt",
+      "alignment": "left",
+      "newline": true,
+      "segments": [
+        {
+          "type": "text",
+          "style": "plain",
+          "properties": {
+            "prefix": "",
+            "postfix": "",
+            "text": "╰─"
+          }
+        },
+        {
+          "type": "git",
+          "style": "plain",
+          "foreground": "#e0f8ff",
+          "properties": {
+            "prefix": "<#ffffff>[</>",
+            "postfix": "<#ffffff>]-</>",
+            "branch_icon": "\ue725 ",
+            "fetch_status": true,
+            "fetch_upstream_icon": true,
+            "template": "{{ .HEAD }}{{ if .Staging.Changed }}<#00AA00> ● {{ .Staging.String }}</>{{ end }}{{ if .Working.Changed }}<#D75F00> ● {{ .Working.String }}</>{{ end }}"
+          }
+        },
+        {
+          "type": "exit",
+          "style": "plain",
+          "foreground": "#ecf7fa",
+          "properties": {
+            "prefix": "\ue285ﬀ",
+            "display_exit_code": false,
+            "always_enabled": true,
+            "error_color": "#ef5350"
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/themes/night-owl.omp.json b/themes/night-owl.omp.json
index f76b3dd6..7270355e 100644
--- a/themes/night-owl.omp.json
+++ b/themes/night-owl.omp.json
@@ -5,6 +5,11 @@
   "console_title": true,
   "console_title_style": "template",
   "console_title_template": "{{ .Folder }}",
+  "transient_prompt": {
+    "background": "transparent",
+    "foreground": "#d6deeb",
+    "template": "\ue285 "
+  },
   "blocks": [
     {
       "type": "prompt",
@@ -100,6 +105,19 @@
             "postfix": ""
           }
         },
+        {
+          "type": "sysinfo",
+          "style": "diamond",
+          "leading_diamond": "\uE0B2",
+          "trailing_diamond": "<transparent,#8f43f3>\uE0B2</>",
+          "foreground": "#ffffff",
+          "background": "#8f43f3",
+          "properties": {
+            "prefix": " \uf85a ",
+            "postfix": "% ",
+            "template": "{{ round .PhysicalPercentUsed .Precision }}"
+          }
+        },
         {
           "type": "node",
           "style": "diamond",
@@ -133,12 +151,24 @@
           "leading_diamond": "\uE0B2",
           "trailing_diamond": "<transparent,#0e8ac8>\uE0B2</>",
           "foreground": "#ec2729",
-          "background": "#0e8ac8",
+          "background": "#ffffff",
           "properties": {
             "prefix": "\uE738 ",
             "postfix": ""
           }
         },
+        {
+          "type": "php",
+          "style": "diamond",
+          "leading_diamond": "\uE0B2",
+          "trailing_diamond": "<transparent,#787CB5>\uE0B2</>",
+          "foreground": "#000000",
+          "background": "#787CB5",
+          "properties": {
+            "prefix": "\ue73d ",
+            "postfix": ""
+          }
+        },
         {
           "type": "dotnet",
           "style": "diamond",
@@ -295,7 +325,7 @@
           "type": "spotify",
           "style": "diamond",
           "leading_diamond": "\uE0B6",
-          "trailing_diamond": "\uE0c6",
+          "trailing_diamond": "\uE0c6 ",
           "foreground": "#011627",
           "background": "#1DB954",
           "properties": {
@@ -309,16 +339,12 @@
           "foreground": "#22da6e",
           "foreground_templates": ["{{ if gt .Code 0 }}#ef5350{{ end }}"],
           "properties": {
+            "prefix": "",
             "template": "\ue285\ue285",
             "always_enabled": true
           }
         }
       ]
     }
-  ],
-  "transient_prompt": {
-    "background": "transparent",
-    "foreground": "#d6deeb",
-    "template": "\ue285 "
-  }
+  ]
 }
diff --git a/themes/schema.json b/themes/schema.json
index 9d3e397f..fdb02b58 100644
--- a/themes/schema.json
+++ b/themes/schema.json
@@ -180,7 +180,8 @@
             "owm",
             "sysinfo",
             "angular",
-            "php"
+            "php",
+            "wifi"
           ]
         },
         "style": {
@@ -1697,6 +1698,26 @@
               }
             }
           }
+        },
+        {
+          "if": {
+            "properties": {
+              "type": { "const": "wifi" }
+            }
+          },
+          "then": {
+            "title": "WiFi Segment",
+            "description": "https://ohmyposh.dev/docs/wifi",
+            "properties": {
+              "properties": {
+                "properties": {
+                  "template": {
+                    "$ref": "#/definitions/template"
+                  }
+                }
+              }
+            }
+          }
         }
       ]
     }


Test patch:
diff --git a/src/segment_nightscout_test.go b/src/segment_nightscout_test.go
new file mode 100644
index 00000000..b3286904
--- /dev/null
+++ b/src/segment_nightscout_test.go
@@ -0,0 +1,115 @@
+package main
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+const (
+	NSAPIURL = "FAKE"
+)
+
+func TestNSSegment(t *testing.T) {
+	cases := []struct {
+		Case            string
+		JSONResponse    string
+		ExpectedString  string
+		ExpectedEnabled bool
+		Template        string
+		Error           error
+	}{
+		{
+			Case: "Flat 150",
+			JSONResponse: `
+			[{"_id":"619d6fa819696e8ded5b2206","sgv":150,"date":1637707537000,"dateString":"2021-11-23T22:45:37.000Z","trend":4,"direction":"Flat","device":"share2","type":"sgv","utcOffset":0,"sysTime":"2021-11-23T22:45:37.000Z","mills":1637707537000}]`, // nolint:lll
+			Template:        " {{.Sgv}}{{.TrendIcon}}",
+			ExpectedString:  " 150→",
+			ExpectedEnabled: true,
+		},
+		{
+			Case: "DoubleDown 50",
+			JSONResponse: `
+			[{"_id":"619d6fa819696e8ded5b2206","sgv":50,"date":1637707537000,"dateString":"2021-11-23T22:45:37.000Z","trend":4,"direction":"DoubleDown","device":"share2","type":"sgv","utcOffset":0,"sysTime":"2021-11-23T22:45:37.000Z","mills":1637707537000}]`, // nolint:lll
+			Template:        " {{.Sgv}}{{.TrendIcon}}",
+			ExpectedString:  " 50↓↓",
+			ExpectedEnabled: true,
+		},
+		{
+			Case: "DoubleUp 250",
+			JSONResponse: `
+			[{"_id":"619d6fa819696e8ded5b2206","sgv":250,"date":1637707537000,"dateString":"2021-11-23T22:45:37.000Z","trend":4,"direction":"DoubleUp","device":"share2","type":"sgv","utcOffset":0,"sysTime":"2021-11-23T22:45:37.000Z","mills":1637707537000}]`, // nolint:lll
+			Template:        " {{.Sgv}}{{.TrendIcon}}",
+			ExpectedString:  " 250↑↑",
+			ExpectedEnabled: true,
+		},
+		{
+			Case: "SingleUp 130",
+			JSONResponse: `
+			[{"_id":"619d6fa819696e8ded5b2206","sgv":130,"date":1637707537000,"dateString":"2021-11-23T22:45:37.000Z","trend":4,"direction":"SingleUp","device":"share2","type":"sgv","utcOffset":0,"sysTime":"2021-11-23T22:45:37.000Z","mills":1637707537000}]`, // nolint:lll
+			Template:        " {{.Sgv}}{{.TrendIcon}}",
+			ExpectedString:  " 130↑",
+			ExpectedEnabled: true,
+		},
+		{
+			Case: "FortyFiveUp 174",
+			JSONResponse: `
+			[{"_id":"619d6fa819696e8ded5b2206","sgv":174,"date":1637707537000,"dateString":"2021-11-23T22:45:37.000Z","trend":4,"direction":"FortyFiveUp","device":"share2","type":"sgv","utcOffset":0,"sysTime":"2021-11-23T22:45:37.000Z","mills":1637707537000}]`, // nolint:lll
+			Template:        " {{.Sgv}}{{.TrendIcon}}",
+			ExpectedString:  " 174↗",
+			ExpectedEnabled: true,
+		},
+		{
+			Case: "FortyFiveDown 61",
+			JSONResponse: `
+			[{"_id":"619d6fa819696e8ded5b2206","sgv":61,"date":1637707537000,"dateString":"2021-11-23T22:45:37.000Z","trend":4,"direction":"FortyFiveDown","device":"share2","type":"sgv","utcOffset":0,"sysTime":"2021-11-23T22:45:37.000Z","mills":1637707537000}]`, // nolint:lll
+			Template:        " {{.Sgv}}{{.TrendIcon}}",
+			ExpectedString:  " 61↘",
+			ExpectedEnabled: true,
+		},
+		{
+			Case: "DoubleDown 50",
+			JSONResponse: `
+			[{"_id":"619d6fa819696e8ded5b2206","sgv":50,"date":1637707537000,"dateString":"2021-11-23T22:45:37.000Z","trend":4,"direction":"DoubleDown","device":"share2","type":"sgv","utcOffset":0,"sysTime":"2021-11-23T22:45:37.000Z","mills":1637707537000}]`, // nolint:lll
+			Template:        " {{.Sgv}}{{.TrendIcon}}",
+			ExpectedString:  " 50↓↓",
+			ExpectedEnabled: true,
+		},
+		{
+			Case:            "Error in retrieving data",
+			JSONResponse:    "nonsense",
+			Error:           errors.New("Something went wrong"),
+			ExpectedEnabled: false,
+		},
+	}
+
+	for _, tc := range cases {
+		env := &MockedEnvironment{}
+		props := &properties{
+			values: map[Property]interface{}{
+				CacheTimeout: 0,
+				URL:          "FAKE",
+			},
+		}
+
+		env.On("doGet", NSAPIURL).Return([]byte(tc.JSONResponse), tc.Error)
+
+		if tc.Template != "" {
+			props.values[SegmentTemplate] = tc.Template
+		}
+
+		ns := &nightscout{
+			props: props,
+			env:   env,
+		}
+
+		enabled := ns.enabled()
+		assert.Equal(t, tc.ExpectedEnabled, enabled, tc.Case)
+		if !enabled {
+			continue
+		}
+
+		assert.Equal(t, tc.ExpectedString, ns.string(), tc.Case)
+	}
+}
diff --git a/src/segment_wifi_test.go b/src/segment_wifi_test.go
new file mode 100644
index 00000000..5f2a1ab6
--- /dev/null
+++ b/src/segment_wifi_test.go
@@ -0,0 +1,138 @@
+package main
+
+import (
+	"errors"
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+type netshStringArgs struct {
+	state          string
+	ssid           string
+	radioType      string
+	authentication string
+	channel        int
+	receiveRate    int
+	transmitRate   int
+	signal         int
+}
+
+func getNetshString(args *netshStringArgs) string {
+	const netshString string = `
+	There is 1 interface on the system:
+
+	Name                   : Wi-Fi
+	Description            : Intel(R) Wireless-AC 9560 160MHz
+	GUID                   : 6bb8def2-9af2-4bd4-8be2-6bd54e46bdc9
+	Physical address       : d4:3b:04:e6:10:40
+	State                  : %s
+	SSID                   : %s
+	BSSID                  : 5c:7d:7d:82:c5:73
+	Network type           : Infrastructure
+	Radio type             : %s
+	Authentication         : %s
+	Cipher                 : CCMP
+	Connection mode        : Profile
+	Channel                : %d
+	Receive rate (Mbps)    : %d
+	Transmit rate (Mbps)   : %d
+	Signal                 : %d%%
+	Profile                : ohsiggy
+
+	Hosted network status  : Not available`
+
+	return fmt.Sprintf(netshString, args.state, args.ssid, args.radioType, args.authentication, args.channel, args.receiveRate, args.transmitRate, args.signal)
+}
+
+func TestWiFiSegment(t *testing.T) {
+	cases := []struct {
+		Case            string
+		ExpectedString  string
+		ExpectedEnabled bool
+		CommandNotFound bool
+		CommandOutput   string
+		CommandError    error
+		DisplayError    bool
+		Template        string
+		ExpectedState   string
+	}{
+		{
+			Case:            "not enabled on windows when netsh command not found",
+			ExpectedEnabled: false,
+			ExpectedString:  "",
+			CommandNotFound: true,
+		},
+		{
+			Case:            "not enabled on windows when netsh command fails",
+			ExpectedEnabled: false,
+			ExpectedString:  "",
+			CommandError:    errors.New("intentional testing failure"),
+		},
+		{
+			Case:            "enabled on windows with DisplayError=true",
+			ExpectedEnabled: true,
+			ExpectedString:  "WIFI ERR: intentional testing failure",
+			CommandError:    errors.New("intentional testing failure"),
+			DisplayError:    true,
+			Template:        "{{.State}}",
+		},
+		{
+			Case:            "enabled on windows with every property in template",
+			ExpectedEnabled: true,
+			ExpectedString:  "connected testing 802.11ac WPA2-Personal 99 500 400 80",
+			CommandOutput: getNetshString(&netshStringArgs{
+				state:          "connected",
+				ssid:           "testing",
+				radioType:      "802.11ac",
+				authentication: "WPA2-Personal",
+				channel:        99,
+				receiveRate:    500.0,
+				transmitRate:   400.0,
+				signal:         80,
+			}),
+			Template: "{{.State}} {{.SSID}} {{.RadioType}} {{.Authentication}} {{.Channel}} {{.ReceiveRate}} {{.TransmitRate}} {{.Signal}}",
+		},
+		{
+			Case:            "enabled on windows but wifi not connected",
+			ExpectedEnabled: true,
+			ExpectedString:  "disconnected",
+			CommandOutput: getNetshString(&netshStringArgs{
+				state: "disconnected",
+			}),
+			Template: "{{if not .Connected}}{{.State}}{{end}}",
+		},
+		{
+			Case:            "enabled on windows but template is invalid",
+			ExpectedEnabled: true,
+			ExpectedString:  "unable to create text based on template",
+			CommandOutput:   getNetshString(&netshStringArgs{}),
+			Template:        "{{.DoesNotExist}}",
+		},
+	}
+
+	for _, tc := range cases {
+		env := new(MockedEnvironment)
+		env.On("getPlatform", nil).Return(windowsPlatform)
+		env.On("isWsl", nil).Return(false)
+		env.On("hasCommand", "netsh.exe").Return(!tc.CommandNotFound)
+		env.On("runCommand", mock.Anything, mock.Anything).Return(tc.CommandOutput, tc.CommandError)
+
+		props := &properties{
+			values: map[Property]interface{}{
+				DisplayError:    tc.DisplayError,
+				SegmentTemplate: tc.Template,
+			},
+		}
+
+		w := &wifi{
+			env:   env,
+			props: props,
+		}
+
+		assert.Equal(t, tc.ExpectedEnabled, w.enabled(), tc.Case)
+		assert.Equal(t, tc.ExpectedString, w.string(), tc.Case)
+	}
+}