Skip to content

Commit

Permalink
Add several Functions and Supports Win 11 (#13)
Browse files Browse the repository at this point in the history
* Update lighthouse-v2-manager.py

* Update README.md

* Update README.md

* Update README.md

* Update lighthouse-v2-manager.py

import bleak, fixes win11 not working

* small bugfix
bleak.exc.BleakError: Device with address D7:4C:07:1D:8E:C1 was not found.
[20304] Failed to execute script lighthouse-v2-manager

* remove deprecated functions

https://bleak.readthedocs.io/en/latest/api/client.html#deprecated

* remove deprecated functions

https://bleak.readthedocs.io/en/latest/api/client.html#deprecated

* use var instead of string

* fix indent and improve code

Don't loop again if it is Valve BS

* Print list of BS end of discover mode

* Unified naming to LH

* formatting using black

--line-length=120

* add retrying function

---------

Co-authored-by: Skyro468 <32147348+Skyro468@users.noreply.github.com>
Co-authored-by: Muhlawor <17768574+Muhlawor@users.noreply.github.com>
Co-authored-by: Pixie Raver <PixieRaveGirl@gmail.com>
  • Loading branch information
4 people authored Aug 9, 2023
1 parent 7cfd5dc commit c5ef6a2
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 132 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
This fork is modified to add a toggle option so you can turn basestations on or off without having to use 2 seperate scripts for it.


# Manager for V2 Lighthouses by Valve/HTC
This python script helps you switch your Valve Lighthouses V2 on and into stand-by. Unfortunately, my Pimax 5K XR needs the lighthouses up and running before I turn it on, so the awesome work done by [@mann1x]( https://github.com/mann1x ) in his project [Pimax BS Manager](https://github.com/mann1x/pimax_bs_manager) is of little use to me. This may be different with your particular Pimax HMD.

Expand All @@ -23,7 +26,7 @@ The script provides three usage options: discovery, turning on and switch to sta
**Note:** if you installed the binary version, simply call the program by executing it. Instead of `python3 .\lighthouse-v2-manager.py` type `lighthouse-v2-manager.exe` in the command prompt window for the commands below.

### display instructions
If you call the executable/script with no command line arguments or with an invalid command, the useage instructions are returned. Choose one of the commands `discover`, `on` or `off` to interact with your Lighthouses V2.
If you call the executable/script with no command line arguments or with an invalid command, the useage instructions are returned. Choose one of the commands `discover`, `on`, `off` or `toggle` to interact with your Lighthouses V2.

**usage:** `python3 .\lighthouse-v2-manager.py`

Expand All @@ -48,6 +51,13 @@ If you want to switch a lighthouse back on, specify either "on" as first argumen

The lighthouses LED will power up. As soon as it's stabilized, the LED turns solid green.

### toggle lighthouse states
If you want to toggle the state of the basestation(s), specify "toggle" as the first argument then each MAC address consecutively as further arguments like so:

**usage:** `python3 .\lighthouse-v2-manager.py toggle aa:aa:aa:aa:aa:aa bb:bb:bb:bb:bb:bb ...`

The script connects to each basestation specified, then gets if they are active or in standby mode. Then based on the state it will send a command to swap it to the opposite state. So if it is on, it will be toggled to standby, and vice versa. The point of this argument is to reduce the ammount of scripts you would need to just one to turn the basestations on or off.

### Hard-coding your Lighthouses' MAC addresses
Inside the script, you can edit the list `lh_macs` to contain the MAC addresses of your lighthouses as strings. Doing so allows a shorter command line interaction:
* `python3 .\lighthouse-v2-manager.py on`
Expand Down
341 changes: 210 additions & 131 deletions lighthouse-v2-manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,154 +4,233 @@
import sys
import re
import os
import time
from bleak import BleakScanner, BleakClient

__PWR_SERVICE = "00001523-1212-efde-1523-785feabcd124"
__PWR_SERVICE = "00001523-1212-efde-1523-785feabcd124"
__PWR_CHARACTERISTIC = "00001525-1212-efde-1523-785feabcd124"
__PWR_ON = bytearray([0x01])
__PWR_STANDBY = bytearray([0x00])
__PWR_ON = bytearray([0x01])
__PWR_STANDBY = bytearray([0x00])

command = ""
lh_macs = [] # hard code mac addresses here if you want, otherwise specify in command line
lh_macs = [] # hard code mac addresses here if you want, otherwise specify in command line

print(" ")
print("=== LightHouse V2 Manager ===")
print(" ")

cmdName = os.path.basename(sys.argv[0])
cmdPath = os.path.abspath(sys.argv[0]).replace(cmdName, "")
cmdStr = (cmdPath+cmdName).replace(os.getcwd(), ".")
if cmdStr.find(".py")>0:
cmdStr = '"'+ sys.executable +'" "' + cmdStr + '"'
cmdStr = (cmdPath + cmdName).replace(os.getcwd(), ".")
if cmdStr.find(".py") > 0:
cmdStr = '"' + sys.executable + '" "' + cmdStr + '"'

if len(sys.argv)>1 and sys.argv[1] in ["on", "off", "discover"]:
command = sys.argv[1]
if len(sys.argv) > 1 and sys.argv[1] in ["on", "off", "discover", "toggle"]:
command = sys.argv[1]

if len(sys.argv)==1 or command=="":
print(" Invalid or no command given. Usage:")
print(" ")
print(" * discover lighthouses V2:")
print(" "+ cmdStr +" discover [--create-shortcuts, -cs]")
print(" ")
print(" * power one or more lighthoses V2 ON:")
print(" "+ cmdStr +" on [MAC1] [MAC2] [...MACn]")
print(" ")
print(" * power one or more lighthoses V2 OFF:")
print(" "+ cmdStr +" off [MAC1] [MAC2] [...MACn]")
print(" ")
sys.exit()
if len(sys.argv) == 1 or command == "":
print(" Invalid or no command given. Usage:")
print(" ")
print(" * discover LightHouse V2:")
print(" " + cmdStr + " discover [--create-shortcuts, -cs]")
print(" ")
print(" * power one or more LightHouse V2 ON:")
print(" " + cmdStr + " on [MAC1] [MAC2] [...MACn]")
print(" ")
print(" * power one or more LightHouse V2 OFF:")
print(" " + cmdStr + " off [MAC1] [MAC2] [...MACn]")
print(" ")
print(" * toggle one or more LightHouse V2 ON or OFF:")
print(" " + cmdStr + " toggle [MAC1] [MAC2] [...MACn]")
print(" ")
sys.exit()

from bleak import discover, BleakClient

async def run(loop, lh_macs):
if command == "discover":
lh_macs = []
createShortcuts = True if ("-cs" in sys.argv or "--create-shortcuts" in sys.argv) else False
print(">> MODE: discover suitable V2 lighthouses")
if createShortcuts: print(" and create desktop shortcuts")
print(" ")
print (">> Discovering BLE devices...")
devices = await discover()
for d in devices:
deviceOk = False
if type(d.name) != str or d.name.find("LHB-") != 0:
continue
print (">> Found potential Valve Lighthouse at '"+ d.address +"' with name '"+ d.name +"'...")
services = None
async with BleakClient(d.address) as client:
try:
services = await client.get_services()
except:
print(">> ERROR: could not get services.")
continue
for s in services:
if (s.uuid==__PWR_SERVICE):
print(" OK: Service "+ __PWR_SERVICE +" found.")
for c in s.characteristics:
if c.uuid==__PWR_CHARACTERISTIC:
print(" OK: Characteristic "+ __PWR_CHARACTERISTIC +" found.")
print(">> This seems to be a valid V2 Base Station.")
print(" ")
lh_macs.append(d.address)
deviceOk = True
if not deviceOk:
print(">> ERROR: Service or Characteristic not found.")
print(">> This is likely NOT a suitable Lighthouse V2.")
print(" ")
if len(lh_macs)>0:
print(">> OK: At least one compatible V2 lighthouse was found.")
print(" ")
if createShortcuts:
print(">> Trying to create Desktop Shortcuts...")
import winshell
from win32com.client import Dispatch
desktop = winshell.desktop()
path = os.path.join(desktop, "LHv2-ON.lnk")
shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(path)
if cmdName.find(".py")>0:
shortcut.Targetpath = sys.executable
shortcut.Arguments = '"' + cmdName + '" on '+ " ".join(lh_macs)
else:
shortcut.Targetpath = '"' + cmdPath + cmdName + '"'
shortcut.Arguments = "on "+ " ".join(lh_macs)
shortcut.WorkingDirectory = cmdPath[:-1]
shortcut.IconLocation = cmdPath + "lhv2_on.ico"
shortcut.save()
print(" * OK: LHv2-ON.lnk was created successfully.")
path = os.path.join(desktop, "LHv2-OFF.lnk")
shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(path)
if cmdName.find(".py")>0:
shortcut.Targetpath = sys.executable
shortcut.Arguments = '"' + cmdName + '" off '+ " ".join(lh_macs)
else:
shortcut.Targetpath = '"' + cmdPath + cmdName + '"'
shortcut.Arguments = "off "+ " ".join(lh_macs)
shortcut.WorkingDirectory = cmdPath[:-1]
shortcut.IconLocation = cmdPath + "lhv2_off.ico"
shortcut.save()
print(" * OK: LHv2-OFF.lnk was created successfully.")
else:
print(" OK, you need to manually create two links, for example on your desktop:")
print(" ")
print(" To turn your lighthouses ON:")
print(" * Link Target: "+ cmdStr +" on "+ " ".join(lh_macs))
print(" ")
print(" To turn your lighthouses OFF:")
print(" * Link Target: "+ cmdStr +" off "+ " ".join(lh_macs))
else:
print(">> Sorry, not suitable V2 Lighthouses found.")
print(" ")
if command == "discover":
lh_macs = []
createShortcuts = True if ("-cs" in sys.argv or "--create-shortcuts" in sys.argv) else False
print(">> MODE: discover suitable LightHouse V2")
if createShortcuts:
print(" and create desktop shortcuts")
print(" ")
print(">> Discovering BLE devices...")
devices = await BleakScanner.discover()
for d in devices:
deviceOk = False
if type(d.name) != str or d.name.find("LHB-") != 0:
continue
print(">> Found potential Valve LightHouse at '" + d.address + "' with name '" + d.name + "'...")
services = None
try:
async with BleakClient(d.address) as client:
try:
services = client.services
except Exception:
print(">> ERROR: could not get services.")
continue
except Exception:
continue
for s in services:
if s.uuid == __PWR_SERVICE:
print(" OK: Service " + __PWR_SERVICE + " found.")
for c in s.characteristics:
if c.uuid == __PWR_CHARACTERISTIC:
print(" OK: Characteristic " + __PWR_CHARACTERISTIC + " found.")
print(">> This seems to be a valid LightHouse V2.")
print(">> Trying to connect to BLE MAC '" + d.address + "'...")
try:
client = BleakClient(d.address, loop=loop)
await client.connect()
print(">> '" + d.address + "' connected...")
power_state = await client.read_gatt_char(__PWR_CHARACTERISTIC)
print(" Device power state: ", end="")
if power_state == __PWR_ON:
print("ON")
else:
print("OFF")
await client.disconnect()
print(">> disconnected. ")
except Exception as e:
print(">> ERROR: " + str(e))
print(" ")
lh_macs.append(d.address)
deviceOk = True
break
print(" ")
if not deviceOk:
print(">> ERROR: Service or Characteristic not found.")
print(">> This is likely NOT a suitable LightHouse V2.")
print(" ")
if len(lh_macs) > 0:
print(">> OK: At least one compatible LightHouse V2 was found.")
for mac in lh_macs:
print(" * " + mac)
print(" ")
if createShortcuts:
print(">> Trying to create Desktop Shortcuts...")
import winshell
from win32com.client import Dispatch

desktop = winshell.desktop()
path = os.path.join(desktop, "LHv2-ON.lnk")
shell = Dispatch("WScript.Shell")
shortcut = shell.CreateShortCut(path)
if cmdName.find(".py") > 0:
shortcut.Targetpath = sys.executable
shortcut.Arguments = '"' + cmdName + '" on ' + " ".join(lh_macs)
else:
shortcut.Targetpath = '"' + cmdPath + cmdName + '"'
shortcut.Arguments = "on " + " ".join(lh_macs)
shortcut.WorkingDirectory = cmdPath[:-1]
shortcut.IconLocation = cmdPath + "lhv2_on.ico"
shortcut.save()
print(" * OK: LHv2-ON.lnk was created successfully.")
path = os.path.join(desktop, "LHv2-OFF.lnk")
shell = Dispatch("WScript.Shell")
shortcut = shell.CreateShortCut(path)
if cmdName.find(".py") > 0:
shortcut.Targetpath = sys.executable
shortcut.Arguments = '"' + cmdName + '" off ' + " ".join(lh_macs)
else:
shortcut.Targetpath = '"' + cmdPath + cmdName + '"'
shortcut.Arguments = "off " + " ".join(lh_macs)
shortcut.WorkingDirectory = cmdPath[:-1]
shortcut.IconLocation = cmdPath + "lhv2_off.ico"
shortcut.save()
print(" * OK: LHv2-OFF.lnk was created successfully.")
else:
print(" OK, you need to manually create two links, for example on your desktop:")
print(" ")
print(" To turn your LightHouse ON:")
print(" * Link Target: " + cmdStr + " on " + " ".join(lh_macs))
print(" ")
print(" To turn your LightHouse OFF:")
print(" * Link Target: " + cmdStr + " off " + " ".join(lh_macs))
else:
print(">> Sorry, not suitable LightHouse V2 found.")
print(" ")

if command in ["on", "off", "toggle"]:
print(">> MODE: switch LightHouse " + command.upper())
lh_macs.extend(sys.argv[2:])
for mac in list(lh_macs):
if re.match("[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}", mac):
continue
print(" * Invalid MAC address format: " + mac)
lh_macs.remove(mac)
if len(lh_macs) == 0:
print(" ")
print(">> ERROR: no (valid) LightHouse MAC addresses given.")
print(" ")
sys.exit()
for mac in lh_macs:
print(" * " + mac)
print(" ")
for mac in lh_macs:
if command.upper() == "TOGGLE":
print(">> Trying to connect to BLE MAC '" + mac + "'...")
try:
client = BleakClient(mac, loop=loop)
await client.connect()
print(">> '" + mac + "' connected...")
power_state = await client.read_gatt_char(__PWR_CHARACTERISTIC)
print(" Getting LightHouse power state...")
if power_state == __PWR_STANDBY:
print(" LightHouse is off, turning on.")
await client.write_gatt_char(__PWR_CHARACTERISTIC, __PWR_ON)
else:
print(" LightHouse is on, putting in standby.")
await client.write_gatt_char(__PWR_CHARACTERISTIC, __PWR_STANDBY)
await client.disconnect()
print(">> disconnected. ")
print(" LightHouse toggled.")
except Exception as e:
print(">> ERROR: " + str(e))
print(" ")
elif command.upper() == "ON":
print(">> Trying to connect to BLE MAC '" + mac + "'...")
try:
client = BleakClient(mac, loop=loop)
await client.connect()
print(">> '" + mac + "' connected...")
print(" Powering ON...")
for i in range(3):
await client.write_gatt_char(__PWR_CHARACTERISTIC, __PWR_ON)
time.sleep(0.5)
power_state = await client.read_gatt_char(__PWR_CHARACTERISTIC)
if power_state == __PWR_STANDBY:
print(" retrying....")
else:
break
await client.disconnect()
print(">> disconnected. ")
print(" LightHouse has been turned on.")
except Exception as e:
print(">> ERROR: " + str(e))
print(" ")
else:
print(">> Trying to connect to BLE MAC '" + mac + "'...")
try:
client = BleakClient(mac, loop=loop)
await client.connect()
print(">> '" + mac + "' connected...")
print(" Putting in STANDBY...")
for i in range(3):
await client.write_gatt_char(__PWR_CHARACTERISTIC, __PWR_STANDBY)
time.sleep(0.5)
power_state = await client.read_gatt_char(__PWR_CHARACTERISTIC)
if power_state == __PWR_ON:
print(" retrying....")
else:
break
await client.disconnect()
print(">> disconnected. ")
print(" LightHouse has been put in standby.")
except Exception as e:
print(">> ERROR: " + str(e))
print(" ")

if command in ["on", "off"]:
print(">> MODE: switch lighthouses "+ command.upper())
lh_macs.extend(sys.argv[2:])
for mac in list(lh_macs):
if re.match("[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}", mac):
continue
print(" * Invalid MAC address format: "+mac)
lh_macs.remove(mac)
if len(lh_macs) == 0:
print(" ")
print(">> ERROR: no (valid) base station MAC addresses given.")
print(" ")
sys.exit()
for mac in lh_macs:
print(" * "+mac)
print(" ")
for mac in lh_macs:
print(">> Trying to connect to BLE MAC '"+ mac +"'...")
try:
client = BleakClient(mac, loop=loop)
await client.connect()
print(">> '"+ mac +"' connected...")
await client.write_gatt_char(__PWR_CHARACTERISTIC, __PWR_ON if command=="on" else __PWR_STANDBY)
print(">> LH switched to '"+ command +"' successfully... ")
await client.disconnect()
print(">> disconnected. ")
except Exception as e:
print(">> ERROR: "+ str(e))
print(" ")

loop = asyncio.get_event_loop()
loop.run_until_complete(run(loop, lh_macs))

0 comments on commit c5ef6a2

Please sign in to comment.