### Install eva-lsl Source: https://github.com/eva-ics/eva4/blob/main/tools/eva-lsl/README.md Installs the EVA ICS v4 Local Service Launcher using Cargo. ```bash cargo install eva-lsl ``` -------------------------------- ### Install kafka-python3 Module Source: https://github.com/eva-ics/eva4/blob/main/contrib/bus-to-kafka/README.md Install the required kafka-python3 module using the EVA venv command. ```shell eva venv add kafka-python3 ``` -------------------------------- ### Install Tuya Module Source: https://github.com/eva-ics/eva4/blob/main/contrib/controller-tuya/README.md Installs the necessary tinytuya module for Tuya device integration. ```bash eva venv add tinytuya==1.15.1 ``` -------------------------------- ### Install build dependencies for local build Source: https://github.com/eva-ics/eva4/blob/main/README.md Installs essential development libraries and tools required for building EVA ICS v4 locally on Debian-based systems. This includes SSL, networking, and build utilities. ```bash apt-get -y install \ libssl-dev libbsd-dev pkg-config build-essential cmake git autoconf \ libtool libsnmp-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev ``` -------------------------------- ### Install cross-rs for Rust cross-compilation Source: https://github.com/eva-ics/eva4/blob/main/README.md Installs the 'cross' tool for cross-compiling Rust projects. Ensure you have Rust and Cargo installed. ```bash cargo install cross --git https://github.com/cross-rs/cross ``` -------------------------------- ### Install TRAP Controller Extra Packages Source: https://github.com/eva-ics/eva4/blob/main/UPDATE.rst If the TRAP controller service is used, install these extra packages. Ensure your package list is updated first. ```bash apt-get update && apt-get install -y --no-install-recommends libbsd-dev libsnmp-dev ``` -------------------------------- ### Configure bus-to-kafka Service Source: https://github.com/eva-ics/eva4/blob/main/contrib/bus-to-kafka/README.md Example service template for configuring the bus-to-kafka bridge, specifying bus path, command, OIDs to subscribe, and Kafka connection details. ```yaml bus: path: var/bus.ipc # service location command: venv/bin/python /opt/eva4/contrib/bus-to-kafka/bus-to-kafka.py config: # OIDs to subscribe oids: - "#" kafka: target: 127.0.0.1:9092 topics: state: state log: log user: nobody workers: 1 ``` -------------------------------- ### Prepare cross-build Docker images Source: https://github.com/eva-ics/eva4/blob/main/README.md Prepares Docker images required for cross-compiling EVA ICS v4. Assumes Docker is installed and running. ```bash make docker-cross ``` -------------------------------- ### Call Tuya Discovery Service Source: https://github.com/eva-ics/eva4/blob/main/contrib/controller-tuya/README.md Example of calling the Tuya discovery service to find devices on the local network. Adjust 'discovery_timeout' as needed. ```shell eva -T15 -J svc call eva.svc.tuya discover discovery_timeout=10 ``` -------------------------------- ### Kafka Bridge Service Configuration Source: https://context7.com/eva-ics/eva4/llms.txt Example configuration for the 'bus-to-kafka' service, specifying Kafka broker target and topic names for state and log events. This configuration is typically placed in a YAML file for service deployment. ```yaml # Service config (in EVA ICS service YAML deployment): # config: # kafka: # target: kafka-broker:9092 # topics: # state: eva-item-states # log: eva-logs # oids: # - sensor:# # - unit:# ``` -------------------------------- ### any-tts Service Configuration Source: https://github.com/eva-ics/eva4/blob/main/contrib/any-tts/README.md This YAML configuration defines the any-tts service for EVA ICS. It specifies the bus path, the command to execute for text-to-speech synthesis (using pico-tts and aplay), and the user to run the service as. Ensure 'aplay' is installed and the command path is correct. ```yaml bus: path: var/bus.ipc # service location command: venv/bin/python /opt/eva4/contrib/any-tts/any-tts.py config: # the command must accept text from stdin # e.g. for PicoTTS # also make sure "aplay" is installed say: "pico-tts -l en-US|aplay -q -f S16_LE -r 16" user: nobody workers: 1 ``` -------------------------------- ### Get Node Info Source: https://github.com/eva-ics/eva4/blob/main/contrib/webinfo/README.md Retrieves core test information for a node. ```APIDOC ## GET /api/test ### Description Retrieves core test information for a node. ### Method GET ### Endpoint /api/test ### Request Headers - **X-Auth-Key**: (string) - Required - A valid API key or token for authentication. ``` -------------------------------- ### Add Elasticsearch Module to EVA venv Source: https://github.com/eva-ics/eva4/blob/main/contrib/bus-to-elastic/README.md Installs the required 'elasticsearch' Python module into the EVA ICS virtual environment. This is a prerequisite for the bus-to-elastic service. ```shell eva venv add elasticsearch ``` -------------------------------- ### evaics.sdk.Registry Source: https://context7.com/eva-ics/eva4/llms.txt The Registry helper provides a key-value store scoped to eva/svc_data// for persisting service data. It supports setting, getting, atomically incrementing, recursively retrieving, and deleting keys. ```APIDOC ## `evaics.sdk.Registry` — Persist service data in the EVA ICS registry The `Registry` helper (available as `service.registry` after `init_rpc`) provides a key-value store scoped to `eva/svc_data//`. Keys are relative strings; values can be any MessagePack-serializable object. ### Methods - **key_set(key: str, value: Any)**: Stores a value associated with the given key. - **key_get(key: str) -> Any**: Retrieves the value associated with the given key. - **key_increment(key: str) -> int**: Atomically increments a counter associated with the key and returns the new value. - **key_get_recursive(prefix: str) -> List[Tuple[str, Any]]**: Lists all key-value pairs under the given prefix. - **key_delete_recursive(prefix: str)**: Deletes all keys and their associated values under the given prefix. ``` -------------------------------- ### Create a new service project Source: https://github.com/eva-ics/eva4/blob/main/tools/eva-lsl/README.md Initializes a new service project with the specified name using eva-lsl. ```bash eva-lsl new myservice ``` -------------------------------- ### Create Service Instance Source: https://github.com/eva-ics/eva4/blob/main/contrib/bus-to-raw-udp/README.md Command to create a new service instance using the provided template. Replace '/path/to/template.yml' with the actual path to your configuration file. ```shell eva svc create eva.bridge.raw_udp1 /path/to/template.yml ``` -------------------------------- ### Create and Run EVA Web Info Service Source: https://github.com/eva-ics/eva4/blob/main/contrib/webinfo/README.md Create and run the EVA Web Info service using the 'eva svc create' command with the specified template file. Remember to update the command path in the template. ```shell eva svc create eva.svc.webinfo /path/to/template.yml ``` -------------------------------- ### Compile Web Info Service (Rust/Cargo) Source: https://github.com/eva-ics/eva4/blob/main/contrib/webinfo/README.md Compile the EVA ICS v4 Web Info service using Cargo. Ensure you are in the project directory. ```bash cargo build --release ``` -------------------------------- ### Get Item/Group State Source: https://github.com/eva-ics/eva4/blob/main/contrib/webinfo/README.md Retrieves the state of a specific item or group within a node, identified by its OID. ```APIDOC ## GET /api/item.state/{OID} ### Description Retrieves the state of a specific item or group within a node, identified by its Object Identifier (OID). ### Method GET ### Endpoint /api/item.state/{OID} ### Parameters #### Path Parameters - **OID** (string) - Required - The Object Identifier of the item or group (e.g., `sensor/tests/t1`). ### Request Headers - **X-Auth-Key**: (string) - Required - A valid API key or token for authentication. ``` -------------------------------- ### Test any-tts Service with eva-shell Source: https://github.com/eva-ics/eva4/blob/main/contrib/any-tts/README.md This command demonstrates how to test the any-tts service by calling its 'say' RPC method with a sample text string using the eva-shell utility. This verifies that the service is running and can synthesize speech. ```shell eva svc call eva.svc.anytts say text="eva i c s is the best automation platform in the world" ``` -------------------------------- ### Create a User Account Source: https://context7.com/eva-ics/eva4/llms.txt Creates a new user account with a login, password, and assigned ACLs. Targets the 'eva.aaa.localauth' service. ```python client.bus_call('user.deploy', { 'users': [{'login': 'alice', 'password': 'P@ssw0rd', 'acls': ['operator']}] }, target='eva.aaa.localauth') ``` -------------------------------- ### Run a service locally Source: https://github.com/eva-ics/eva4/blob/main/tools/eva-lsl/README.md Builds and runs a service locally, connecting it to the specified EVA ICS node bus. Use --watch to automatically rebuild on code changes. Ensure remote connections are allowed on the node if it's not on the same machine. ```bash eva-lsl run -b IP:PORT SVC_ID ``` -------------------------------- ### List API Keys Source: https://context7.com/eva-ics/eva4/llms.txt Lists all deployed API keys. Requires the 'eva.aaa.localauth' target. ```python keys = client.bus_call('key.list', target='eva.aaa.localauth') ``` -------------------------------- ### Deploy a New API Key Source: https://context7.com/eva-ics/eva4/llms.txt Deploys a new API key with a specified ID, key string, and associated ACLs. Uses the 'eva.aaa.localauth' target. ```python client.bus_call('key.deploy', { 'keys': [{'id': 'hmi-ro', 'key': 'abc123xyz', 'acls': ['default']}] }, target='eva.aaa.localauth') ``` -------------------------------- ### Create Dummy Tuya Discovery Service Source: https://github.com/eva-ics/eva4/blob/main/contrib/controller-tuya/README.md Command to create a dummy service template for the Tuya discovery service. The 'command' field in the YAML needs to be updated to point to the discovery script. ```shell eva svc create eva.svc.tuya /opt/eva4/share/svc-tpl/svc-tpl-dummy.yml ``` -------------------------------- ### Service Configuration Template Source: https://github.com/eva-ics/eva4/blob/main/contrib/raw-udp-to-bus/README.md This YAML template configures the raw-udp-to-bus service. Specify the EVA ICS bus path, the command to execute the Python script, and the IP address to listen on. The 'map' section defines UDP ports, their decoding format, and the corresponding EVA ICS OIDs. ```yaml bus: path: var/bus.ipc # service location command: venv/bin/python /opt/eva4/contrib/raw-udp-to-bus/raw-udp-to-bus.py config: # ip address to listen to listen: 0.0.0.0 map: # UDP ports, one per item # # The service sets item values only, status register is always 1 "25001": # for decode options, see https://docs.python.org/3/library/struct.html, # the default encoding is " => { if (!service.isActive()) return; const payload = unpack(e.frame.getPayload()); if (e.frame.topic?.startsWith(EapiTopic.LocalStateTopic)) { const oid = new OID(e.frame.topic.slice(EapiTopic.LocalStateTopic.length), true); service.logger.info(`${oid.asString()} value=${payload.value}`); } }; const onRpcCall = async (e: RpcEvent): Promise => { service.needReady(); const method = e.method?.toString(); const payload = unpack(e.getPayload()); switch (method) { case "x": { // Handle HMI HTTP X-calls const xcall = new XCall(payload as XCallData); xcall.acl.requireAdmin(); return pack({ ok: true, data: xcall.params }); } case "action": { // Handle unit actions const action = new Action(payload); await service.controller.eventRunning(action); // ... device I/O ... await service.controller.eventCompleted(action, "done"); await service.bus.publish( `${EapiTopic.RawStateTopic}${action.oid.asPath()}`, pack({ status: 1, value: action.params?.value }) ); return; } case "hello": return payload?.name ? pack(`hello ${payload.name}`) : pack("hi there"); default: noRpcMethod(); } }; const main = async () => { const info = new ServiceInfo({ author: "Acme", description: "My TS Service", version: "1.0.0" }) .addMethod(new ServiceMethod("hello").optional("name")); service = await createService({ info, onFrame, onRpcCall }); // Subscribe to all item events await service.subscribeOIDs(["#"], EventKind.Any); service.logger.info("service ready"); await service.block(); await sleep(500); exit(); }; main(); ``` ``` -------------------------------- ### User Account Deployment Source: https://context7.com/eva-ics/eva4/llms.txt Deploys new user accounts with specified credentials and ACLs. ```APIDOC ## User Account Deployment ### Description Deploys new user accounts with specified login credentials and associated ACLs. ### Method `client.bus_call('user.deploy', {'users': [...]}, target='eva.aaa.localauth')` ### Parameters #### Request Body - **users** (list) - Required - A list of user objects to deploy. - **login** (string) - Required - The username for the new account. - **password** (string) - Required - The password for the new account. - **acls** (list) - Required - A list of ACL identifiers to associate with the user. ### Response #### Success Response (200) - Indicates successful deployment of the provided user accounts. ### Request Example ```python # Create a user account client.bus_call('user.deploy', { 'users': [{'login': 'alice', 'password': 'P@ssw0rd', 'acls': ['operator']}] }, target='eva.aaa.localauth') ``` ``` -------------------------------- ### Service Configuration Template Source: https://github.com/eva-ics/eva4/blob/main/contrib/bus-to-raw-udp/README.md This YAML template configures the bus-to-raw-udp service. Specify the bus path, command to execute the Python script, target IP address, source port, and a map of EVA ICS items to UDP ports. The 'encode' option allows specifying the data format for each item. ```yaml bus: path: var/bus.ipc # service location command: venv/bin/python /opt/eva4/contrib/bus-to-raw-udp/bus-to-raw-udp.py config: # ip address to send packets to target: 127.0.0.1 source_port: 9999 # sampling interval. if specified, real-time events are not processed #interval: 1 map: # UDP ports, one per item # # The service sets item values only, status register is always 1 sensor:tests/t1: # for encode options, see https://docs.python.org/3/library/struct.html, # the default encoding is " { const el = document.createElement("div"); el.classList.add("vendored-app"); va_el.appendChild(el); fetch("./" + v.id + "/icon.svg") .then((r) => { if (r.status == 200) { r.blob().then((blob) => { const el_link = document.createElement("a"); el_link.setAttribute("href", "./" + v.id + "/"); const img_url = URL.createObjectURL(blob); const img_el = document.createElement("img"); img_el.setAttribute("width", "120px"); img_el.src = img_url; el_link.appendChild(img_el); const label_el = document.createElement("div"); label_el.classList.add("vendored-app-label"); label_el.innerHTML = v.name; el_link.appendChild(label_el); el.appendChild(el_link); }); } }) .catch((e) => {}); }); ``` -------------------------------- ### Create Raw UDP Service Source: https://github.com/eva-ics/eva4/blob/main/contrib/raw-udp-to-bus/README.md This shell command creates and registers the raw-udp-to-bus service within EVA ICS v4 using a provided template file. ```shell eva svc create eva.controller.raw_udp1 /path/to/template.yml ``` -------------------------------- ### Create any-tts Service Source: https://github.com/eva-ics/eva4/blob/main/contrib/any-tts/README.md Command to create and register the any-tts service within EVA ICS using a provided template YAML file. Adjust the path to the template file as necessary. ```shell eva svc create eva.svc.anytts /path/to/template.yml ``` -------------------------------- ### Local IPC BusClient: Test Connectivity Source: https://context7.com/eva-ics/eva4/llms.txt Connects directly to the local BUS/RT socket using BusClient to test core connectivity. Prints system name and active status. ```python from evaics.client import BusClient client = BusClient( path='/opt/eva4/var/bus.ipc', name='my-script', timeout=10.0 ) client.connect() # Test core connectivity info = client.test() print('System:', info.system_name, 'Active:', info.active) ``` -------------------------------- ### Create and Run a Custom EVA ICS Python Service Source: https://context7.com/eva-ics/eva4/llms.txt Use the `evaics.sdk.Service` class to build Python microservices for EVA ICS. It handles BUS/RT connection, privilege dropping, logging, and process blocking until termination. Requires initial payload from the EVA ICS core launcher. ```python #!/opt/eva4/venv/bin/python # A minimal temperature-threshold alerting service import evaics.sdk as sdk import busrt from types import SimpleNamespace from evaics.sdk import pack, unpack, OID, LOCAL_STATE_TOPIC __version__ = '1.0.0' _d = SimpleNamespace(service=None, threshold=0.0, rcpt=None, mailer_svc=None) def on_frame(frame): """Handle incoming BUS/RT bus frames (item state updates).""" if _d.service.is_active() and frame.topic and frame.topic.startswith(LOCAL_STATE_TOPIC): oid = OID(frame.topic[len(LOCAL_STATE_TOPIC):], from_path=True) state = unpack(frame.payload) temperature = float(state.get('value', 0)) if temperature > _d.threshold and _d.rcpt and _d.mailer_svc: letter = { 'rcp': _d.rcpt, 'subject': f'{oid} overheated', 'text': f'{oid} temperature={temperature}' } _d.service.rpc.call( _d.mailer_svc, busrt.rpc.Request('send', pack(letter)) ).wait_completed() def handle_rpc(event): """Handle bus RPC calls targeted at this service.""" _d.service.need_ready() if event.method == b'status': return pack({'threshold': _d.threshold}) sdk.no_rpc_method() def run(): info = sdk.ServiceInfo( author='Acme Corp', description='Temperature alerting service', version=__version__ ) info.add_method('status', description='Get current threshold config') service = sdk.Service() _d.service = service config = service.get_config() _d.threshold = config.get('threshold', 80.0) _d.rcpt = config.get('rcpt') _d.mailer_svc = config.get('mailer_svc', 'eva.svc.mailer') service.init(info, on_frame=on_frame, on_rpc_call=handle_rpc) # Subscribe to local state updates for a specific sensor group service.subscribe_oids(['sensor:plant/#'], event_kind='local') service.block() run() ``` -------------------------------- ### EVA Web Info Service Configuration Template Source: https://github.com/eva-ics/eva4/blob/main/contrib/webinfo/README.md A YAML template for configuring the EVA Web Info service. This includes bus path, service command, listening address, HMI service instance, and worker count. ```yaml bus: path: var/bus.ipc # service location command: /path/to/eva-webinfo config: # ip/port listen to listen: 0.0.0.0:9922 # the service uses a HMI service instance to authenticate API calls. An # instance must be deployed on the node hmi_svc: eva.hmi.default # optional real ip header #real_ip_header: X-Real-Ip user: nobody workers: 1 ``` -------------------------------- ### Bus to Kafka Integration Service Source: https://context7.com/eva-ics/eva4/llms.txt This service integrates EVA ICS bus events with Kafka. It requires Kafka configuration including bootstrap servers and topics for state and logs. The service subscribes to local events and log events, publishing them to specified Kafka topics. ```python import evaics.sdk as sdk from evaics.sdk import unpack, OID, LOCAL_STATE_TOPIC, LOG_EVENT_TOPIC import json from kafka3 import KafkaProducer from types import SimpleNamespace d = SimpleNamespace(producer=None, topic_state=None, topic_log=None, service=None, timeout=None) def on_frame(frame): if not frame.topic: return if d.topic_state and frame.topic.startswith(LOCAL_STATE_TOPIC): key = str(OID(frame.topic[len(LOCAL_STATE_TOPIC):], from_path=True)) value = json.dumps(unpack(frame.payload)) d.producer.send(d.topic_state, key=key.encode(), value=value.encode()).get(timeout=d.timeout) elif d.topic_log and frame.topic.startswith(LOG_EVENT_TOPIC): data = unpack(frame.payload) d.producer.send(d.topic_log, key=b'log', value=json.dumps(data).encode()).get(timeout=d.timeout) def run(): service = sdk.Service() d.service = service config = service.get_config() kafka_cfg = config['kafka'] d.producer = KafkaProducer(bootstrap_servers=kafka_cfg['target'], api_version=(0, 10)) d.topic_state = kafka_cfg.get('topics', {}).get('state') d.topic_log = kafka_cfg.get('topics', {}).get('log') d.timeout = service.timeout['default'] service.init(sdk.ServiceInfo(author='Bohemia Automation', description='EAPI to Kafka', version='0.0.1'), on_frame=on_frame) if d.topic_state: service.subscribe_oids(config.get('oids', []), event_kind='local') if d.topic_log: service.bus.subscribe(f'{LOG_EVENT_TOPIC}#').wait_completed() service.block() run() ``` -------------------------------- ### TypeScript/JavaScript SDK: Service Initialization Source: https://context7.com/eva-ics/eva4/llms.txt Initializes a TypeScript EVA ICS service using the `createService` factory function. Handles service info, frame events, and RPC calls. ```typescript import { createService, ServiceInfo, ServiceMethod, Service, OID, pack, unpack, EventKind, Action, XCall, XCallData, EapiTopic, EvaErrorCode, EvaError, noRpcMethod, RpcEvent, exit, sleep } from "@eva-ics/sdk"; let service: Service; const onFrame = async (e: RpcEvent): Promise => { if (!service.isActive()) return; const payload = unpack(e.frame.getPayload()); if (e.frame.topic?.startsWith(EapiTopic.LocalStateTopic)) { const oid = new OID(e.frame.topic.slice(EapiTopic.LocalStateTopic.length), true); service.logger.info(`${oid.asString()} value=${payload.value}`); } }; const onRpcCall = async (e: RpcEvent): Promise => { service.needReady(); const method = e.method?.toString(); const payload = unpack(e.getPayload()); switch (method) { case "x": { // Handle HMI HTTP X-calls const xcall = new XCall(payload as XCallData); xcall.acl.requireAdmin(); return pack({ ok: true, data: xcall.params }); } case "action": { // Handle unit actions const action = new Action(payload); await service.controller.eventRunning(action); // ... device I/O ... await service.controller.eventCompleted(action, "done"); await service.bus.publish( `${EapiTopic.RawStateTopic}${action.oid.asPath()}`, pack({ status: 1, value: action.params?.value }) ); return; } case "hello": return payload?.name ? pack(`hello ${payload.name}`) : pack("hi there"); default: noRpcMethod(); } }; const main = async () => { const info = new ServiceInfo({ author: "Acme", description: "My TS Service", version: "1.0.0" }) .addMethod(new ServiceMethod("hello").optional("name")); service = await createService({ info, onFrame, onRpcCall }); // Subscribe to all item events await service.subscribeOIDs(["#"], EventKind.Any); service.logger.info("service ready"); await service.block(); await sleep(500); exit(); }; main(); ``` -------------------------------- ### Cross-compile EVA ICS v4 for x86_64 Source: https://github.com/eva-ics/eva4/blob/main/README.md Builds EVA ICS v4 for the x86_64 architecture using cross-compilation. Requires Docker images prepared with 'make docker-cross'. ```bash cross build --target x86_64-unknown-linux-gnu --release ``` -------------------------------- ### Python EAPI Bus/RT Core Methods Source: https://context7.com/eva-ics/eva4/llms.txt Interact with EVA ICS core functionalities using the Python SDK. This includes querying item states, creating, deploying, and announcing item states. ```python from evaics.client import BusClient client = BusClient() client.connect() # Get a single item state state = client.bus_call('item.state', {'i': 'sensor:plant/temp1'}) # Returns: {'oid': 'sensor:plant/temp1', 'status': 1, 'value': 23.4, 't': 1715000000.123} # Get states for all sensors in a group (MQTT-style mask) states = client.bus_call('item.list', {'i': 'sensor:plant/#'}) for s in states: print(f"{s['oid']:40s} status={s['status']} value={s.get('value')}") # Create an item client.bus_call('item.create', {'i': 'sensor:plant/humidity1'}) # Deploy items with full config client.bus_call('item.deploy', {'items': [ { 'oid': 'sensor:plant/pressure1', 'enabled': True, 'meta': {'unit': 'bar'} } ]}) # Announce a new state (raw state injection) # Typically done from a service using bus.send() directly ``` -------------------------------- ### Load Vendored App Icons and Links Source: https://github.com/eva-ics/eva4/blob/main/svc/hmi/default/index.html This JavaScript code iterates through a list of vendored applications, fetches their SVG icons, and dynamically creates links to access them. It appends these elements to the 'va' element on the page. ```javascript const vendored_apps = [ {id:"opcentre",name:"Operation centre"}, {id:"sdash",name:"System dashboard"}, ]; const va_el = document.getElementById("va"); vendored_apps.forEach((v) => { const el = document.createElement("div"); el.classList.add("vendored-app"); va_el.appendChild(el); fetch("./vendored-apps/" + v.id + "/icon.svg") .then((r) => { if (r.status == 200) { r.blob().then((blob) => { const el_link = document.createElement("a"); el_link.setAttribute("href", "./vendored-apps/" + v.id + "/"); const img_url = URL.createObjectURL(blob); const img_el = document.createElement("img"); img_el.setAttribute("width", "120px"); img_el.src = img_url; el_link.appendChild(img_el); const label_el = document.createElement("div"); label_el.classList.add("vendored-app-label"); label_el.innerHTML = v.name; el_link.appendChild(label_el); el.appendChild(el_link); }); } }).catch((e)=>{}); }); ``` -------------------------------- ### Create EVA ICS Service Source: https://github.com/eva-ics/eva4/blob/main/contrib/bus-to-kafka/README.md Command to create and register the EVA ICS bus-to-kafka service using a provided template file. ```shell eva svc create eva.bridge.kafka1 /path/to/template.yml ``` -------------------------------- ### Create EVA Service Source: https://github.com/eva-ics/eva4/blob/main/contrib/bus-to-udp/README.md Command to create and register the bus-to-udp service within the EVA ICS environment using a provided template file. ```shell eva svc create eva.bridge.udp1 /path/to/template.yml ``` -------------------------------- ### Local IPC BusClient: Call eva.core Method Source: https://context7.com/eva-ics/eva4/llms.txt Uses a local BusClient to directly call an eva.core method, such as retrieving an item's state. Prints the retrieved value. ```python # Call eva.core methods directly result = client.bus_call('item.state', {'i': 'sensor:plant/temp1'}) print('Temperature:', result.get('value')) ``` -------------------------------- ### Cross-compile EVA ICS v4 for aarch64 Source: https://github.com/eva-ics/eva4/blob/main/README.md Builds EVA ICS v4 for the aarch64 architecture using cross-compilation. Requires Docker images prepared with 'make docker-cross'. ```bash cross build --target aarch64-unknown-linux-gnu --release ``` -------------------------------- ### bus-to-elastic Service Template Configuration Source: https://github.com/eva-ics/eva4/blob/main/contrib/bus-to-elastic/README.md A YAML service template for configuring the bus-to-elastic bridge. It specifies the bus connection, the Python script to run, and ElasticSearch client connection details including host, port, authentication, and the index name for logs. ```yaml bus: path: var/bus.ipc # service location command: venv/bin/python /opt/eva4/contrib/bus-to-elastic/bus-to-elastic.py config: # passed to the client as-is. see https://elasticsearch-py.readthedocs.io client: hosts: - host: localhost port: 9200 scheme: http basic_auth: ['elastic', 'changeme'] # index name to store logs in index: logs-eva user: nobody workers: 1 ``` -------------------------------- ### EAPI BUS/RT Core Methods Source: https://context7.com/eva-ics/eva4/llms.txt Standard EAPI methods callable against `eva.core` to query and manage item states. ```APIDOC ## EAPI BUS/RT Core Methods These are standard EAPI methods callable by any authenticated client or service against the `eva.core` target. ### `item.state` — Query item state Returns the current state (status + value) for a single item. - **Parameters**: `{'i': 'item_oid'}` - **Example Response**: `{'oid': 'sensor:plant/temp1', 'status': 1, 'value': 23.4, 't': 1715000000.123}` ### `item.list` — Query multiple item states Returns current item states for items matching a given MQTT-style mask. - **Parameters**: `{'i': 'item_oid_mask'}` - **Example Usage**: Iterating through returned states to print OID, status, and value. ### `item.create` — Create an item Creates a new item. - **Parameters**: `{'i': 'item_oid'}` ### `item.deploy` — Deploy items with full config Deploys items with their full configuration, including metadata. - **Parameters**: `{'items': [{'oid': 'item_oid', 'enabled': bool, 'meta': { ... }}]}` ``` -------------------------------- ### Create bus-to-elastic Service Source: https://github.com/eva-ics/eva4/blob/main/contrib/bus-to-elastic/README.md Command to create and register the bus-to-elastic service within EVA ICS v4 using a provided YAML template file. Ensure the path to the template is correct. ```shell eva svc create eva.bridge.elastic1 /path/to/template.yml ``` -------------------------------- ### evaics.sdk.Service — Create and run a custom EVA ICS service Source: https://context7.com/eva-ics/eva4/llms.txt The `Service` class is the entry point for building Python-based EVA ICS microservices. It handles connection to the BUS/RT, privilege dropping, logging, and process lifecycle management. ```APIDOC ## evaics.sdk.Service ### Description The `Service` class is the entry point for building Python-based EVA ICS microservices. It reads the initial payload from stdin (injected by the EVA ICS core launcher), establishes a BUS/RT connection, drops OS-level privileges, initializes logging, and blocks the process until a termination signal is received. ### Methods - **`Service()`**: Constructor for the Service class. - **`init(info, on_frame=None, on_rpc_call=None)`**: Initializes the service with provided information and event handlers. - `info` (ServiceInfo): Information about the service. - `on_frame` (callable, optional): Handler for incoming BUS/RT bus frames. - `on_rpc_call` (callable, optional): Handler for incoming RPC calls. - **`subscribe_oids(oids, event_kind='local')`**: Subscribes to Object Identifiers (OIDs) for state updates. - `oids` (list): A list of OID patterns to subscribe to. - `event_kind` (str, optional): The kind of events to subscribe to ('local' or 'remote'). Defaults to 'local'. - **`get_config()`**: Retrieves the service configuration. - **`block()`**: Blocks the process until a termination signal is received. ### Example Usage ```python #!/opt/eva4/venv/bin/python import evaics.sdk as sdk import busrt from types import SimpleNamespace from evaics.sdk import pack, unpack, OID, LOCAL_STATE_TOPIC __version__ = '1.0.0' _d = SimpleNamespace(service=None, threshold=0.0, rcpt=None, mailer_svc=None) def on_frame(frame): """Handle incoming BUS/RT bus frames (item state updates).""" if _d.service.is_active() and frame.topic and frame.topic.startswith(LOCAL_STATE_TOPIC): oid = OID(frame.topic[len(LOCAL_STATE_TOPIC):], from_path=True) state = unpack(frame.payload) temperature = float(state.get('value', 0)) if temperature > _d.threshold and _d.rcpt and _d.mailer_svc: letter = { 'rcp': _d.rcpt, 'subject': f'{oid} overheated', 'text': f'{oid} temperature={temperature}' } _d.service.rpc.call( _d.mailer_svc, busrt.rpc.Request('send', pack(letter)) ).wait_completed() def handle_rpc(event): """Handle bus RPC calls targeted at this service.""" _d.service.need_ready() if event.method == b'status': return pack({'threshold': _d.threshold}) sdk.no_rpc_method() def run(): info = sdk.ServiceInfo( author='Acme Corp', description='Temperature alerting service', version=__version__ ) info.add_method('status', description='Get current threshold config') service = sdk.Service() _d.service = service config = service.get_config() _d.threshold = config.get('threshold', 80.0) _d.rcpt = config.get('rcpt') _d.mailer_svc = config.get('mailer_svc', 'eva.svc.mailer') service.init(info, on_frame=on_frame, on_rpc_call=handle_rpc) # Subscribe to local state updates for a specific sensor group service.subscribe_oids(['sensor:plant/#'], event_kind='local') service.block() run() ``` -------------------------------- ### Cloud Client: Call Method on Remote Node Source: https://context7.com/eva-ics/eva4/llms.txt Connects to a remote cluster node via a cloud client to call a method. Prepares the cloud client and then performs a bus call. ```python from evaics.client import HttpClient from evaics.client.cloud import Client as CloudClient base = HttpClient(url='http://eva-node-1:7727', key='adminkey') cloud = CloudClient(base) cloud.prepare() remote_states = cloud.bus_call('item.list', params={'i': '#'}, node='factory-node-2') print(remote_states) ``` -------------------------------- ### API Key Management Source: https://context7.com/eva-ics/eva4/llms.txt Manages API keys, including listing, deploying, and associating them with ACLs. ```APIDOC ## API Key Management ### Description Manages API keys, including listing and deploying new keys with specified ACLs. ### Method `client.bus_call('key.list', target='eva.aaa.localauth')` `client.bus_call('key.deploy', {'keys': [...]}, target='eva.aaa.localauth')` ### Parameters #### `key.list` No parameters required. #### `key.deploy` Request Body - **keys** (list) - Required - A list of key objects to deploy. - **id** (string) - Required - The unique identifier for the API key. - **key** (string) - Required - The actual API key string. - **acls** (list) - Required - A list of ACL identifiers to associate with the key. ### Response #### `key.list` Success Response (200) - Returns a list of existing API keys. #### `key.deploy` Success Response (200) - Indicates successful deployment of the provided API keys. ### Request Example ```python # List API keys keys = client.bus_call('key.list', target='eva.aaa.localauth') # Deploy a new API key client.bus_call('key.deploy', { 'keys': [{'id': 'hmi-ro', 'key': 'abc123xyz', 'acls': ['default']}] }, target='eva.aaa.localauth') ``` ``` -------------------------------- ### Update EVA ICS Node for x86_64 Architecture Source: https://github.com/eva-ics/eva4/blob/main/UPDATE.rst Execute this command on each node to update EVA ICS for the x86_64 architecture. Further updates can be performed using standard methods. ```bash EVA_ARCH_SFX=x86_64 eva update ``` ```bash EVA_ARCH_SFX=x86_64 /opt/eva4/bin/eva-cloud-manager node update ``` -------------------------------- ### Perform an Action on a Unit Source: https://context7.com/eva-ics/eva4/llms.txt Executes an action on a specified unit and waits for a result. Prints the action UUID and its status. ```python result = client.call('action', { 'i': 'unit:ctrl/valve1', 'params': {'value': 1}, 'wait': 5.0 }) print('Action UUID:', result['uuid'], 'Status:', result['status']) ``` -------------------------------- ### Persist service data using evaics.sdk.Registry Source: https://context7.com/eva-ics/eva4/llms.txt Use the Registry helper to store, retrieve, increment, and delete key-value data within a service's scope. Ensure `service.init_rpc()` or `service.init()` has been called before accessing the registry. ```python import evaics.sdk as sdk def run(): service = sdk.Service() service.init(sdk.ServiceInfo(author='me', description='counter demo', version='1.0')) reg = service.registry # available after init_rpc / init # Store a value reg.key_set('config/threshold', 42.5) # Retrieve a value value = reg.key_get('config/threshold') print('Stored threshold:', value) # 42.5 # Atomically increment a counter new_count = reg.key_increment('stats/alerts_sent') print('Alerts sent so far:', new_count) # List all keys under a prefix pairs = reg.key_get_recursive('config') for key, val in pairs: print(f' {key} = {val}') # Clean up a sub-tree reg.key_delete_recursive('stats') service.block() run() ``` -------------------------------- ### Publish action lifecycle events with evaics.sdk.Controller Source: https://context7.com/eva-ics/eva4/llms.txt Implement device controller logic by publishing action lifecycle events (pending, running, completed, failed) using the Controller class. This ensures the EVA ICS core can track action states. Ensure the service is initialized with an `on_rpc_call` handler. ```python import evaics.sdk as sdk import busrt from evaics.sdk import pack, unpack, Action def handle_rpc(event): if event.method == b'action': action = sdk.Action(event) controller = sdk.Controller(_d.service.bus) try: controller.event_pending(action) controller.event_running(action) # ... perform actual device I/O here ... # Publish new unit state to the raw state topic _d.service.bus.send( f'RAW/{action.i.to_path()}', busrt.client.Frame( pack({'status': 1, 'value': action.params.get('value')}), tp=busrt.client.OP_PUBLISH ) ) controller.event_completed(action, out='valve opened') except Exception as e: controller.event_failed(action, err=str(e), exitcode=-1) else: sdk.no_rpc_method() import types _d = types.SimpleNamespace(service=None) def run(): info = sdk.ServiceInfo(author='Acme', description='Valve controller', version='1.0') _d.service = sdk.Service() _d.service.init(info, on_rpc_call=handle_rpc) _d.service.block() run() ``` -------------------------------- ### List Mapped Ports Source: https://github.com/eva-ics/eva4/blob/main/contrib/bus-to-raw-udp/README.md This command calls the 'port.list' EAPI method on the service to retrieve a list of all configured mapped ports. ```shell eva svc call eva.bridge.raw_udp1 port.list ``` -------------------------------- ### Tuya Controller Service Configuration Source: https://github.com/eva-ics/eva4/blob/main/contrib/controller-tuya/README.md YAML configuration template for the Tuya controller service. Requires device ID, IP address, and local key. Maps device DPS values to EVA ICS OIDs and defines action mappings. ```yaml bus: path: var/bus.ipc # service location command: venv/bin/python /opt/eva4/contrib/controller-tuya/controller-tuya.py config: device: # Tuya device ID id: DEVICE_ID # Tuya device IP address addr: DEVICE_IP_ADDR # Tuya device local key local_key: DEVICE_LOCAL_KEY # Tuya device API version: 3.1, 3.2, 3.3, 3.4 or 3.5 version: 3.3 # polling interval in seconds pull_interval: 2 # DPS - EVA ICS OID map map: '16': oid: 'unit:t1/switch' '101': oid: 'sensor:t1/voltage' # divide by 10 factor: 10 '103': oid: 'sensor:t1/temp' # Action map action_map: 'unit:t1/switch': dps: '16' # control or set_value kind: control # convert action value to boolean convert_bool: true react_to_fail: true user: nobody workers: 1 ```