1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
|
.. _tutorial:
PyISY Tutorial
==============
This is the basic user guide for the PyISY Python module.
This module was developed to communicate with the UDI ISY-994 home automation hub
via the hub's REST interface and Websocket/SOAP event streams. It provides
near real-time updates from the device and allows control of all devices that
are supported within the ISY.
The way this module works is by connecting to your device, gathering information about
the configuration and nodes connected, and then creating a local shadow structure that
mimics the ISY's internal structure; similar to how UDI's Admin Console works in Java.
Once connected, the module can then be used by other programs or Python scripts to
interact with the ISY and either a) get the status of a node, program, variable, etc.,
or b) run a command on those items.
.. note::
This documentation is specific to PyISY Version 3.x.x, which uses asynchronous
communications and the asyncio module. If you need threaded (synchronous) support
please use Version 2.x.x.
Environment Setup
-----------------
This module can be installed via pip in any environment supporting Python 3.7 or later:
.. code-block:: shell
pip3 install pyisy
Quick Start
~~~~~~~~~~~
Starting with Version 3, this module can connect directly from the the command line
to immediately print the list of nodes, and connect to the event stream and print
the events as they are sent from the ISY.
After installation, you can test the connection with the following
.. code-block:: shell
python3 -m pyisy http://your-isy-url:80 username password
A good starting point for developing your own code is to copy the `__main__.py` file
from the module's source code. This walks you through how to create the connections and
some simple commands to get you started.
You can download it from GitHub: `<https://github.com/automicus/PyISY/blob/v3.x.x/pyisy/__main__.py>`_
Basic Usage
-----------
Testing Your Connection
~~~~~~~~~~~~~~~~~~~~~~~
When connecting to the ISY, it will connect and download all available information and populate
the local structures. Sometimes you just want to make sure the connection works before setting
everything up. This can be done using the :class:`Connection<pyisy.connection.Connection>` Class.
.. code-block:: python
import asyncio
import logging
from urlparse import urlparse
from pyisy import ISY
from pyisy.connection import ISYConnectionError, ISYInvalidAuthError, get_new_client_session
_LOGGER = logging.getLogger(__name__)
"""Validate the user input allows us to connect."""
user = "username"
password = "password"
host = urlparse("http://isy994-ip-address:port/")
tls_version = "1.2" # Can be False if using HTTP
if host.scheme == "http":
https = False
port = host.port or 80
elif host.scheme == "https":
https = True
port = host.port or 443
else:
_LOGGER.error("host value in configuration is invalid.")
return False
# Use the helper function to get a new aiohttp.ClientSession.
websession = get_new_client_session(https, tls_ver)
# Connect to ISY controller.
isy_conn = Connection(
host.hostname,
port,
user,
password,
use_https=https,
tls_ver=tls_version,
webroot=host.path,
websession=websession,
)
try:
with async_timeout.timeout(30):
isy_conf_xml = await isy_conn.test_connection()
except (ISYInvalidAuthError, ISYConnectionError):
_LOGGER.error(
"Failed to connect to the ISY, please adjust settings and try again."
)
Once you have a connection class and successfully tested the configuration, you can
then use the :class:`Configuration<pyisy.configuration.Configuration>` Class to get
some additional details about the ISY, including the firmware version, name, and
installed options like Networking, Variables, or NodeServers.
.. code-block:: python
try:
isy_conf = Configuration(xml=isy_conf_xml)
except ISYResponseParseError as error:
raise CannotConnect from error
if not isy_conf or "name" not in isy_conf or not isy_conf["name"]:
raise CannotConnect
Connecting to the Controller
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Connecting to the controller is simple and will create an instance of
the :class:`ISY<pyisy.isy.ISY>` class. This instance is what we will use to interact with the
controller. By default when connecting to the ISY, it will load all available modules;
this means all of the Nodes, Scenes, Programs, and Variables. The
networking module will only be loaded if it is available.
As mentioned above, the best starting point for your own script is the
`__main__.py` file. This includes the basic connection to the ISY and also
connecting to the event stream.
Looking at the main function here, you can see the general flow:
1. Validate the settings
2. Create (or provide) an `asyncio` WebSession.
3. Create an instance of the :class:`ISY<pyisy.isy.ISY>` Class
4. Initialize the connection with :meth:`isy.initialize<pyisy.isy.ISY.initialize>`.
5. Connect to the :class:`WebSocketClient<pyisy.events.websocket.WebSocketClient>` for real-time event updates.
6. Safely shutdown the connection when done with :meth:`isy.shutdown()<pyisy.isy.shutdown>`.
.. literalinclude:: ../pyisy/__main__.py
:language: python
:pyobject: main
General Structure of the ISY Class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The :class:`ISY<pyisy.isy.ISY>` Class holds the local "shadow" copy of the
ISY's structure and status. You can access the different components just like Python a `dict`.
Each category is a `dict`-like object that holds the structure, and then each
element is populated within that structure.
- Nodes & Groups (Scenes): :class:`isy.nodes<pyisy.nodes.Nodes>`
- Programs & Program Folders: :class:`isy.programs<pyisy.programs.Programs>`
- Variables: :class:`isy.variables<pyisy.variables.Variables>`
- Network Resources: :class:`isy.networking<pyisy.networking.NetworkResources>`
- Clock Info: :class:`isy.clock<pyisy.clock.Clock>`
- Configuration Info: :class:`isy.configuration<pyisy.configuration.Configuration>`
Controlling a Node on the Insteon Network
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Let's get straight into the fun by toggling a node on the Insteon
network. To interact with both Insteon nodes and scenes, the nodes
subclass is used. The best way to connect to a node is by using its
address directly. Nodes and folders on the ISY controller can also be
called by their name.
.. code:: python
# interact with node using address
NODE = '22 5C EB 1'
node = isy.nodes[NODE]
await node.turn_off()
sleep(5)
await node.turn_on()
.. code:: python
# interact with node using name
node = isy.nodes['Living Room Lights']
await node.turn_off()
sleep(5)
await node.turn_on()
Controlling a Scene (Group) on the Insteon Network
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Just a small point of order here. The words Group and Scene are used
interchangeably on the ISY device and similarly in this library. Don't
let this confuse you.
Now, groups and nodes are controlled in nearly identical ways. They can
be referenced by either name or address.
.. code:: python
# control scene by address
SCENE = '28614'
await isy.nodes[SCENE].turn_off()
asyncio.sleep(5)
await isy.nodes[SCENE].turn_on()
.. code:: python
# control scene by name
await isy.nodes['Downstairs Dim'].turn_off()
asyncio.sleep(5)
await isy.nodes['Downstairs Dim'].turn_on()
Controlling an ISY Program
~~~~~~~~~~~~~~~~~~~~~~~~~~
Programs work the same way. I feel like you are probably getting the
hang of this now, so I'll only show an example using an address. One
major difference between programs and nodes and groups is that with
programs, you can also interact directly with folders.
.. code:: python
# controlling a program
PROG = '005E'
await isy.programs[PROG].run()
asyncio.sleep(3)
await isy.programs[PROG].run_else()
asyncio.sleep(3)
await isy.programs[PROG].run_then()
In order to interact with a folder as if it were a program, there is one
extra step involved.
.. code:: python
PROG_FOLDER = '0061'
# the leaf property must be used to get an object that acts like program
await isy.programs[PROG_FOLDER].leaf.run()
Controlling ISY Variables
~~~~~~~~~~~~~~~~~~~~~~~~~
Variables can be a little tricky. There are integer variables and state
variables. Integer variables are called with a 1 and state variables are
called with a 2. Below is an example of both.
.. code:: python
# controlling an integer variable
var = isy.variables[1][3]
await var.set_value(0)
print(var.status)
await var.set_value(6)
print(var.status)
.. parsed-literal::
0
6
.. code:: python
# controlling a state variable (Type 2) init value
var = isy.variables[2][14]
await var.set_init(0)
print(var.init)
await var.set_init(6)
print(var.init)
.. parsed-literal::
0
6
Controlling the Networking Module
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is in the works and coming soon.
Event Updates
-------------
This library can subscribe to the ISY's Event Stream to receive updates
on devices as they are manipulated. This means that your program can
respond to events on your controller in real time using websockets and
a subscription-based event notification.
Subscribing to Updates
~~~~~~~~~~~~~~~~~~~~~~
.. warning::
THIS DOCUMENTATION IS STILL A WORK-IN-PROGRESS. The details have not yet been updated
for Version 2 or Version 3 of the PyISY Module. If you would like to help, please contribute
on GitHub.
The ISY class will not be receiving updates by default. It is, however,
easy to enable, and it is done like so.
.. code:: python
isy.auto_update = True
By default, PyISY will detect when the controller is no longer
responding and attempt a reconnect. Keep in mind though, it can take up
to two minutes to detect a lost connection. This means if you restart
your controller, in about two minutes PyISY will detect that, reconnect,
and update all the elements to their updated state. To turn off auto
reconnects, the following parameter can be changed.
.. code:: python
isy.auto_reconnect = False
Now, once the connection is lost, it will stay disconnected until it is
told to reconnect.
Binding Events to Updates
~~~~~~~~~~~~~~~~~~~~~~~~~
Using the VarEvents library, we can bind functions to be called when
certain events take place. Subscribing to an event will return a handler
that we can use to unsubscribe later. For a full list of events, check
out the VarEvents documentation.
.. code:: python
def notify(e):
print('Notification Received')
# interact with node using address
NODE = '22 5C EB 1'
node = isy.nodes[NODE]
handler = node.status.subscribe('changed', notify)
Now, when we make a change to the node, we will receive the
notification...
.. code:: python
node.status.update(100)
.. parsed-literal::
Notification Received
Now we can unsubscribe from the event using the handler.
.. code:: python
handler.unsubscribe()
node.status.update(75)
More details about event handling are discussed inside the rest of the
documentation, but that is the basics.
|