I have my windows tablet PC setup as a kiosk with strict battery management rules, but there isn't a way to control the battery charge/discharge functions natively. So I have prepared the below driver configuration for Hubitat to read the PC's battery state, I then use RM to automatically charge it.
You have to set up a Task to run in Windows which posts a device battery report to a network shared location. The driver allows you to add your PCs as new devices, each device instance reads the designated powershell report output within Hubitat. Using the device's battery state, I allow it to run a 45%-75% charge cycle using rule machine and a smart plug, which enables my 2.5 hour battery to become a very responsive kiosk with a ~24 hour charge cycle banded within this 30% charge range with occasional use.
Windows 7-8-10-11 PC Setup:
Power Plan - Advanced Settings
Sleep - Allow Wake Timers
Settings - Sign In Options
If you've been away, Require Sign In - Never
Task Scheduler - Create Task
1. New Trigger - Repeat Task Every: (your pick) - Indefinitely
2. First New Action (Overwrite report with current date/time) - Start Program - "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
Add Arguments: Get-Date | Out-File -FilePath .\batteryreport.txt
Start In (shared network location to save the report)
3. Second New Action (Add battery percent to new report) - Start Program - "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
Add Arguments: WMIC PATH Win32_Battery Get EstimatedChargeRemaining | Out-File -FilePath .\batteryreport.txt -Append
Start In (shared network location to save the report)
4. Conditions - uncheck start only on AC && Allow Wake Computer
5. Settings - Allow Run task as soon as it is missed && Uncheck Stop after 3 days
I don't know your particular network file sharing setup, but the link to the report, when entered to a web browser on a different device on your network, should prompt download of the text file without username/password. Mine is posted to a NAS with sharing for that specific file (whose address is always static and the file overwritten by the scheduled windows task)
Hubitat Laptop Battery Driver:
/*
* Laptop Battery
*
*
* Change History:
*
* Date Source Version What URL
* ---- ------ ------- ---- ---
* 2022-01-22 pentalingual 0.1.0 Starting version
*
*/
import java.text.SimpleDateFormat
static String version() { return '0.1.0' }
metadata {
definition(
name: "Laptop Battery",
namespace: "pentalingual",
author: "Andrew Nunes",
) {
capability "Actuator"
capability "Refresh"
capability "Battery"
attribute "battery", "number"
attribute "lastCheckin", "Date"
attribute "lastRefresh", "Date"
}
}
preferences {
input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: false
input name: "txtEnable", type: "bool", title: "Enable descriptionText logging", defaultValue: true
input name: "decodeEnable", type: "bool", title: "Enable decoding/Clean non-ANSI characters", defaultValue: true
input name: "fileLocation", type: "string", title: "Link to the BatteryReport", defaultValue: null
input("autoUpdatehour", "number", title: "Enable automatic update every n hours (enter 0 for never)", defaultValue: 0, required: true, displayDuringSetup: true)
input("autoUpdatemin", "number", title: "Enable automatic update every n minutes (enter 0 for never)", defaultValue: 0, required: true, displayDuringSetup: true)
}
def logsOff() {
log.warn "debug logging disabled..."
device.updateSetting("logEnable", [value: "false", type: "bool"])
}
def installed() {
logging("installed()", 100)
unschedule()
refresh()
}
def updated() {
log.info "updated..."
log.warn "debug logging is: ${logEnable}"
log.warn "description logging is: ${txtEnable}"
if (logEnable) {
if (autoUpdatemin+autoUpdatehour ==0)log.warn("Update: Automatic Update DISABLED")
}
if (logEnable) {
if (autoUpdatemin+autoUpdatehour>0)log.debug("Update: Automatic Update enabled")
}
if (logEnable) runIn(1800, logsOff)
if (logEnable) log.debug("autoupdate: Next scheduled refresh set")
unschedule()
refresh()
schedUpdate()
}
def refresh() {
runCmd()
}
def schedUpdate() {
unschedule()
if (txtEnable) log.info("schedUpdate: Refresh schedule cleared ...")
if (autoUpdatemin+autoUpdatehour>0) {
if (txtEnable) log.debug("Update: Setting next scheduled refresh...")
if (autoUpdatemin+autoUpdatehour>0) {
if (autoUpdatemin>0) {
if (autoUpdatehour>0) {
schedule("0 0/${autoUpdatemin} * * * ?", refresh)
} else {
schedule("0 0/${autoUpdatemin} 0/${autoUpdatehour} * * ?", refresh)
}
} else {
schedule("0 0 0/${autoUpdatehour} * * ?", refresh)
}
}
if (autoUpdatemin+autoUpdatehour>0) log.info("Update: Next scheduled refresh set")
}
}
def runCmd() {
now = new Date()
sendEvent(name: "lastRefresh", value: now)
checkBattery()
}
def checkBattery() {
if (device.currentValue("lastRefresh") == (null)) {
runCmd()
} else {
batteryPreviouslySet = true
existingBatteryreport = "${battery}"
}
readFile()
}
def readFile(){
if (logEnable) log.debug("Downloading the newest report at ${fileLocation}")
uri = fileLocation
def queryResults = ""
def params = [
uri: uri,
contentType: "text/html; charset=ANSI",
headers: [
"Cookie": cookie
]
]
try {
httpGet(params) { resp ->
if(resp!= null) {
printLine = resp.getData() as String
printLines = printLine.readLines()
String BL = printLines[12]
String LCI = printLines[2]
if(decodeEnable) {
String Remover = BL.substring(0,1)
String BLC = BL.replaceAll(Remover, '')
int BLI = BLC.toInteger()
String LCIFinal = LCI.replaceAll(Remover, '')
Date LCIDate = Date.parse('EEEEE, MMMMM d, yyyy h:mm:ss aa', LCIFinal)
sendEvent(name: "lastCheckin", value: LCIDate.format('EEE MMM d HH:mm:ss z yyyy'), descriptionText: descriptionText)
sendEvent(name: "battery", value: BLI)
if (txtEnable) log.info("Battery reported as ${BLI}% on ${LCIDate}")
} else {
int BLI = BL.toInteger()
Date LCIDate = Date.parse('EEEEE, MMMMM d, yyyy h:mm:ss aa', LCI)
sendEvent(name: "lastCheckin", value: LCIDate.format('EEE MMM d HH:mm:ss z yyyy'), descriptionText: descriptionText)
sendEvent(name: "battery", value: BLI)
}
}
else {
log.error "Null Response"
}
}
} catch (exception) {
log.error "Read Error: ${exception.message}"
return null;
}
}