### Install pyhaystack from source Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/README.md Install pyhaystack by cloning the develop branch and using setup.py. This method is useful for development or when using the latest unreleased features. ```bash python setup.py install ``` -------------------------------- ### Install pyhaystack using pip Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/README.md Install the pyhaystack library using pip. This is the recommended method for most users. ```bash pip install pyhaystack ``` -------------------------------- ### Import hszinc and Skyspark client Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/README.md Import the hszinc library and the SkysparkHaystackSession client for interacting with Skyspark Haystack servers. This demonstrates basic setup for using the library. ```python import hszinc hszinc.MODE_ZINC from pyhaystack.client.skyspark import SkysparkHaystackSession ``` -------------------------------- ### Install pyhaystack dependencies in a virtual environment Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/README.md Install necessary libraries like requests, hszinc, and pyhaystack within an activated virtual environment. Avoid using sudo for pip installs within a virtual environment. ```bash pip install requests pip install hszinc pip install pyhaystack ``` -------------------------------- ### Evaluate Axon Expression using GET Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/skyspark_eval.md Use this endpoint to evaluate a simple Axon expression via a GET request. The expression is provided as a query parameter. ```http /api/demo/eval?expr=readAll(site) ``` -------------------------------- ### Get Multiple Entities by ID Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Retrieve multiple entities simultaneously by providing a list of their fully qualified identifiers. Results are returned as a dictionary keyed by ID. ```python # Multiple entities at once op = session.get_entity(['S.SERVISYS.Equip1', 'S.SERVISYS.Equip2']) op.wait() entities = op.result # dict keyed by ID ``` -------------------------------- ### Describe Pandas DataFrame Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/his.md Use the `.describe()` method on a pandas DataFrame or Series to get summary statistics, such as count, mean, standard deviation, and quartiles. ```python room_temp_serie.describe() ``` ```default count 55.000000 mean 23.454680 std 0.388645 min 22.551900 25% 23.169800 50% 23.689800 75% 23.748750 max 23.806300 dtype: float64 ``` -------------------------------- ### SkySpark Axon Expression Evaluation Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Evaluates an Axon expression on a SkySpark server. The result is returned as a `hszinc.Grid`. Ensure the SkySpark plugin is installed. ```python # Evaluate any Axon expression result = session.get_eval('readAll(site)').result print(result) ``` ```python # More complex expression result = session.get_eval( 'readAll(point and siteRef==@S.myProject and his).hisRead(today)' ).result print(result) ``` -------------------------------- ### Niagara BQL Query for Alarms Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Executes a BQL query against a Niagara station to retrieve yesterday's alarm log. Results are returned as a Pandas DataFrame. Ensure the Niagara plugin is installed. ```python # Query yesterday's alarm log bql_request = ( "station:|alarm:/|bql:select timestamp, alarmData.sourceName, " "normalTime, ackTime, alarmData.timeInAlarm, msgText, user, " "alarmData.lowLimit, alarmData.highLimit, alarmData.alarmValue, " "alarmData.notes, sourceState, ackState" " where timestamp in bqltime.yesterday" ) alarms_df = session.get_bql(bql_request).result print(alarms_df.head()) print(alarms_df.columns.tolist()) ``` -------------------------------- ### session.get_entity() — Get Entities by ID Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Retrieves one or more entities by their fully qualified identifier strings or hszinc.Ref references. Supports fetching a single entity directly or multiple entities into a dictionary. ```APIDOC ## `session.get_entity()` — Get Entities by ID Retrieves one or more entities by their fully qualified identifier strings or `hszinc.Ref` references. ```python # Single entity (single=True returns the entity directly, not a dict) op = session.get_entity('S.SERVISYS', single=True) op.wait() site = op.result print(site.tags['dis']) # 'site' print(site.iana_tz) # 'America/New_York' (from TzMixin) print(site.tz) # # Multiple entities at once op = session.get_entity(['S.SERVISYS.Equip1', 'S.SERVISYS.Equip2']) op.wait() entities = op.result # dict keyed by ID ``` ``` -------------------------------- ### Set up a virtual environment for pyhaystack Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/README.md Instructions for setting up a Python virtual environment to manage pyhaystack and its dependencies. This helps avoid conflicts with system-wide Python packages. ```bash sudo pip install virtualenv mkdir your project folder cd project virtualenv venv source venv/bin/activate ``` -------------------------------- ### HTTP Client Initialization Logic Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/connect.md Illustrates the internal logic for initializing an HTTP client within the HaystackSession, including handling optional arguments. ```python if http_args is None: http_args = {} # … etc … ``` -------------------------------- ### Connecting to a Haystack Server Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Demonstrates how to establish a connection to a Haystack server using the `pyhaystack.connect()` factory function or by directly importing vendor-specific session classes. It also shows how to use a context manager for automatic session logout. ```APIDOC ## Connecting to a Haystack Server — `pyhaystack.connect()` / direct session constructors The `pyhaystack.connect()` factory (alias for `pyhaystack.client.loader.get_instance`) creates a session from keyword arguments or a configuration dict/file. Alternatively, import a vendor-specific session class directly. Supported `implementation` keys: `'n4'`, `'ax'`, `'widesky'`, `'skyspark'`. ```python import pyhaystack import json import logging logging.getLogger().setLevel(logging.INFO) # --- Factory approach (recommended for scripts) --- session = pyhaystack.connect( implementation='skyspark', uri='http://192.168.1.10:8080', username='admin', password='s3cr3t', project='myBuilding', pint=True, # enable Pint physical quantities ) # --- From a JSON config file --- with open('haystack.json') as f: session = pyhaystack.connect(**json.load(f)) # haystack.json: {"implementation":"skyspark","uri":"http://...","username":"...","password":"...","project":"...","pint":true} # --- Direct import (Niagara 4) --- from pyhaystack.client.niagara import Niagara4HaystackSession session = Niagara4HaystackSession( uri='http://jace-host:80', username='operator', password='password', pint=True, ) # --- Direct import (WideSky / OAuth2) --- from pyhaystack.client.widesky import WideskyHaystackSession session = WideskyHaystackSession( uri='https://mytenant.on.widesky.cloud/reference', username='user@example.com', password='pass', client_id='my_oauth_client', client_secret='my_oauth_secret', pint=True, ) # --- Context manager (auto-logout) --- with pyhaystack.connect(implementation='n4', uri='http://host', username='u', password='p') as session: print(session.about().result) ``` ``` -------------------------------- ### List Entity Identifiers Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/tags.md Get a list of all matching entities' identifiers from a `FindEntityOperation` result by converting the dictionary keys to a list. ```python list(znt.keys()) ``` -------------------------------- ### Initialize HTTP Client Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/connect.md Instantiate the HTTP client with optional debug and log arguments. Other arguments are passed directly to the underlying HTTP client class. ```python if bool(http_args.pop('debug',None)) and ('log' not in http_args): http_args['log'] = log.getChild('http_client') self._client = http_client(uri=uri, **http_args) ``` -------------------------------- ### session.about() Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Retrieves server metadata including product name, version, timezone, boot time, and Haystack protocol version. The result is returned as an `hszinc.Grid` object. ```APIDOC ## `session.about()` — Query Server Info Returns a `hszinc.Grid` with server metadata: product name, version, timezone, boot time, and Haystack protocol version. ```python op = session.about() op.wait() grid = op.result # grid is a hszinc.Grid print(grid) # # Row 0: productName='Niagara AX', moduleName='nhaystack', productVersion='3.8.41.2', # serverTime=datetime(2016, 4, 28, 21, 31, 33, ...), tz='Montreal', # haystackVersion='2.0', ... # # Access individual fields for row in grid: print(row['productName'], row['haystackVersion']) ``` ``` -------------------------------- ### Synchronous High-Level Browse with Site and Equipment Entities Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Use bracket notation on site and equipment entities for convenient, cached access to tags, sub-equipments, and points. This avoids redundant polling. ```python site = session.site # Read a tag print(site['area']) # BasicQuantity(0.0, 'ft²') # Find equipment by name/ID/navName vav = site['PCV~2d11~2d012_BVV~2d06'] # "Reading equipments for this site..." (first access triggers a fetch) # All equipments as a list for equip in site.equipments: print(equip) # Find a point under equipment zone_temp = vav['ZN~2dT'] # "Reading points for this equipment..." # All points for an equipment for point in vav.points: print(point) # Ad-hoc filter under a site context air_sensors = site['sensor and air'] # Retrieve today's history directly from a point entity series = zone_temp.his() # returns Pandas Series (today by default) print(series.tail()) ``` -------------------------------- ### Connect to Skyspark using `connect()` Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/connect.md Use the `pyhaystack.connect()` function with `implementation='skyspark'` for a simplified connection to Skyspark. Requires URI, username, password, and project name. ```python import pyhaystack session = pyhaystack.connect(implementation='skyspark', uri='http://ip:port', username='user', password='my_password', project='my_project' pint=True) ``` -------------------------------- ### Get Single Entity by ID Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Retrieve a single entity directly using its fully qualified identifier. `single=True` returns the entity object instead of a dictionary. ```python # Single entity (single=True returns the entity directly, not a dict) op = session.get_entity('S.SERVISYS', single=True) op.wait() site = op.result print(site.tags['dis']) # 'site' print(site.iana_tz) # 'America/New_York' (from TzMixin) print(site.tz) # ``` -------------------------------- ### Connect to Skyspark using Direct Session Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/connect.md Instantiate `SkysparkHaystackSession` directly for connecting to Skyspark. Requires URI, username, password, and project name. ```python from pyhaystack.client.skyspark import SkysparkHaystackSession session = SkysparkHaystackSession(uri='http://ip:port', username='user', password='my_password', project='my_project' pint=True) ``` -------------------------------- ### Navigate Server Hierarchy with session.nav() Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Explore the server's tree structure, such as ComponentSpace, HistorySpace, and Site, using `session.nav()`. Pass a `nav_id` to drill down into specific sub-nodes of the hierarchy. ```python # Root navigation op = session.nav() op.wait() root = op.result print(root) # Row 0: dis='ComponentSpace', navId='slot:/' # Row 1: dis='HistorySpace', navId='his:/' # Row 2: dis='Site', navId='sep:/' # Drill into history space op = session.nav(nav_id='his:/') op.wait() print(op.result) # Row 0: dis='mySite', stationName='mySite', navId='his:/mySite' ``` -------------------------------- ### Navigate Server Data Structure Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/site.md Use session.nav() to explore the server's data structure. You can navigate to specific paths, such as 'his:/' for history data. ```python op = session.nav() op.wait() nav = op.result print(nav) ``` ```python op = session.nav(nav_id='his:/') op.wait() nav = op.result print(nav) ``` -------------------------------- ### Read Historical Data with Time Range Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/his.md Read historical data for a given point within a specified time range. The `rng` parameter can accept a `slice` object representing the start and end times. ```default op = session.his_read('S.SERVISYS.Bureau-Christian.ZN~2dT', rng=slice(sometime, now)) op.wait() history = op.result ``` -------------------------------- ### session.nav() Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Explores the server's tree structure (e.g., ComponentSpace, HistorySpace, Site) natively. You can drill into sub-nodes by providing a `nav_id`. ```APIDOC ## `session.nav()` — Navigate Server Hierarchy Explores the server's tree structure (ComponentSpace, HistorySpace, Site, etc.) natively. Pass `nav_id` to drill into a sub-node. ```python # Root navigation op = session.nav() op.wait() root = op.result print(root) # Row 0: dis='ComponentSpace', navId='slot:/' # Row 1: dis='HistorySpace', navId='his:/' # Row 2: dis='Site', navId='sep:/' # Drill into history space op = session.nav(nav_id='his:/') op.wait() print(op.result) # Row 0: dis='mySite', stationName='mySite', navId='his:/mySite' ``` ``` -------------------------------- ### Eval API Endpoint Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/skyspark_eval.md The Eval API endpoint is used to evaluate Axon expressions. It can be accessed via GET or POST requests. The request should include the expression to be evaluated, and the response will be the result of the expression converted to a grid, or an error grid if an error occurs. ```APIDOC ## Eval API Eval is used to evaluate any Axon expression. Request: a grid with one column called expr and one row with Str expression to evaluate. Response: result of the expression converted to a grid using Etc.toGrid If an error occurs an error grid is returned. ### Usage > // GET request > /api/demo/eval?expr=readAll(site) > // POST request > ver:"2.0" > expr > "readAll(site)" ```default result = session.get_eval('readAll(site)').result ``` ``` -------------------------------- ### Query Server Info with session.about() Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Retrieve server metadata such as product name, version, timezone, and Haystack protocol version using the `session.about()` method. The result is a `hszinc.Grid` object that can be iterated or accessed by field names. ```python op = session.about() op.wait() grid = op.result # grid is a hszinc.Grid print(grid) # # Row 0: productName='Niagara AX', moduleName='nhaystack', productVersion='3.8.41.2', # serverTime=datetime(2016, 4, 28, 21, 31, 33, ...), tz='Montreal', # haystackVersion='2.0', ... # # Access individual fields for row in grid: print(row['productName'], row['haystackVersion']) ``` -------------------------------- ### Load Connection Details from JSON File Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/connect.md Instantiate a Haystack session by loading connection parameters from a JSON configuration file. Ensure the file contains keys like 'implementation', 'uri', 'username', 'password', 'project', and 'pint'. ```json { "implementation": "skyspark", "uri": "http://ip:port", "username": "user", "password": "password", "project": "my_project", "pint": true } ``` ```python import json import pyhaystack session = pyhaystack.connect( **json.load( open("my-haystack-server.json","r") ) ) ``` -------------------------------- ### Create and Convert PintQuantity Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/quantity.md Create a PintQuantity object using Q_() and perform unit conversions. This demonstrates the basic usage of PintQuantity for physical quantities. ```python from pyhaystack import Q_ temp = Q_(13,'degC') temp.to('degF') ``` -------------------------------- ### Finding Equipments Synchronously Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/synchronous.md Access equipment by its ID, display name, or navigation name using dictionary-like access on the site object. This populates the site.equipments list. ```python my_equip = site['myEquip'] Reading equipments for this site... ``` -------------------------------- ### Connect to Niagara AX using `connect()` Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/connect.md Use the `pyhaystack.connect()` function with `implementation='ax'` for a simplified connection to Niagara AX. Requires URI, username, and password. ```python import pyhaystack session = pyhaystack.connect(implementation='ax', uri='http://ip:port', username='user', password='myPassword', pint=True) ``` -------------------------------- ### Connect to Haystack Server with pyhaystack.connect() Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Use the `pyhaystack.connect()` factory function to establish a session with a Haystack server. It supports direct keyword arguments or loading configuration from a JSON file. Alternatively, import vendor-specific session classes for direct instantiation. The `pint` argument enables physical quantity support. ```python import pyhaystack import json import logging logging.getLogger().setLevel(logging.INFO) # --- Factory approach (recommended for scripts) --- session = pyhaystack.connect( implementation='skyspark', uri='http://192.168.1.10:8080', username='admin', password='s3cr3t', project='myBuilding', pint=True, # enable Pint physical quantities ) # --- From a JSON config file --- with open('haystack.json') as f: session = pyhaystack.connect(**json.load(f)) # haystack.json: {"implementation":"skyspark","uri":"http://...","username":"...","password":"...","project":"...","pint":true} # --- Direct import (Niagara 4) --- from pyhaystack.client.niagara import Niagara4HaystackSession session = Niagara4HaystackSession( uri='http://jace-host:80', username='operator', password='password', pint=True, ) # --- Direct import (WideSky / OAuth2) --- from pyhaystack.client.widesky import WideskyHaystackSession session = WideskyHaystackSession( uri='https://mytenant.on.widesky.cloud/reference', username='user@example.com', password='pass', client_id='my_oauth_client', client_secret='my_oauth_secret', pint=True, ) # --- Context manager (auto-logout) --- with pyhaystack.connect(implementation='n4', uri='http://host', username='u', password='p') as session: print(session.about().result) ``` -------------------------------- ### Connect to Niagara AX using Direct Session Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/connect.md Instantiate `NiagaraHaystackSession` directly for connecting to Niagara AX. Requires URI, username, and password. ```python from pyhaystack.client.niagara import NiagaraHaystackSession session = NiagaraHaystackSession(uri='http://ip:port', username='user', password='myPassword', pint=True) ``` -------------------------------- ### Asynchronous / Signal-based Usage Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Explains how to use session methods asynchronously with callbacks and signals, including checking operation state and handling exceptions. ```APIDOC ## Asynchronous / Signal-based Usage All session methods return `HaystackOperation` state machines that support a callback / signal interface for non-blocking use. ### Callback Example ```python from pyhaystack.util.asyncexc import AsynchronousException def on_find_done(operation, **kwargs): """Slot called when find_entity operation completes.""" try: if isinstance(operation.result, Exception): raise operation.result entities = operation.result for eid, entity in entities.items(): print(eid, entity.tags.get('dis')) except Exception as e: print("Error:", e) # Connect the slot before the operation starts op = session.find_entity(filter_expr='site') op.done_sig.connect(on_find_done) ``` ### Checking Operation State ```python # Check operation state without blocking print(op.is_done) # True or False print(op.is_failed) # True if exception occurred # Block with optional timeout (seconds) op.wait(timeout=30) ``` ### Safe Exception Handling ```python # Safe exception handling in async context def safe_callback(result): try: if isinstance(result, AsynchronousException): result.reraise() # process result except Exception as e: print("Caught async error:", e) ``` ``` -------------------------------- ### session.site / session.sites — Site Shortcuts Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Properties that synchronously return the first site or all sites found on the server, returned as entity objects with SiteMixin and TzMixin applied. ```APIDOC ## `session.site` / `session.sites` — Site Shortcuts Properties that synchronously return the first site or all sites found on the server, returned as entity objects with `SiteMixin` and `TzMixin` applied. ```python # First site (typical single-site server) site = session.site print(site['area']) # BasicQuantity(5000.0, 'ft²') print(site['geoCity']) # 'Montreal' # All sites as a dict all_sites = session.sites for site_id, site_entity in all_sites.items(): print(site_id, site_entity.tags['dis']) ``` ``` -------------------------------- ### Connect to VRT Widesky using `connect()` Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/connect.md Use the `pyhaystack.connect()` function with `implementation='widesky'` for a simplified connection to VRT Widesky. Requires URI, username, password, client ID, and client secret. The `impersonate` parameter can be used for acting as another user. ```python import pyhaystack session = pyhaystack.connect(implementation='widesky', uri='https://yourtenant.on.widesky.cloud/reference', username='user', password='my_password', client_id='my_id', client_secret='my_secret', pint=True, impersonate='user_id') ``` -------------------------------- ### Query Server About Information Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/site.md Use the session.about() method to retrieve basic information about the Haystack server. The result is a hszinc.Grid instance. ```python op = session.about() op.wait() ``` ```python print(op.result) ``` -------------------------------- ### Synchronous High-Level Browse — site[key], equip[key], point.his() Source: https://context7.com/christiantremblay/pyhaystack/llms.txt The bracket operator on site and equipment entities provides a convenient shortcut to retrieve tags, sub-equipments, points, or run a find_entity filter — with results cached to avoid re-polling. ```APIDOC ## Synchronous High-Level Browse — `site[key]`, `equip[key]`, `point.his()` The bracket operator on site and equipment entities provides a convenient shortcut to retrieve tags, sub-equipments, points, or run a `find_entity` filter — with results cached to avoid re-polling. ```python site = session.site # Read a tag print(site['area']) # BasicQuantity(0.0, 'ft²') # Find equipment by name/ID/navName vav = site['PCV~2d11~2d012_BVV~2d06'] # "Reading equipments for this site..." (first access triggers a fetch) # All equipments as a list for equip in site.equipments: print(equip) # Find a point under equipment zone_temp = vav['ZN~2dT'] # "Reading points for this equipment..." # All points for an equipment for point in vav.points: print(point) # Ad-hoc filter under a site context air_sensors = site['sensor and air'] # Retrieve today's history directly from a point entity series = zone_temp.his() # returns Pandas Series (today by default) print(series.tail()) ``` ``` -------------------------------- ### Connect to Niagara4 using Direct Session Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/connect.md Instantiate `Niagara4HaystackSession` directly for connecting to Niagara4. Requires URI, username, and password. ```python from pyhaystack.client.niagara import Niagara4HaystackSession session = Niagara4HaystackSession(uri='http://ip:port', username='user', password='myPassword', pint=True) ``` -------------------------------- ### Import SkysparkHaystackSession Source: https://github.com/christiantremblay/pyhaystack/blob/master/README.rst Shows how to import the SkysparkHaystackSession class from the pyhaystack client library, used for connecting to Skyspark servers. ```python from pyhaystack.client.skyspark import SkysparkHaystackSession ``` -------------------------------- ### session.watch_sub() / session.watch_poll() / session.watch_unsub() Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Creates a watch subscription to monitor live values for a set of points, polls for updates, and cleans up. ```APIDOC ## `session.watch_sub()` / `session.watch_poll()` / `session.watch_unsub()` — Point Watches Creates a watch subscription to monitor live values for a set of points, polls for updates, and cleans up. ```python # Subscribe to a watch op = session.watch_sub( points=['S.SERVISYS.Bureau-Christian.ZN~2dT', 'S.SERVISYS.Corridor.ZN~2dT'], watch_dis='My zone temps watch', lease=120, ) op.wait() watch = op.result # Poll for changed values op = session.watch_poll(watch) op.wait() print(op.result) # Poll for ALL values (refresh=True) op = session.watch_poll(watch, refresh=True) op.wait() # Remove specific points from the watch op = session.watch_unsub(watch, points=['S.SERVISYS.Corridor.ZN~2dT']) op.wait() # Close the watch entirely op = session.watch_unsub(watch) op.wait() ``` ``` -------------------------------- ### Connect to Niagara4 using `connect()` Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/connect.md Use the `pyhaystack.connect()` function with `implementation='n4'` for a simplified connection to Niagara4. Requires URI, username, and password. ```python import pyhaystack session = pyhaystack.connect(implementation='n4', uri='http://ip:port', username='user', password='myPassword', pint=True) ``` -------------------------------- ### Connect to VRT Widesky using Direct Session Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/connect.md Instantiate `WideskyHaystackSession` directly for connecting to VRT Widesky. Requires URI, username, password, client ID, and client secret. The `impersonate` parameter can be used for acting as another user. ```python from pyhaystack.client.widesky import WideskyHaystackSession session = WideskyHaystackSession( uri='https://yourtenant.on.widesky.cloud/reference', username='user', password='my_password', client_id='my_id', client_secret='my_secret' pint=True, impersonate='user_id') ``` -------------------------------- ### Access First Site Entity Source: https://context7.com/christiantremblay/pyhaystack/llms.txt The `session.site` property provides synchronous access to the first site entity found on the server. It includes `SiteMixin` and `TzMixin`. ```python # First site (typical single-site server) site = session.site print(site['area']) # BasicQuantity(5000.0, 'ft²') print(site['geoCity']) # 'Montreal' ``` -------------------------------- ### Dynamically Configure Pint Support Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/quantity.md Modify the Pint support setting for an existing session. Call session.config_pint() with True to enable or False to disable Pint functionality. ```python session.config_pint(False) # or True ``` -------------------------------- ### Import and use hszinc mode Source: https://github.com/christiantremblay/pyhaystack/blob/master/README.rst Demonstrates how to import the hszinc library and set its mode to MODE_ZINC, which is used for parsing Zinc encoded data. ```python import hszinc hszinc.MODE_ZINC ``` -------------------------------- ### Pint Quantity Unit Conversion Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Demonstrates unit conversion for temperature values using the Pint library. Ensure `pint=True` is set in the session constructor for server-side values to be returned as PintQuantity objects. ```python from pyhaystack import Q_ # Unit conversion using Pint temp_c = Q_(13, 'degC') temp_f = temp_c.to('degF') print(temp_f) # 55.4 degree_Fahrenheit ``` -------------------------------- ### Configure NiagaraHaystackSession with Pint Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/quantity.md Initialize a NiagaraHaystackSession with Pint support enabled by setting the 'pint' parameter to True. This is required to use PintQuantity objects. ```python session = NiagaraHaystackSession(uri='http://server', username='user', password='myComplicatedPassword', pint=True) ``` -------------------------------- ### Accessing Equipment Points Synchronously Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/synchronous.md Retrieve points associated with an equipment using dictionary-like access. This populates the equip.points list for rapid iteration. ```python zone_temp = my_equip['ZN~2dT'] Reading points for this equipment... ``` -------------------------------- ### Pint Quantity Support Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Explains how to enable and use Pint for unit conversions with numeric values obtained from the server, returning them as PintQuantity objects. ```APIDOC ## Pint Quantity Support — `pyhaystack.Q_` / `session.config_pint()` When `pint=True` is passed to the session constructor, numeric values from the server are returned as `PintQuantity` objects supporting unit conversion via the [Pint](https://pint.readthedocs.io/) library. ### Unit Conversion ```python from pyhaystack import Q_ # Unit conversion using Pint temp_c = Q_(13, 'degC') temp_f = temp_c.to('degF') print(temp_f) # 55.4 degree_Fahrenheit ``` ### Dynamic Pint Configuration ```python # Enable/disable Pint dynamically per session session.config_pint(True) # switch to PintQuantity session.config_pint(False) # switch back to BasicQuantity ``` ### Server Values with Pint ```python # Values read from server with pint=True come as PintQuantity op = session.find_entity(filter_expr='sensor and zone and temp') op.wait() entity = list(op.result.values())[0] cur_val = entity.tags['curVal'] # PintQuantity print(cur_val.to('degF')) # auto unit conversion ``` ### Extracting Metadata Unit ```python # Extract metadata unit from a Pandas Series op = session.his_read_series('S.SERVISYS.Corridor.ZN~2dT', rng='today') op.wait() series = op.result print(series.meta['units']) # ``` ``` -------------------------------- ### SkySpark Axon Eval Execution Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Demonstrates how to evaluate Axon expressions on a SkySpark server and retrieve the results as a hszinc.Grid. ```APIDOC ## SkySpark Axon Eval — `session.get_eval()` (SkySpark plugin) Evaluates an Axon expression on a SkySpark server and returns the result as a `hszinc.Grid`. ### Example ```python # Evaluate any Axon expression result = session.get_eval('readAll(site)').result print(result) # More complex expression result = session.get_eval( 'readAll(point and siteRef==@S.myProject and his).hisRead(today)' ).result print(result) ``` ``` -------------------------------- ### session.read() Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Low-level entity read; returns a raw hszinc.Grid without the Python entity wrapper layer. ```APIDOC ## `session.read()` — Low-Level Entity Read The low-level equivalent of `find_entity`/`get_entity`; returns a raw `hszinc.Grid` without the Python entity wrapper layer. ```python # By filter expression op = session.read(filter_expr='equip and siteRef==@S.SERVISYS', limit=50) op.wait() grid = op.result # By one or more IDs op = session.read(ids=['S.SERVISYS.Equip1', 'S.SERVISYS.Equip2']) op.wait() grid = op.result for row in grid: print(row.get('dis'), row.get('id')) ``` ``` -------------------------------- ### Accessing Site Tags Synchronously Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/synchronous.md Retrieve tags associated with a site using dictionary-like access. The result is a BasicQuantity object. ```python site ['area'] # Returns BasicQuantity(0.0, 'ft²') ``` -------------------------------- ### Access All Site Entities Source: https://context7.com/christiantremblay/pyhaystack/llms.txt The `session.sites` property synchronously returns all sites found on the server as a dictionary, keyed by site ID. ```python # All sites as a dict all_sites = session.sites for site_id, site_entity in all_sites.items(): print(site_id, site_entity.tags['dis']) ``` -------------------------------- ### Accessing All Site Entities Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/site.md Retrieve a dictionary containing all site entities associated with the session. ```default # Get a dict with all sites session.sites ``` -------------------------------- ### Niagara BQL Execution Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Shows how to execute BQL (Building Query Language) statements against a Niagara station and retrieve results as a Pandas DataFrame. ```APIDOC ## Niagara BQL — `session.get_bql()` (Niagara plugin) Executes a BQL (Building Query Language) statement against a Niagara station and returns results as a Pandas DataFrame. ### Example ```python # Query yesterday's alarm log bql_request = ( "station:|alarm:/|bql:select timestamp, alarmData.sourceName, " "normalTime, ackTime, alarmData.timeInAlarm, msgText, user, " "alarmData.lowLimit, alarmData.highLimit, alarmData.alarmValue, " "alarmData.notes, sourceState, ackState" " where timestamp in bqltime.yesterday" ) alarms_df = session.get_bql(bql_request).result print(alarms_df.head()) print(alarms_df.columns.tolist()) ``` ``` -------------------------------- ### Accessing the First Site Entity Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/site.md A shortcut property on the session object to directly access the first site entity, which is typically a SiteTzEntity. ```default # Target the first site (returns a SiteTzEntity) session.site ``` -------------------------------- ### session.invoke_action() Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Invokes a named action on a Project Haystack entity with optional keyword arguments. ```APIDOC ## `session.invoke_action()` — Invoke a Server-Side Action Invokes a named action on a Project Haystack entity with optional keyword arguments. ```python # Invoke a custom action defined on an entity op = session.invoke_action( entity='S.SERVISYS.AHU1', action='reset', ) op.wait() print(op.result) # Invoke an action with parameters op = session.invoke_action( entity='S.SERVISYS.AHU1', action='setSetpoint', temp=21.0, duration=3600, ) op.wait() ``` ``` -------------------------------- ### Manage Point Watch Subscriptions Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Subscribes to live point value updates, polls for changes, and unsubscribes. Watches can be managed by lease duration and specific points. ```python # Subscribe to a watch op = session.watch_sub( points=['S.SERVISYS.Bureau-Christian.ZN~2dT', 'S.SERVISYS.Corridor.ZN~2dT'], watch_dis='My zone temps watch', lease=120, ) op.wait() watch = op.result ``` ```python # Poll for changed values op = session.watch_poll(watch) op.wait() print(op.result) ``` ```python # Poll for ALL values (refresh=True) op = session.watch_poll(watch, refresh=True) op.wait() ``` ```python # Remove specific points from the watch op = session.watch_unsub(watch, points=['S.SERVISYS.Corridor.ZN~2dT']) op.wait() ``` ```python # Close the watch entirely op = session.watch_unsub(watch) op.wait() ``` -------------------------------- ### Finding Entities with Filters Synchronously Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/synchronous.md Use the bracket syntax with a filter string to find entities (tags, equipments, or points) within the current context (site or equipment). This internally calls find_entity. ```python air_sensors = my_equip['sensor and air'] # Returns all the points corresponding to this search. ``` -------------------------------- ### Compound AND / OR Expressions Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Demonstrates how to construct complex filter expressions using logical AND (&) and OR (|) operators with pyhaystack's Field and Scalar objects. ```APIDOC ## Compound AND / OR Expressions This section shows how to build complex filter expressions using logical AND and OR operators. ### Example ```python expr = ( fb.Field('sensor') & fb.Field('zone') & ( (fb.Field('tz') == fb.Scalar('Brisbane')) | (fb.Field('tz') == fb.Scalar('Montreal')) ) ) print(str(expr)) # Expected output: 'sensor and zone and ( tz == "Brisbane" or tz == "Montreal" )' # Use in find_entity op = session.find_entity(expr) op.wait() points = op.result ``` ``` -------------------------------- ### Retrieve and Chain History Function Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/synchronous.md Demonstrates chaining the `his()` function to a retrieved point to access its historical data. This is typically used after selecting a specific point from a site. ```python pcv6 = site['PCV~2d11~2d012_BVV~2d06'] zone_temp = pcv6['ZN~2dT'] zone_temp.his() ``` -------------------------------- ### Numeric Comparison with Pint Support Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Illustrates filtering entities based on numeric comparisons, including support for units using the Pint library when enabled. ```APIDOC ## Numeric Comparison with Pint Support This section covers filtering entities based on numeric comparisons and demonstrates how to use Pint for unit handling. ### Example ```python # Numeric comparison area_filter = fb.Field('area') > fb.Scalar(hszinc.Quantity(1000, 'ft²')) op = session.find_entity(fb.Field('site') & area_filter) op.wait() ``` ``` -------------------------------- ### Reading Server Values with Pint Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Retrieves entity data where numeric values are automatically converted to PintQuantity objects if `pint=True` was used during session initialization. Allows for easy unit conversion. ```python # Values read from server with pint=True come as PintQuantity op = session.find_entity(filter_expr='sensor and zone and temp') op.wait() entity = list(op.result.values())[0] cur_val = entity.tags['curVal'] # PintQuantity print(cur_val.to('degF')) # auto unit conversion ``` -------------------------------- ### Invoke Server-Side Actions Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Invokes named actions on Project Haystack entities, with or without parameters. Ensure the entity and action names are correct. ```python # Invoke a custom action defined on an entity op = session.invoke_action( entity='S.SERVISYS.AHU1', action='reset', ) op.wait() print(op.result) ``` ```python # Invoke an action with parameters op = session.invoke_action( entity='S.SERVISYS.AHU1', action='setSetpoint', temp=21.0, duration=3600, ) op.wait() ``` -------------------------------- ### Read Raw History Grid by Date Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Fetch historical data for a single point using a specific date. The result is a raw `hszinc.Grid`. ```python # By date op = session.his_read('S.SERVISYS.Bureau-Christian.ZN~2dT', rng=datetime.date(2017, 5, 7)) op.wait() ``` -------------------------------- ### Requesting Yesterday's Alarms with BQL Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/bql.md Constructs a BQL query to select specific alarm fields from the alarm database for yesterday. The result is fetched using session.get_bql() and returned as a pandas DataFrame. ```python bql_request = "station:|alarm:/|bql:select timestamp, alarmData.sourceName, \ normalTime,ackTime,alarmData.timeInAlarm, msgText, user, alarmData.lowLimit, \ alarmData.highLimit,alarmData.alarmValue, alarmData.notes, sourceState, ackState" yesterday_alarms = bql_request + ' where timestamp in bqltime.yesterday' alarms = session.get_bql(yesterday_alarms).result ``` -------------------------------- ### Connecting to Operation Completion Signal Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/site.md Connect a callback function to the operation's done_sig signal to execute code when the operation completes. The callback receives the operation instance as an argument. ```default def _on_op_done(operation, **kwargs): assert op.is_done # <- should not fire # Operation is done, do something with result. op = session.someoperation(arg1, arg2, arg3) op.done_sig.connect(_on_op_done) ``` -------------------------------- ### Locate Certifi CA Bundle Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/connect.md Find the path to the certifi CA certificate bundle file. This is useful for appending custom trusted certificates. ```python import certifi certifi.where() ``` -------------------------------- ### About Operation Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/site.md The 'about' operation queries basic information about the Haystack server. It returns a single row grid with details about the server's implementation, timezone, name, and version information. ```APIDOC ## About Operation ### Description Queries basic information about the server. ### Request Empty grid. ### Response Single row grid with the following columns: * haystackVersion (Str): Version of REST implementation, must be “2.0”. * tz (Str): Server’s default timezone. * serverName (Str): Name of the server or project database. * serverTime (DateTime): Current DateTime of server’s clock. * serverBootTime (DateTime): DateTime when server was booted up. * productName (Str): Name of the server software product. * productUri (Uri): URI of the product’s web site. * productVersion (Str): Version of the server software product. * moduleName (Str): Module which implements Haystack server protocol if it's a plug-in to the product. * moduleVersion (Str): Version of moduleName. ### Request Example (Python SDK) ```python op = session.about() op.wait() ``` ### Response Example (Grid Output) ``` Columns: productName moduleName productVersion serverTime tz moduleUri serverName productUri serverBootTime haystackVersion moduleVersion Row 0: productName='Niagara AX', moduleName='nhaystack', productVersion='3.8.41.2', serverTime=datetime.datetime(2016, 4, 28, 21, 31, 33, 882000, tzinfo=), tz='Montreal', moduleUri=Uri('https://bitbucket.org/jasondbriggs/nhaystack'), serverName='Servisys', productUri=Uri('http://www.tridium.com/'), serverBootTime=datetime.datetime(2016, 4, 5, 15, 9, 8, 119000, tzinfo=), haystackVersion='2.0', moduleVersion='1.2.5.18.1' ``` ``` -------------------------------- ### Read Raw History Grid by String Range Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Use `session.his_read` with string keywords like 'today' or 'yesterday' to fetch historical data for a single point as a raw `hszinc.Grid`. ```python # By string range keyword op = session.his_read('S.SERVISYS.Bureau-Christian.ZN~2dT', rng='today') op.wait() grid = op.result # hszinc.Grid with ts/val columns ``` -------------------------------- ### Filter String Builder Source: https://github.com/christiantremblay/pyhaystack/blob/master/docs/source/site.md A utility for programmatically building filter strings to avoid issues with unsanitized data and to simplify complex filter construction. ```APIDOC ## Filter String Builder ### Description Provides a utility to build filter strings programmatically using Python objects, helping to avoid unsanitized data issues. ### Module `pyhaystack.util.filterbuilder` ### Usage Example (Python SDK) ```python from pyhaystack.util import filterbuilder as fb op = session.find_entity(fb.Field('site') & \ ((fb.Field('tz') == fb.Scalar('Brisbane')) | \ (fb.Field('tz') == fb.Scalar('Montreal')))) op.wait() sites_in_brisbane_and_montreal = op.result ``` ### Description of Example This example retrieves all sites that are located in either the 'Brisbane' or 'Montreal' timezones. ``` -------------------------------- ### session.point_write() Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Writes a value to a writeable point at a given priority level (1–17), or reads the current write array when `level` is `None`. ```APIDOC ## `session.point_write()` — Write to a Controllable Point Writes a value to a writeable point at a given priority level (1–17), or reads the current write array when `level` is `None`. ```python # Read the current write array (no level given) op = session.point_write('S.SERVISYS.AHU1.SAT~2dSP') op.wait() print(op.result) # Write a setpoint override at priority level 8 op = session.point_write( point='S.SERVISYS.AHU1.SAT~2dSP', level=8, val=21.5, who='operator-script', duration=3600, # seconds ) op.wait() print(op.result) ``` ``` -------------------------------- ### Programmatic Queries with FilterBuilder Source: https://context7.com/christiantremblay/pyhaystack/llms.txt Use `filterbuilder` to construct complex queries programmatically. This is useful for dynamic query generation based on application logic. ```python from pyhaystack.util import filterbuilder as fb op = session.find_entity( fb.Field('site') & ( (fb.Field('tz') == fb.Scalar('Brisbane')) | (fb.Field('tz') == fb.Scalar('Montreal')) ) ) op.wait() sites = op.result ```