Microsoft Teams Status

So I tried this with dcnoren suggestions on my own computer and it is working. Next i tried it on my work computer and no luck. The biggest problem seems to be Constrained Language mode, after I was able to bypass execution policy. I found that using powershell v2 will get around the constrained language mode, as it didn’t exist in v2. But other functions such as get-content -tail wont work as tail was implemented in v3. I didn’t go through all of the command not found exceptions but there seem to be a few.

Anyone get this working in a super restricted environment? No admin rights, can’t run exe, and constrained language mode…

I have a (mostly) working script that runs under AutoHotKey that can pull the status from the log file and send it to a HA webhook. This works without any elevated permissions and can be run by AHK portable (nothing to install).

ajobbins/AHK-Teams-Presence (github.com)

Key limitation is that I haven’t got it getting the status on initial script load (as it’s just tailing the log). Can work around by toggling the status initially, but welcome someone with better coding skills than I working out how to get the last status on initial load.

I am moving jobs and won’t be using Teams much longer but hopefully this is useful for those like me who can’t get access to the Graph API and who don’t have local admin permissions.

4 Likes

does any of the solutions here work on a mac?

1 Like

I’m now using a Mac and have moved to using the HA Companion app which can create sensors to show when your mic and camera in use. The only issue is that there is a big with M1 Macs where the camera is currently always showing as on, but the mic sensor works fine

1 Like

yes i immediately found out about the mac app. i have been using HA for 3 years and did not even know the mac app was launched for a whole year now. this is amazing! the possibilities are endless: i can raise my desk, set the mood lighting etc when i unlock my macbook

1 Like

This is awesome. I just hooked my LED “On a Call” sign (LED Matrix "On a Call" Message Sign) to my Teams activity sensor!

1 Like

I created an IoTLink Addon for MS Teams. It uses @Egglestron’s approach (Tail + RegEx). If anyone is interested you can find it here: GitHub - ledhed-jgh/IoTLink-Addons: Addons for IoTLink

I prefer IoTLink because my automation stuff lives in an isolated VLAN and can’t communicate directly with my PC. I was already using it to shutdown my PC, so it made sense to create the Addon and use what was already there rather than create Scheduled Tasks or PowerShell services.

1 Like

Thanks for doing this. Does this addon constantly poll or check the log file and therefore consume cpu cycles that would impact overall pc performance?
thanks

Its fairly efficient from the small amount of testing I’ve done. The code I used to watch the log file comes from a code snippet called Tail .NET. Essentially it checks the size of the log file every 1 second and if the filesize has changed it only reads the changed data (rather than loading the entire log file which can be quite large). I expect it to consume similar resource as the PowerShell based solutions (which also tail the log file).

Gotcha, thanks

maybe someone know what to do …
I`ve try to do it like EBOOZ wrote… instruction looks very easy but got error with last command in PowerShell

PS C:\Scripts> Start-Service -Name "Microsoft Teams Status Monitor"
Start-Service : Service 'Microsoft Teams Status Monitor (Microsoft Teams Status Monitor)' cannot be started due to the
following error: Cannot start service Microsoft Teams Status Monitor on computer '.'.
At line:1 char:1
+ Start-Service -Name "Microsoft Teams Status Monitor"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OpenError: (System.ServiceProcess.ServiceController:ServiceController) [Start-Service],
   ServiceCommandException
    + FullyQualifiedErrorId : CouldNotStartService,Microsoft.PowerShell.Commands.StartServiceCommand

PS C:\Scripts>

Great idea using the Teams log, that’s exactly what I was looking for. However I came with a different way of extracting the status from Teams, I’m looking for “Setting the taskbar overlay icon”. Here is the Powershell script I created, it works well. I find that querying the log every 5 seconds and grabbing the last 2000 lines is sufficient so far.

$token = 'mytoken'
$headers = @{"Authorization" = "Bearer $token"; }

while ($true) {
    $tl = Get-Content "$env:APPDATA\Microsoft\Teams\logs.txt" -tail 2000 | ? { $_ -like '*Setting the taskbar overlay icon*' }
    $lastline = $tl[$tl.Count - 1]
    $i = $lastline.IndexOf('Setting the taskbar overlay icon - ') + 35
    $CurrentState = $lastline.SubString($i).Trim()

    $params = @{
        "state"      = "$CurrentState";
        "attributes" = @{
            "friendly_name" = "Microsoft Teams";
            "icon"          = "mdi:microsoft-teams";
        }
    }
    
    Invoke-RestMethod -Uri 'https://homeassistant:8123/api/states/sensor.teams_status' -Method POST -Headers $headers -Body ($params | ConvertTo-Json) -ContentType "application/json" 
    Start-Sleep 5
}

4 Likes

Like your solution. :+1:

Got this working on my work machine.
For anyone wanting to automate this.

  1. Open “Task Scheduler
  2. Create Task
  3. Give it a “Name” and importantly tick “Run with highest privileges
  4. Triggers tab
    a. New
    b. Dropdown “Begin task” set to “On log on
  5. Action tab
    a. New
    b. “Start a Program
    c. Program/script: “C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe”
    d. Add arguments -NonInteractive -WindowStyle Hidden -File "C:\path\to\the\script\on\your\machine\teams_meeting_presence.ps1"

Note: you might need to look for Powershell on your machine with Get-Command powershell.exe from a powershell session.

3 Likes

Hi all,

I got it all working, very nice job of you all !!! (I use the scheduler task to start the script when I boot). But, I have one more question: If reboot Home Assistant, my status is changed to ‘Unknown’ and get changed again when I run the script on my laptop.

Does anyone have a clue on how to ‘update’ the status once HA gets rebooted ?

Thanks in advance !!

Kr,

Bart

@weemaba999 I don’t see where the problem is.

If you restart Home Assistant then it needs to wait for the script to send the status.
Personally, I wouldn’t want HA to assume I am not in a meeting.

Can you explain what the issue is?

Well, how long does it take after reboot of HA for the script to send once again the status of my teams agent ? It seems that, unless I manually execute the script, the status will remain on ‘UNKNOWN’

Kr,

Bart

Looking at the last script it sends a status update every 5 seconds to HA - regardless of the state of your Teams. So that is odd …

Hi Fanuch,

I’m using the Get-TeamStatus with following content:

<#
.NOTES
    Name: Get-TeamsStatus.ps1
    Author: Danny de Vries
    Requires: PowerShell v2 or higher
    Version History: https://github.com/EBOOZ/TeamsStatus/commits/main
.SYNOPSIS
    Sets the status of the Microsoft Teams client to Home Assistant.
.DESCRIPTION
    This script is monitoring the Teams client logfile for certain changes. It
    makes use of two sensors that are created in Home Assistant up front.
    The status entity (sensor.teams_status by default) displays that availability 
    status of your Teams client based on the icon overlay in the taskbar on Windows. 
    The activity entity (sensor.teams_activity by default) shows if you
    are in a call or not based on the App updates deamon, which is paused as soon as 
    you join a call.
.PARAMETER SetStatus
    Run the script with the SetStatus-parameter to set the status of Microsoft Teams
    directly from the commandline.
.EXAMPLE
    .\Get-TeamsStatus.ps1 -SetStatus "Offline"
#>
# Configuring parameter for interactive run
Param($SetStatus)

# Import Settings PowerShell script
. ($PSScriptRoot + "\Settings.ps1")

$headers = @{"Authorization"="Bearer $HAToken";}

# Run the script when a parameter is used and stop when done
If($null -ne $SetStatus){
    Write-Host ("Setting Microsoft Teams status to "+$SetStatus+":")
    $params = @{
     "state"="$SetStatus";
     "attributes"= @{
        "friendly_name"="$entityStatusName";
        "icon"="mdi:microsoft-teams";
        }
     }
	 
    $params = $params | ConvertTo-Json
    Invoke-RestMethod -Uri "$HAUrl/api/states/$entityStatus" -Method POST -Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes($params)) -ContentType "application/json"
    break
}

If ($env:APPDATA -match "C:\\Users") {
	$path = "$env:APPDATA\Microsoft\Teams\logs.txt"
}
Else {
	$path = "C:\Gebruikers\bart.weemaels$([char]0X0040)cegeka.be\AppData\Roaming\Microsoft\Teams\logs.txt"
}

# Start monitoring the Teams logfile when no parameter is used to run the script
Get-Content -Path $path -Tail 10000 -ReadCount 0 -Encoding Utf8 -Wait | % {
    
    # Get Teams Logfile and last icon overlay status
    $TeamsStatus = $_ | Select-String -Pattern `
        'Setting the taskbar overlay icon -',`
        'StatusIndicatorStateService: Added' | Select-Object -Last 1

    # Get Teams Logfile and last app update deamon status
    $TeamsActivity = $_ | Select-String -Pattern `
        'Resuming daemon App updates',`
        'Pausing daemon App updates',`
        'SfB:TeamsNoCall',`
        'SfB:TeamsPendingCall',`
        'SfB:TeamsActiveCall',`
        'name: desktop_call_state_change_send, isOngoing' | Select-Object -Last 1

    # Get Teams application process
    $TeamsProcess = Get-Process -Name Teams -ErrorAction SilentlyContinue

    # Check if Teams is running and start monitoring the log if it is
    If ($null -ne $TeamsProcess) {
        If($TeamsStatus -eq $null){ }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgAvailable*" -or `
            $TeamsStatus -like "*StatusIndicatorStateService: Added Available*" -or `
            $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: Available -> NewActivity*") {
            $Status = $lgAvailable
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgBusy*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added Busy*" -or `
                $TeamsStatus -like "*Setting the taskbar overlay icon - $lgOnThePhone*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added OnThePhone*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: Busy -> NewActivity*") {
            $Status = $lgBusy
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgAway*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added Away*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: Away -> NewActivity*") {
            $Status = $lgAway
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgBeRightBack*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added BeRightBack*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: BeRightBack -> NewActivity*") {
            $Status = $lgBeRightBack
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgDoNotDisturb *" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added DoNotDisturb*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: DoNotDisturb -> NewActivity*") {
            $Status = $lgDoNotDisturb
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgFocusing*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added Focusing*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: Focusing -> NewActivity*") {
            $Status = $lgFocusing
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgPresenting*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added Presenting*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: Presenting -> NewActivity*") {
            $Status = $lgPresenting
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgInAMeeting*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added InAMeeting*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: InAMeeting -> NewActivity*") {
            $Status = $lgInAMeeting
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgOffline*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added Offline*") {
            $Status = $lgOffline
            Write-Host $Status
        }

        If($TeamsActivity -eq $null){ }
        ElseIf ($TeamsActivity -like "*Resuming daemon App updates*" -or `
            $TeamsActivity -like "*SfB:TeamsNoCall*" -or `
            $TeamsActivity -like "*name: desktop_call_state_change_send, isOngoing: false*") {
            $Activity = $lgNotInACall
            $ActivityIcon = $iconNotInACall
            Write-Host $Activity
        }
        ElseIf ($TeamsActivity -like "*Pausing daemon App updates*" -or `
            $TeamsActivity -like "*SfB:TeamsActiveCall*" -or `
            $TeamsActivity -like "*name: desktop_call_state_change_send, isOngoing: true*") {
            $Activity = $lgInACall
            $ActivityIcon = $iconInACall
            Write-Host $Activity
        }
    }
    # Set status to Offline when the Teams application is not running
    Else {
            $Status = $lgOffline
            $Activity = $lgNotInACall
            $ActivityIcon = $iconNotInACall
            Write-Host $Status
            Write-Host $Activity
    }

    # Call Home Assistant API to set the status and activity sensors
    If ($CurrentStatus -ne $Status -and $Status -ne $null) {
        $CurrentStatus = $Status

        $params = @{
        "state"="$CurrentStatus";
        "attributes"= @{
            "friendly_name"="$entityStatusName";
            "icon"="mdi:microsoft-teams";
            }
        }
        
        $params = $params | ConvertTo-Json
        Invoke-RestMethod -Uri "$HAUrl/api/states/$entityStatus" -Method POST -Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes($params)) -ContentType "application/json" 
    }

    If ($CurrentActivity -ne $Activity) {
        $CurrentActivity = $Activity

        $params = @{
        "state"="$Activity";
        "attributes"= @{
            "friendly_name"="$entityActivityName";
            "icon"="$ActivityIcon";
            }
        }
        $params = $params | ConvertTo-Json
        Invoke-RestMethod -Uri "$HAUrl/api/states/$entityActivity" -Method POST -Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes($params)) -ContentType "application/json" 
    }
        
}

Quick look at the code and it only sends state to HA when there is a change

So in your case, you restart HA but didn’t change Teams state it will show “Unavailable”.
The last script up there runs every 5 seconds so it would send state regardless.

If you change

If ($CurrentStatus -ne $Status -and $Status -ne $null) {

to

If ($Status -ne $null) {

Then it will send status every time the code runs. Or you could get this code to check if the state is unavailable and send an update if so.

Several ways to slice this cat.

Hi @fanuch,

Tried to adapt following your remarks, but still my teamsstatus gets an unknown state after reboot of Hass. I think the problem is that the start / sleep functionality doesn’t work somehow to periodically send the status over:

# Configuring parameter for interactive run
Param($SetStatus)

# Import Settings PowerShell script
. ($PSScriptRoot + "\Settings.ps1")

$headers = @{"Authorization"="Bearer $HAToken";}

# Run the script when a parameter is used and stop when done
If($null -ne $SetStatus){
    Write-Host ("Setting Microsoft Teams status to "+$SetStatus+":")
    $params = @{
     "state"="$SetStatus";
     "attributes"= @{
        "friendly_name"="$entityStatusName";
        "icon"="mdi:microsoft-teams";
        }
     }
	 
    $params = $params | ConvertTo-Json
    Invoke-RestMethod -Uri "$HAUrl/api/states/$entityStatus" -Method POST -Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes($params)) -ContentType "application/json"
    break
}

If ($env:APPDATA -match "C:\\Users") {
	$path = "$env:APPDATA\Microsoft\Teams\logs.txt"
}
Else {
	$path = "C:\Gebruikers\bart.weemaels$([char]0X0040)cegeka.be\AppData\Roaming\Microsoft\Teams\logs.txt"
}

# Start monitoring the Teams logfile when no parameter is used to run the script
Get-Content -Path $path -Tail 10000 -ReadCount 0 -Encoding Utf8 -Wait | % {
    
    # Get Teams Logfile and last icon overlay status
    $TeamsStatus = $_ | Select-String -Pattern `
        'Setting the taskbar overlay icon -',`
        'StatusIndicatorStateService: Added' | Select-Object -Last 1

    # Get Teams Logfile and last app update deamon status
    $TeamsActivity = $_ | Select-String -Pattern `
        'Resuming daemon App updates',`
        'Pausing daemon App updates',`
        'SfB:TeamsNoCall',`
        'SfB:TeamsPendingCall',`
        'SfB:TeamsActiveCall',`
        'name: desktop_call_state_change_send, isOngoing' | Select-Object -Last 1

    # Get Teams application process
    $TeamsProcess = Get-Process -Name Teams -ErrorAction SilentlyContinue

    # Check if Teams is running and start monitoring the log if it is
    If ($null -ne $TeamsProcess) {
        If($TeamsStatus -eq $null){ }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgAvailable*" -or `
            $TeamsStatus -like "*StatusIndicatorStateService: Added Available*" -or `
            $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: Available -> NewActivity*") {
            $Status = $lgAvailable
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgBusy*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added Busy*" -or `
                $TeamsStatus -like "*Setting the taskbar overlay icon - $lgOnThePhone*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added OnThePhone*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: Busy -> NewActivity*") {
            $Status = $lgBusy
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgAway*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added Away*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: Away -> NewActivity*") {
            $Status = $lgAway
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgBeRightBack*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added BeRightBack*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: BeRightBack -> NewActivity*") {
            $Status = $lgBeRightBack
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgDoNotDisturb *" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added DoNotDisturb*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: DoNotDisturb -> NewActivity*") {
            $Status = $lgDoNotDisturb
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgFocusing*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added Focusing*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: Focusing -> NewActivity*") {
            $Status = $lgFocusing
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgPresenting*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added Presenting*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: Presenting -> NewActivity*") {
            $Status = $lgPresenting
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgInAMeeting*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added InAMeeting*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added NewActivity (current state: InAMeeting -> NewActivity*") {
            $Status = $lgInAMeeting
            Write-Host $Status
        }
        ElseIf ($TeamsStatus -like "*Setting the taskbar overlay icon - $lgOffline*" -or `
                $TeamsStatus -like "*StatusIndicatorStateService: Added Offline*") {
            $Status = $lgOffline
            Write-Host $Status
        }

        If($TeamsActivity -eq $null){ }
        ElseIf ($TeamsActivity -like "*Resuming daemon App updates*" -or `
            $TeamsActivity -like "*SfB:TeamsNoCall*" -or `
            $TeamsActivity -like "*name: desktop_call_state_change_send, isOngoing: false*") {
            $Activity = $lgNotInACall
            $ActivityIcon = $iconNotInACall
            Write-Host $Activity
        }
        ElseIf ($TeamsActivity -like "*Pausing daemon App updates*" -or `
            $TeamsActivity -like "*SfB:TeamsActiveCall*" -or `
            $TeamsActivity -like "*name: desktop_call_state_change_send, isOngoing: true*") {
            $Activity = $lgInACall
            $ActivityIcon = $iconInACall
            Write-Host $Activity
        }
    }
    # Set status to Offline when the Teams application is not running
    Else {
            $Status = $lgOffline
            $Activity = $lgNotInACall
            $ActivityIcon = $iconNotInACall
            Write-Host $Status
            Write-Host $Activity
    }

    # Call Home Assistant API to set the status and activity sensors
#    If ($CurrentStatus -ne $Status -and $Status -ne $null) {
     If ($Status -ne $null) {
        $CurrentStatus = $Status

        $params = @{
        "state"="$CurrentStatus";
        "attributes"= @{
            "friendly_name"="$entityStatusName";
            "icon"="mdi:microsoft-teams";
            }
        }
        
        $params = $params | ConvertTo-Json
        Invoke-RestMethod -Uri "$HAUrl/api/states/$entityStatus" -Method POST -Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes($params)) -ContentType "application/json" 
    }

    If ($CurrentActivity -ne $Activity) {
        $CurrentActivity = $Activity

        $params = @{
        "state"="$Activity";
        "attributes"= @{
            "friendly_name"="$entityActivityName";
            "icon"="$ActivityIcon";
            }
        }
        $params = $params | ConvertTo-Json
        Invoke-RestMethod -Uri "$HAUrl/api/states/$entityActivity" -Method POST -Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes($params)) -ContentType "application/json"
	Start-Sleep 5 
    }
        
}

Kind regards,

Bart