Skip to content

Commit 804d0f9

Browse files
committed
Initial commit
0 parents  commit 804d0f9

File tree

118 files changed

+5729
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+5729
-0
lines changed

.gitignore

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# general things to ignore
2+
build/
3+
dist/
4+
*.egg-info/
5+
*.egg
6+
*.py[cod]
7+
__pycache__/
8+
*.so
9+
*~

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 James Smith
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# aiovantage
2+
3+
aiovantage is a Python library for interacting with and controlling Vantage InFusion home automation controllers.
4+
5+
Uses a "controller" pattern inspired heavily by the [aiohue](https://github.com/home-assistant-libs/aiohue) library.
6+
7+
This open-source, non-commercial library is not affiliated, associated, authorized, endorsed by, or in any way officially connected with Vantage, and is provided for interoperability purposes only.
8+
9+
## Example
10+
11+
```python
12+
from aiovantage import Vantage
13+
14+
async with Vantage("192.168.1.2", "username", "password") as vantage:
15+
async for load in vantage.loads:
16+
print(f"{load.name} is at {load.level}%")
17+
```
18+
19+
See the [examples](https://github.com/loopj/aiovantage/tree/main/examples) folder for more examples.
20+
21+
## Features
22+
- Uses Python asyncio for non-blocking I/O.
23+
- Exposes "controllers" to make fetching and controlling various objects easy.
24+
- Uses SSL connections by default, with automatic reconnection.
25+
- Fetch objects lazily (with `async for obj in controller`).
26+
- Alternatively, eager-fetch objects with `controller.initialize`.
27+
28+
## Supported objects/controllers
29+
- Areas (rooms, etc) - `vantage.areas`
30+
- Blinds (blinds and shades) - `vantage.blinds`
31+
- BlindGroups (groups of blinds/shades) - `vantage.blind_groups`
32+
- Buttons - `vantage.buttons`
33+
- DryContacts (motion sensors, etc) - `vantage.dry_contacts`
34+
- GMem (variables) - `vantage.gmem`
35+
- Loads (lights, relays, etc) - `vantage.loads`
36+
- LoadGroups (groups of Loads) - `vantage.load_groups`
37+
- OmniSensors (power, current, etc) - `vantage.omni_sensors`
38+
- RGBLoads (RGB lights) - `vantage.rgb_loads`
39+
- Stations (keypads, etc) - `vantage.stations`
40+
- Tasks - `vantage.tasks`
41+
42+
## Installation
43+
44+
```
45+
pip install git+https://github.com/loopj/aiovantage.git
46+
```

examples/command_client/event_log.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import argparse
2+
import asyncio
3+
import logging
4+
5+
from aiovantage.command_client import CommandClient, Event, EventType
6+
7+
# Grab connection info from command line arguments
8+
parser = argparse.ArgumentParser(description="aiovantage example")
9+
parser.add_argument("host", help="hostname of Vantage controller")
10+
parser.add_argument("--username", help="username for Vantage controller")
11+
parser.add_argument("--password", help="password for Vantage controller")
12+
parser.add_argument("--debug", help="enable debug logging", action="store_true")
13+
args = parser.parse_args()
14+
15+
16+
# Define callback function for Host Command events
17+
def command_client_callback(event: Event) -> None:
18+
if event["tag"] == EventType.EVENT_LOG:
19+
print(event["log"])
20+
elif event["tag"] == EventType.CONNECTED:
21+
print("Connected and monitoring for log events...")
22+
23+
24+
async def main() -> None:
25+
if args.debug:
26+
logging.basicConfig(level=logging.DEBUG)
27+
28+
# Create a Host Command client
29+
async with CommandClient(args.host, args.username, args.password) as client:
30+
# Subscribe to connection events
31+
client.subscribe(command_client_callback, EventType.CONNECTED)
32+
33+
# Subscribe to system log events
34+
await client.subscribe_event_log(command_client_callback, "SYSTEM")
35+
36+
# Keep running for a while
37+
await asyncio.sleep(3600)
38+
39+
40+
try:
41+
asyncio.run(main())
42+
except KeyboardInterrupt:
43+
pass
+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import argparse
2+
import asyncio
3+
import logging
4+
5+
from aiovantage.command_client import CommandClient, Event, EventType
6+
7+
# Grab connection info from command line arguments
8+
parser = argparse.ArgumentParser(description="aiovantage example")
9+
parser.add_argument("host", help="hostname of Vantage controller")
10+
parser.add_argument("--username", help="username for Vantage controller")
11+
parser.add_argument("--password", help="password for Vantage controller")
12+
parser.add_argument("--debug", help="enable debug logging", action="store_true")
13+
args = parser.parse_args()
14+
15+
16+
# Define callback function for command client events
17+
def command_client_callback(event: Event) -> None:
18+
if event["tag"] == EventType.STATUS:
19+
print(f"[{event['status_type']}] id: {event['id']}, args: {event['args']}")
20+
elif event["tag"] == EventType.CONNECTED:
21+
print("Connected and monitoring for status updates...")
22+
elif event["tag"] == EventType.DISCONNECTED:
23+
print("Disconnected")
24+
elif event["tag"] == EventType.RECONNECTED:
25+
print("Reconnected")
26+
27+
28+
async def main() -> None:
29+
if args.debug:
30+
logging.basicConfig(level=logging.DEBUG)
31+
32+
# Create a Host Command client
33+
async with CommandClient(args.host, args.username, args.password) as client:
34+
# Subscribe to connection events
35+
client.subscribe(
36+
command_client_callback,
37+
(EventType.CONNECTED, EventType.DISCONNECTED, EventType.RECONNECTED),
38+
)
39+
40+
# Subscribe to status updates for LOAD objects (STATUS LOAD)
41+
await client.subscribe_status(command_client_callback, "LOAD")
42+
43+
# Keep running for a while
44+
await asyncio.sleep(3600)
45+
46+
47+
try:
48+
asyncio.run(main())
49+
except KeyboardInterrupt:
50+
pass
+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import argparse
2+
import asyncio
3+
import logging
4+
5+
from aiovantage.config_client import ConfigClient
6+
from aiovantage.config_client.helpers import get_objects_by_type
7+
from aiovantage.config_client.objects import Area, Load, StationObject
8+
9+
10+
# Grab connection info from command line arguments
11+
parser = argparse.ArgumentParser(description="aiovantage example")
12+
parser.add_argument("host", help="hostname of Vantage controller")
13+
parser.add_argument("--username", help="username for Vantage controller")
14+
parser.add_argument("--password", help="password for Vantage controller")
15+
parser.add_argument("--debug", help="enable debug logging", action="store_true")
16+
args = parser.parse_args()
17+
18+
19+
async def main() -> None:
20+
if args.debug:
21+
logging.basicConfig(level=logging.DEBUG)
22+
23+
async with ConfigClient(
24+
args.host, args.username, args.password
25+
) as client:
26+
# Dump all Areas using the get_objects_by_type helper
27+
print("# Vantage Areas")
28+
async for area in get_objects_by_type(client, ["Area"], Area):
29+
print(area)
30+
print()
31+
32+
# Dump all Loads using the get_objects_by_type helper
33+
print("# Vantage Loads")
34+
async for load in get_objects_by_type(client, ["Load"], Load):
35+
print(load)
36+
print()
37+
38+
# Dump some StationObjects using the get_objects_by_type helper
39+
print("# Vantage Stations")
40+
async for station in get_objects_by_type(
41+
client, ["Keypad", "EqCtrl"], StationObject
42+
):
43+
print(station)
44+
print()
45+
46+
47+
try:
48+
asyncio.run(main())
49+
except KeyboardInterrupt:
50+
pass

examples/config_client/get_version.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import argparse
2+
import asyncio
3+
import logging
4+
5+
from aiovantage.config_client import ConfigClient
6+
from aiovantage.config_client.methods.introspection import GetVersion
7+
8+
9+
# Grab connection info from command line arguments
10+
parser = argparse.ArgumentParser(description="aiovantage example")
11+
parser.add_argument("host", help="hostname of Vantage controller")
12+
parser.add_argument("--username", help="username for Vantage controller")
13+
parser.add_argument("--password", help="password for Vantage controller")
14+
parser.add_argument("--debug", help="enable debug logging", action="store_true")
15+
args = parser.parse_args()
16+
17+
18+
async def main() -> None:
19+
if args.debug:
20+
logging.basicConfig(level=logging.DEBUG)
21+
22+
async with ConfigClient(args.host, args.username, args.password) as client:
23+
# Simple RPC request without any params (IIntrospection.GetVersion)
24+
version = await client.request(GetVersion)
25+
print(version)
26+
27+
28+
try:
29+
asyncio.run(main())
30+
except KeyboardInterrupt:
31+
pass

examples/control_load.py

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import argparse
2+
import asyncio
3+
import logging
4+
import sys
5+
import termios
6+
import tty
7+
from contextlib import contextmanager
8+
from typing import Iterator, Optional
9+
10+
from aiovantage import Vantage
11+
12+
13+
# Grab connection info from command line arguments
14+
parser = argparse.ArgumentParser(description="aiovantage example")
15+
parser.add_argument("host", help="hostname of Vantage controller")
16+
parser.add_argument("--username", help="username for Vantage controller")
17+
parser.add_argument("--password", help="password for Vantage controller")
18+
parser.add_argument("--debug", help="enable debug logging", action="store_true")
19+
args = parser.parse_args()
20+
21+
22+
def parse_keypress() -> Optional[str]:
23+
# Rudimentary keypress parser
24+
25+
c = sys.stdin.read(1)
26+
if c == "\x1b":
27+
seq = sys.stdin.read(2)
28+
if seq == "[A":
29+
return "KEY_UP"
30+
elif seq == "[B":
31+
return "KEY_DOWN"
32+
else:
33+
return None
34+
else:
35+
return c
36+
37+
38+
@contextmanager
39+
def cbreak_mode(fd: int) -> Iterator[None]:
40+
# Context manager to read terminal input character by character
41+
42+
old_attrs = termios.tcgetattr(fd)
43+
try:
44+
tty.setcbreak(fd)
45+
yield
46+
finally:
47+
termios.tcsetattr(fd, termios.TCSADRAIN, old_attrs)
48+
49+
50+
async def main() -> None:
51+
if args.debug:
52+
logging.basicConfig(level=logging.DEBUG)
53+
54+
async with Vantage(args.host, args.username, args.password) as vantage:
55+
# Print out the available loads
56+
print("Load ID Name")
57+
print("------- ----")
58+
async for load in vantage.loads:
59+
print(f"{load.id: ^7} {load.name}")
60+
print()
61+
62+
# Ask which load to control
63+
while True:
64+
try:
65+
print("Enter a load ID to control:")
66+
print("> ", end="", flush=True)
67+
68+
load_id = int(input())
69+
load = vantage.loads[load_id]
70+
break
71+
except (ValueError, KeyError):
72+
print("Invalid load id")
73+
continue
74+
75+
# Print control instructions
76+
print(f"\nControlling load '{load.name}'")
77+
print(" Use the arrow keys to increase or decrease the load's brightness.")
78+
print(" Press the spacebar to toggle the load.")
79+
print(" Press 'q' to quit.\n")
80+
81+
# Listen for control keypresses
82+
with cbreak_mode(sys.stdin.fileno()):
83+
while True:
84+
key = parse_keypress()
85+
level = load.level or 0
86+
87+
if key == "KEY_UP":
88+
# Increase the load's brightness
89+
await vantage.loads.set_level(load.id, level + 10, transition=1)
90+
print(f"Increased '{load.name}' brightness to {load.level}%")
91+
92+
elif key == "KEY_DOWN":
93+
# Decrease the load's brightness
94+
await vantage.loads.set_level(load.id, level - 10, transition=1)
95+
print(f"Decreased '{load.name}' brightness to {load.level}%")
96+
97+
elif key == " ":
98+
# Toggle load
99+
if load.is_on:
100+
await vantage.loads.turn_off(load.id)
101+
print(f"Turned '{load.name}' load off")
102+
else:
103+
await vantage.loads.turn_on(load.id)
104+
print(f"Turned '{load.name}' load on")
105+
106+
elif key == "q":
107+
break
108+
109+
110+
try:
111+
asyncio.run(main())
112+
except KeyboardInterrupt:
113+
pass

0 commit comments

Comments
 (0)