If you work from home using Microsoft Teams in a Virtual Desktop Infrastructure (VDI) environment (AVD, Windows365, Terminal Server, etc.) and want a presence light in Home Assistant, most existing solutions won’t work for you. They either require Graph API app registrations (needing IT admin consent), or assume Teams is running locally on your machine.
This guide gets around both of those problems by reading the Teams log file directly on the VDI VM and passing the status to your local PC via a redirected drive — no cloud, no API keys, no admin rights needed.
Requirements
- Local Windows PC (This may work with Mac, but I haven’t tested it)
- VDI environment with local C drive redirection enabled (\tsclient\C)
- New Microsoft Teams VDI client (not classic Teams)
- Home Assistant setup with a supported RGB light (tested with Yeelight)
- A long-lived access token from Home Assistant (see How to get long lived access token? - Home Assistant Community)
- Smart Light Entity ID (from Home Assistant)
How it Works
The new Teams client logs presence state changes to a log file on the VDI VM. A PowerShell script on the VM watches that file and writes the current status to a small text file on your local machine (via the redirected C drive that most VDI setups provide). A second PowerShell script on your local PC polls that file and calls the Home Assistant API to update your light.
| VDI VM | Local PC |
|---|---|
| Teams log changes | written to C:\Scripts\teams_status.txt |
| Watch-TeamsLog.ps1 writes last change to \tsclient\C\Scripts\teams_status.txt | Watch-TeamsStatus.ps1 polls every 5 seconds |
| calls Home Assistant API and changes the light |
Step 1 — Verify your Teams logs contain presence data
Before setting anything up, confirm the logs work on your VDI. Open PowerShell on the VDI and run:
Get-Content "$env:LocalAppData\Packages\MSTeams_8wekyb3d8bbwe\LocalCache\Microsoft\MSTeams\Logs\MSTeams_*.log" -Tail 10 | Select-String "status "
Change your Teams status and run it again. You should see lines like:
… SetTaskbarIconOverlay overlay description:No items, status Busy
… SetTaskbarIconOverlay overlay description:No items, status Available
The status strings this solution handles are:
| Teams status | Log string |
|---|---|
| Available | Available |
| Busy | Busy |
| Do Not Disturb | Do not disturb |
| Away / Be Right Back | Away |
| Appear Offline | Offline |
Note: “Be Right Back” and “Away” both show as
Awayin the log.
Step 2 — Create the folder on your local PC
Create C:\Scripts\ on your local machine. This is where the status file will land and where the local watcher script lives.
Step 3 — VDI watcher script
Save this to your FSLogix profile (or any path that roams with you) on the VDI, e.g. Documents\Scripts\Watch-TeamsLog.ps1. It requires no installation — pure PowerShell.
$logDir = "$env:LocalAppData\Packages\MSTeams_8wekyb3d8bbwe\LocalCache\Microsoft\MSTeams\Logs"
$outputFile = "\\tsclient\C\Scripts\teams_status.txt"
$statusPattern = [regex]"status (Available|Busy|Do not disturb|Away|Offline)"
$lastStatus = ""
Write-Host "Watching Teams logs for presence changes..."
while ($true) {
try {
$latestLog = Get-ChildItem "$logDir\MSTeams_*.log" -ErrorAction Stop |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
if ($latestLog) {
$lines = Get-Content $latestLog.FullName -Tail 100 -Encoding UTF8 -ErrorAction Stop
for ($i = $lines.Count - 1; $i -ge 0; $i--) {
$match = $statusPattern.Match($lines[$i])
if ($match.Success) {
$status = $match.Groups[1].Value.ToLower()
if ($status -ne $lastStatus) {
$lastStatus = $status
$status | Out-File -FilePath $outputFile -Encoding UTF8 -NoNewline
Write-Host "$(Get-Date -Format 'HH:mm:ss'): Status changed to '$status'"
}
break
}
}
}
} catch {
Write-Host "$(Get-Date -Format 'HH:mm:ss'): Error - $_"
}
Start-Sleep -Seconds 5
}
Step 4 — Local PC watcher script
Save this to C:\Scripts\Watch-TeamsStatus.ps1 on your local machine.
Fill in your HomeAssistant IP , long-lived token, and light entity ID.
$statusFile = "C:\Scripts\teams_status.txt"
$haBaseUrl = "http://YOUR_HA_IP:8123"
$haToken = "YOUR_LONG_LIVED_TOKEN"
$entityId = "light.YOUR_LIGHT_ENTITY_ID"
$headers = @{
"Authorization" = "Bearer $haToken"
"Content-Type" = "application/json"
}
$colorMap = @{
"available" = @{ action = "on"; rgb = @(0, 255, 102) }
"busy" = @{ action = "on"; rgb = @(255, 0, 0) }
"do not disturb" = @{ action = "on"; rgb = @(150, 0, 50) }
"away" = @{ action = "on"; rgb = @(255, 210, 0) }
"offline" = @{ action = "off"; rgb = $null }
}
function Set-PresenceLight($status) {
$config = $colorMap[$status]
if (-not $config) { return }
if ($config.action -eq "off") {
$url = "$haBaseUrl/api/services/light/turn_off"
$body = @{ entity_id = $entityId } | ConvertTo-Json
} else {
$url = "$haBaseUrl/api/services/light/turn_on"
$body = @{
entity_id = $entityId
rgb_color = $config.rgb
brightness = 255
} | ConvertTo-Json
}
try {
Invoke-RestMethod -Uri $url -Method POST -Headers $headers -Body $body
Write-Host "$(Get-Date -Format 'HH:mm:ss'): Light updated for '$status'"
} catch {
Write-Host "$(Get-Date -Format 'HH:mm:ss'): HA call failed - $_"
}
}
$lastStatus = ""
Write-Host "Polling C:\Scripts\teams_status.txt for changes..."
while ($true) {
try {
if (Test-Path $statusFile) {
$status = (Get-Content $statusFile -Raw).Trim().ToLower()
if ($status -ne $lastStatus) {
$lastStatus = $status
Write-Host "$(Get-Date -Format 'HH:mm:ss'): Status changed to '$status'"
Set-PresenceLight $status
}
}
} catch {
Write-Host "$(Get-Date -Format 'HH:mm:ss'): Error - $_"
}
Start-Sleep -Seconds 5
}
Step 5 — Auto-Start both scripts
VDI — registry run key (roams with FSLogix)
Run this once on the VDI, in PowerShell. Because it writes to HKCU, it lives in your FSLogix profile and follows you to every VDI host automatically:
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name “TeamsStatusWatcher” -Value "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -File"C:\Users\YOUR_USERNAME\Documents\Scripts\Watch-TeamsLog.ps1""
Local PC — startup folder
Create a shortcut in %AppData%\Microsoft\Windows\Start Menu\Programs\Startup with this as the target:
powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -File "C:\Scripts\Watch-TeamsStatus.ps1"
Color Mapping
The scripts use Teams’ own status colors by default. Edit the $colorMap in the local script to change them:
| Status | Default Color | RGB |
|---|---|---|
| Available | Green | (0, 255, 102) |
| Busy | Red | (255, 0, 0) |
| Do Not Disturb | Dark red | (150, 0, 50) |
| Away / Be Right Back | Yellow | (255, 210, 0) |
| Offline | Light off | — |
This relies on an undocumented log format. If Microsoft changes the Teams log structure in a future update, the status pattern may need updating. The regex to look for is
status (Available|Busy|Do not disturb|Away|Offline)in the VDI script.
