├── pyproject.toml
├── examples
├── complex-example
│ ├── csvs
│ │ ├── ModbusTags_SingleTag.csv
│ │ └── ModbusTags.csv
│ ├── objs
│ │ ├── device.json
│ │ ├── tag.json
│ │ ├── agent_item.json
│ │ ├── channel.json
│ │ └── agent.json
│ └── setup.json
├── README.md
├── kepware lls config example.py
├── ua server config example.py
├── services and logs example.py
├── user management example.py
└── iot gateway example.py
├── docs
└── index.html
├── kepconfig
├── connectivity
│ ├── udd
│ │ ├── __init__.py
│ │ └── profile.py
│ ├── egd
│ │ ├── __init__.py
│ │ ├── name.py
│ │ └── range.py
│ ├── __init__.py
│ └── channel.py
├── ua_gateway
│ ├── __init__.py
│ ├── certificates.py
│ └── common.py
├── admin
│ ├── __init__.py
│ ├── ua_server.py
│ ├── users.py
│ ├── user_groups.py
│ └── lls.py
├── datalogger
│ ├── __init__.py
│ ├── mapping.py
│ ├── log_items.py
│ ├── triggers.py
│ └── log_group.py
├── iot_gateway
│ ├── __init__.py
│ └── iot_items.py
├── __init__.py
├── helpers
│ └── deprecation_utils.py
├── utils.py
├── adv_tags
│ ├── __init__.py
│ ├── link_tags.py
│ ├── max_tags.py
│ ├── min_tags.py
│ ├── complex_tags.py
│ ├── derived_tags.py
│ ├── average_tags.py
│ └── cumulative_tags.py
├── error.py
└── structures.py
├── tests
├── conftest.py
└── udd_test.py
├── LICENSE
├── setup.py
├── .gitignore
├── release.py
└── README.md
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools", "wheel"]
3 | build-backend = "setuptools.build_meta"
--------------------------------------------------------------------------------
/examples/complex-example/csvs/ModbusTags_SingleTag.csv:
--------------------------------------------------------------------------------
1 | Device,Device_IP,TagName,Address,DataType
2 | ModbusDevice,192.168.0.167,Humidity,40003,8
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/complex-example/objs/device.json:
--------------------------------------------------------------------------------
1 | {
2 | "common.ALLTYPES_NAME": "Device1",
3 | "servermain.MULTIPLE_TYPES_DEVICE_DRIVER": "Modbus TCP/IP Ethernet",
4 | "servermain.DEVICE_MODEL": 0,
5 | "servermain.DEVICE_ID_STRING": "<132.253.75.52>.0"
6 | }
--------------------------------------------------------------------------------
/examples/complex-example/objs/tag.json:
--------------------------------------------------------------------------------
1 | {
2 | "common.ALLTYPES_NAME": "Acceleration_X",
3 | "servermain.TAG_ADDRESS": "40007",
4 | "servermain.TAG_DATA_TYPE": 8,
5 | "servermain.TAG_READ_WRITE_ACCESS": 1,
6 | "servermain.TAG_SCAN_RATE_MILLISECONDS": 100,
7 | "servermain.TAG_SCALING_TYPE": 0
8 | }
9 |
--------------------------------------------------------------------------------
/examples/complex-example/setup.json:
--------------------------------------------------------------------------------
1 | {
2 | "Kepware_IP_Array" : ["127.0.0.1"],
3 | "Kepware_Port" : "57412",
4 | "configApiUsername" : "administrator",
5 | "configApiPassword" : "",
6 | "channel_name" : "channelModbus",
7 | "path" : "csvs/ModbusTags.csv",
8 | "Broker_URL" : "tcp://127.0.0.1:1883"
9 | }
--------------------------------------------------------------------------------
/examples/complex-example/objs/agent_item.json:
--------------------------------------------------------------------------------
1 | {
2 | "common.ALLTYPES_NAME": "Channel1_Device1_Acceleration_X",
3 | "iot_gateway.IOT_ITEM_SERVER_TAG": "Channel1.Device1.Acceleration_X",
4 | "iot_gateway.IOT_ITEM_USE_SCAN_RATE": true,
5 | "iot_gateway.IOT_ITEM_SCAN_RATE_MS": 10000,
6 | "iot_gateway.IOT_ITEM_SEND_EVERY_SCAN": false,
7 | "iot_gateway.IOT_ITEM_DEADBAND_PERCENT": 0,
8 | "iot_gateway.IOT_ITEM_ENABLED": true,
9 | "iot_gateway.IOT_ITEM_DATA_TYPE": 8
10 | }
--------------------------------------------------------------------------------
/kepconfig/connectivity/udd/__init__.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`udd` module provides support for Universal Device driver specific objects
8 | within the Kepware Configuration API
9 | """
10 |
11 | from . import profile
--------------------------------------------------------------------------------
/kepconfig/ua_gateway/__init__.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) 2023 PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`ua_gateway` module provides support for Kepware's UA Gateway plug-in
8 | specific objects within the Kepware Configuration API
9 | """
10 |
11 | from . import certificates, client, server
--------------------------------------------------------------------------------
/kepconfig/admin/__init__.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`admin` module provides functionality to manage Kepware Administration based
8 | properties available through the Kepware Configuration API
9 | """
10 |
11 | from . import users, user_groups,ua_server, lls
--------------------------------------------------------------------------------
/kepconfig/datalogger/__init__.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`datalogger` module provides support for Kepware's Datalogger plug-in specific objects
8 | within the Kepware Configuration API
9 | """
10 | from . import log_group, log_items, triggers, mapping
11 |
12 |
--------------------------------------------------------------------------------
/kepconfig/connectivity/egd/__init__.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`egd` module provides support for GE EGD driver specific objects
8 | within the Kepware Configuration API
9 | """
10 |
11 | from . import exchange, range, name
12 |
13 | CONSUMER_EXCHANGE = 'CONSUMER'
14 | PRODUCER_EXCHANGE = 'PRODUCER'
15 |
--------------------------------------------------------------------------------
/kepconfig/iot_gateway/__init__.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`iot_gateway` module provides support for Kepware's IoT Gateway plug-in
8 | specific objects within the Kepware Configuration API
9 | """
10 |
11 | from . import agent, iot_items
12 |
13 | #IoT Gateway Agent Types
14 | MQTT_CLIENT_AGENT = 'MQTT Client'
15 | REST_CLIENT_AGENT = 'REST Client'
16 | REST_SERVER_AGENT = 'REST Server'
17 | THINGWORX_AGENT = 'ThingWorx'
18 |
--------------------------------------------------------------------------------
/kepconfig/connectivity/__init__.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`connectivity` module provides functionality to manage Kepware driver configuration
8 | available through the Kepware Configuration API. This includes channels, devices,
9 | tags, tag groups and driver specific objects.
10 |
11 | Driver specific object support, if available, through their own modules. Currently
12 | the GE Ethernet Global Data and Universal Device Drivers have driver specific API
13 | support in the SDK.
14 | """
15 | from . import channel, device, tag, egd, udd
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # Pytest config file
8 |
9 | import pytest, sys, os
10 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
11 | import kepconfig
12 |
13 | @pytest.fixture(scope="module")
14 | def kepware_server():
15 | return [kepconfig.connection.server(host = '127.0.0.1', port = 57412, user = 'Administrator', pw = '', https = False), 'TKS']
16 |
17 | # server = kepconfig.connection.server(host = '127.0.0.1', port = 57513, user = 'Administrator', pw = '', https = True)
18 | # server.SSL_trust_all_certs = True
19 | # return [server, 'TKE']
20 |
--------------------------------------------------------------------------------
/examples/complex-example/csvs/ModbusTags.csv:
--------------------------------------------------------------------------------
1 | Device,Device_IP,TagName,Address,DataType
2 | ModbusDevice,192.168.0.167,Temperature,40001,8
3 | ModbusDevice,192.168.0.167,Humidity,40003,8
4 | ModbusDevice,192.168.0.167,Pressure,40005,8
5 | ModbusDevice,192.168.0.167,Acceleration_X,40007,8
6 | ModbusDevice,192.168.0.167,Acceleration_Y,40009,8
7 | ModbusDevice,192.168.0.167,Acceleration_Z,40011,8
8 | ModbusDevice,192.168.0.167,Gyroscope_Roll,40013,8
9 | ModbusDevice,192.168.0.167,Gyroscope_Pitch,40015,8
10 | ModbusDevice,192.168.0.167,Gyroscope_Yaw,40017,8
11 | ModbusDevice,192.168.0.167,Orientation_Roll,40019,8
12 | ModbusDevice,192.168.0.167,Orientation_Pitch,40021,8
13 | ModbusDevice,192.168.0.167,Orientation_Yaw,40023,8
14 | ModbusDevice,192.168.0.167,Screen_Control,40025,5
15 | ModbusDevice,192.168.0.167,GoodCount,40026,5
16 | ModbusDevice,192.168.0.167,BadCount,40027,5
17 | ModbusDevice1,192.168.0.168,BadCount,40027,5
--------------------------------------------------------------------------------
/examples/complex-example/objs/channel.json:
--------------------------------------------------------------------------------
1 | {
2 | "common.ALLTYPES_NAME": "Channel1",
3 | "servermain.MULTIPLE_TYPES_DEVICE_DRIVER": "Modbus TCP/IP Ethernet",
4 | "servermain.CHANNEL_DIAGNOSTICS_CAPTURE": false,
5 | "servermain.CHANNEL_ETHERNET_COMMUNICATIONS_NETWORK_ADAPTER_STRING": "",
6 | "servermain.CHANNEL_WRITE_OPTIMIZATIONS_METHOD": 2,
7 | "servermain.CHANNEL_WRITE_OPTIMIZATIONS_DUTY_CYCLE": 10,
8 | "servermain.CHANNEL_NON_NORMALIZED_FLOATING_POINT_HANDLING": 0,
9 | "servermain.CHANNEL_COMMUNICATIONS_SERIALIZATION_VIRTUAL_NETWORK": 0,
10 | "servermain.CHANNEL_COMMUNICATIONS_SERIALIZATION_TRANSACTIONS_PER_CYCLE": 1,
11 | "servermain.CHANNEL_COMMUNICATIONS_SERIALIZATION_NETWORK_MODE": 0,
12 | "modbus_ethernet.CHANNEL_USE_ONE_OR_MORE_SOCKETS_PER_DEVICE": 1,
13 | "modbus_ethernet.CHANNEL_MAXIMUM_SOCKETS_PER_DEVICE": 1,
14 | "modbus_ethernet.CHANNEL_ETHERNET_PORT_NUMBER": 502,
15 | "modbus_ethernet.CHANNEL_ETHERNET_PROTOCOL": 0
16 | }
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # Samples for the Kepware Configuration API SDK
2 |
3 | This directory contains samples showing how to use the various features of the Kepware Config API SDK including configuring and modifying various parameters of Kepware.
4 |
5 | **Note that these samples are tested for Python 3.6+**
6 |
7 | - Simple Connectivity Example
8 | - IoT Gateway Example
9 | - Services and Logs Example
10 | - UA Server Configuration Example
11 | - User Management Configuration Example
12 | - DataLogger Plug-in Example
13 | - GE EGD Driver Exchanges Example
14 |
15 | ## Complex Example
16 |
17 | Further samples with more complex scenarios are contained in the [Complex Example](complex-example) directory, including:
18 |
19 | * Read in setup files and templates to build JSON structures
20 | * Create JSON for channel, devices tags
21 | * Create JSON for iot gateway mqtt agent for each device
22 | * Push all configuration to multiple Kepware instances
23 |
--------------------------------------------------------------------------------
/kepconfig/__init__.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 | # **Kepware Configuration (kepconfig) API Library**
7 | # --
8 |
9 | # This is a package to help create Python applications to conduct operations with the Kepware Configuration API.
10 | # This package is designed to work with all versions of Kepware that support the Configuration API including
11 | # Thingworx Kepware Server (TKS), Thingworx Kepware Edge (TKE) and KEPServerEX (KEP).
12 |
13 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
14 | # See License.txt in the project root for
15 | # license information.
16 |
17 | r"""
18 | .. include:: ../README.md
19 |
20 | """
21 | __version__ = "1.4.1"
22 | from . import connection, error
23 |
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-2024 PTC Inc. and/or all its affiliates. All rights reserved.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/kepconfig/helpers/deprecation_utils.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # Note: The code within this file was created in total or in part
8 | # with the use of AI tools.
9 |
10 | import warnings
11 | import functools
12 |
13 | def _deprecated(reason):
14 | """
15 | A decorator to mark functions as deprecated.
16 |
17 | Args:
18 | reason (str): The reason why the function is deprecated.
19 |
20 | Returns:
21 | function: The decorated function that emits a DeprecationWarning when called.
22 | """
23 | def decorator(func):
24 | @functools.wraps(func)
25 | def wrapper(*args, **kwargs):
26 | warnings.warn(
27 | f"{func.__name__} is deprecated: {reason}",
28 | category=DeprecationWarning,
29 | stacklevel=2
30 | )
31 | return func(*args, **kwargs)
32 | return wrapper
33 | return decorator
34 |
--------------------------------------------------------------------------------
/examples/complex-example/objs/agent.json:
--------------------------------------------------------------------------------
1 | {
2 | "common.ALLTYPES_NAME": "AgentMQTT",
3 | "iot_gateway.AGENTTYPES_TYPE": "MQTT Client",
4 | "iot_gateway.AGENTTYPES_ENABLED": true,
5 | "iot_gateway.MQTT_CLIENT_URL": "tcp://localhost:1883",
6 | "iot_gateway.MQTT_CLIENT_TOPIC": "iotgateway",
7 | "iot_gateway.MQTT_CLIENT_QOS": 1,
8 | "iot_gateway.AGENTTYPES_RATE_MS": 10000,
9 | "iot_gateway.AGENTTYPES_PUBLISH_FORMAT": 0,
10 | "iot_gateway.AGENTTYPES_MAX_EVENTS": 1000,
11 | "iot_gateway.AGENTTYPES_TIMEOUT_S": 5,
12 | "iot_gateway.AGENTTYPES_MESSAGE_FORMAT": 0,
13 | "iot_gateway.AGENTTYPES_STANDARD_TEMPLATE": "timestamp: |SERVERTIMESTAMP|\r\nvalues: |VALUES|\r\n",
14 | "iot_gateway.AGENTTYPES_EXPANSION_OF_VALUES": "id: |TAGNAME|\r\nv: |TAGVALUE|\r\nq: |TAGQUALITY|\r\nt: |TAGTIMESTAMP|\r\n",
15 | "iot_gateway.AGENTTYPES_ADVANCED_TEMPLATE": "{\r\n \"timestamp\": |SERVERTIMESTAMP|,\r\n \"values\": [\r\n |#each VALUES|\r\n {\"id\": \"|TAGNAME|\", \"v\": |VALUE|, \"q\": |QUALITY|, \"t\": |TIMESTAMP| } |#unless @last|,|/unless|\r\n |/each|\r\n ]\r\n}",
16 | "iot_gateway.MQTT_CLIENT_CLIENT_ID": "",
17 | "iot_gateway.MQTT_CLIENT_USERNAME": "",
18 | "iot_gateway.MQTT_CLIENT_PASSWORD": "",
19 | "iot_gateway.MQTT_TLS_VERSION": 0,
20 | "iot_gateway.MQTT_CLIENT_CERTIFICATE": false,
21 | "iot_gateway.MQTT_CLIENT_ENABLE_LAST_WILL": false,
22 | "iot_gateway.MQTT_CLIENT_LAST_WILL_TOPIC": "",
23 | "iot_gateway.MQTT_CLIENT_LAST_WILL_MESSAGE": "",
24 | "iot_gateway.MQTT_CLIENT_ENABLE_WRITE_TOPIC": false,
25 | "iot_gateway.MQTT_CLIENT_WRITE_TOPIC": "iotgateway/write"
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/kepconfig/utils.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`utils` provides general utilities to help manage
8 | various objects for Kepware's configuration
9 | """
10 |
11 | from urllib import parse
12 |
13 | def path_split(path: str):
14 | '''Used to split the standard Kepware address decimal notation into a dict that contains the
15 | *channel*, *device* and *tag_path* keys.
16 |
17 | :param path: standard Kepware address in decimal notation ("ch1.dev1.tg1.tg2.tg3")
18 | :return: dict that contains the "channel", "device" and "tag_path"
19 | :rtype: dict
20 |
21 | Ex: path = "ch1.dev1.tg1.tg2.tg3"
22 |
23 | return = {'channel': 'ch1', 'device': 'dev1', 'tag_path': ['tg1','tg2','tg3']}
24 |
25 | Ex: path = "ch1.dev1"
26 |
27 | return = {'channel': 'ch1', 'device': 'dev1'}
28 | '''
29 | path_list = path.split('.', 2)
30 | path_obj = {}
31 | for x in range(0, len(path_list)):
32 | if x == 0:
33 | path_obj['channel'] = path_list[0]
34 | elif x == 1:
35 | path_obj['device'] = path_list[1]
36 | else:
37 | path_obj['tag_path'] = path_list[2].split('.')
38 | return path_obj
39 |
40 | def _address_dedecimal(tag_address):
41 | '''Used to handle URL references where decimal notation isn't supported in object names, i.e. IoT Gateway items.
42 |
43 | Replaces `.` with `_` and removes leading `_` for system tag references'''
44 | if tag_address[0] == '_':
45 | tag_address = tag_address[1::]
46 | updated = tag_address.replace('.','_')
47 | return updated
48 |
49 | def _url_parse_object(object):
50 | '''Common url parse to handle reserved characters. Used to convert object
51 | names when building URLs.
52 |
53 | Reserved character list that Kepware allows in object names: :/?#[]@!$&'()*+,;='''
54 | return parse.quote(object, safe='')
55 |
--------------------------------------------------------------------------------
/kepconfig/adv_tags/__init__.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`adv_tags` module provides support for Kepware's Advanced Tags plug-in
8 | specific objects within the Kepware Configuration API
9 | """
10 |
11 | from ..error import KepError
12 | from . import adv_tag_group, average_tags, derived_tags, complex_tags, cumulative_tags, min_tags, max_tags, link_tags
13 | ADV_TAGS_ROOT = '/project/_advancedtags'
14 |
15 | def _adv_tag_path_split(path: str, *, isItem=False) -> dict:
16 | '''Used to split the standard Kepware address decimal notation into a dict that contains the
17 | advanced tag path components.
18 |
19 | :param path: standard Kepware address in decimal notation ("_advancedtags.tg1.tg2.tg3")
20 | :return: dict that contains the "adv_tag_root" and "tag_path"
21 | :rtype: dict
22 |
23 | Ex: path = "_advancedtags.tg1.tg2.tg3"
24 |
25 | return = {'adv_tag_root': '_advancedtags', 'tag_path': ['tg1','tg2','tg3']}
26 |
27 | Ex: path = "_advancedtags.ch1.dev1"
28 |
29 | return = {'adv_tag_root': '_advancedtags', 'tag_path': ['ch1','dev1']}
30 | '''
31 | path_list = path.split('.', 2)
32 | if path_list[0] != '_advancedtags':
33 | raise KepError('Error: Invalid advanced tag path - Must start with "_advancedtags"')
34 |
35 | path_obj = {}
36 | for x in range(0, len(path_list)):
37 | if x == 0:
38 | path_obj['adv_tag_root'] = path_list[0]
39 | elif x == 1:
40 | if isItem:
41 | path_obj['tag_path'] = path_list[1:-1]
42 | path_obj['item'] = path_list[-1]
43 | else:
44 | path_obj['tag_path'] = path_list[1:]
45 | return path_obj
46 |
47 | def _create_adv_tags_base_url(base_url, path_obj):
48 | '''Creates url object for the "path_obj" which provides the adv tags tag group structure of Kepware's project tree. Used
49 | to build a part of Kepware Configuration API URL structure
50 |
51 | Returns the advanced tag group specific url when a value is passed as the tag group name.
52 | '''
53 | url = base_url + ADV_TAGS_ROOT
54 | url += adv_tag_group._create_adv_tags_group_url(path_obj)
55 |
56 | return url
--------------------------------------------------------------------------------
/kepconfig/error.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 |
8 | r"""`error` Exception classes raised by Kepconfig.
9 | Includes KepError, KepURLError and KepHTTPError
10 | """
11 |
12 | __all__ = ['KepError', 'KepURLError', 'KepHTTPError']
13 |
14 |
15 | class KepError(Exception):
16 | '''General Exception class for Kepconfig.
17 |
18 | :param msg: General error message returned in string format.
19 | '''
20 | def __init__(self, msg):
21 | self.msg = msg
22 |
23 | def __str__(self):
24 | return 'KepError Error: %s' % (self.msg)
25 |
26 | class KepURLError(KepError):
27 | '''Exception class raised by Kepconfig that derives responses from the urllib URLError exceptions.
28 |
29 | :param url: full url path of the request that flagged a URLError exception
30 | :param msg: reason parameter in URLError exception
31 | '''
32 | def __init__(self, url=None, *args, **kwargs):
33 | super().__init__(*args, **kwargs)
34 | self.url = url
35 |
36 | @property
37 | def reason(self):
38 | return self.msg
39 |
40 | def __str__(self):
41 | return '' % self.reason
42 |
43 | class KepHTTPError(KepError):
44 | '''Exception class raised by Kepconfig that derives responses from the urllib HTTPError
45 | exceptions. This exception class is also a valid HTTP response instance. It behaves
46 | this way because HTTP protocol errors are valid responses, with a status
47 | code, headers, and a body. In some contexts, an application may want to
48 | handle an exception like a regular response.
49 |
50 | :param url: url parameter in HTTPError exception
51 | :param code: HTTP response code parameter in HTTPError exception
52 | :param hdrs: hdrs parameter in HTTPError exception
53 | :param payload: string of HTTP response payload that flagged HTTPError exception
54 | :param msg: msg parameter in HTTPError exception
55 | '''
56 | def __init__(self, url=None, code=None, hdrs=None, payload=None, *args, **kwargs):
57 | super().__init__(*args, **kwargs)
58 | self.url = url
59 | self.code = code
60 | self.hdrs = hdrs
61 | self.payload = payload
62 |
63 | @property
64 | def reason(self):
65 | return self.msg
66 |
67 | def __str__(self):
68 | return 'HTTP Error %s: %s' % (self.code, self.msg)
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # Package Setup Script - run this to build the package
8 | # Usage - python setup.py sdist bdist_wheel
9 | # Example: python setup.py 1.0.0 sdist bdist_wheel
10 |
11 | import setuptools
12 | import pathlib, os
13 |
14 | def read(rel_path):
15 | # type: (str) -> str
16 | here = os.path.abspath(os.path.dirname(__file__))
17 | # intentionally *not* adding an encoding option to open, See:
18 | # https://github.com/pypa/virtualenv/issues/201#issuecomment-3145690
19 | with open(os.path.join(here, rel_path)) as fp:
20 | return fp.read()
21 |
22 | def get_version(rel_path):
23 | # type: (str) -> str
24 | for line in read(rel_path).splitlines():
25 | if line.startswith('__version__'):
26 | # __version__ = "0.9"
27 | delim = '"' if '"' in line else "'"
28 | return line.split(delim)[1]
29 | raise RuntimeError("Unable to find version string.")
30 |
31 | here = pathlib.Path(__file__).parent.resolve()
32 |
33 | # Get the long description from the README file
34 | long_description = (here / 'README.md').read_text(encoding='utf-8')
35 |
36 | setuptools.setup(
37 | name="kepconfig",
38 | version= get_version("kepconfig/__init__.py"),
39 | author="PTC Inc",
40 | description="SDK package for Kepware Configuration API",
41 | keywords="Kepware, OPC, Configuration, Thingworx",
42 | license="MIT License",
43 | long_description=long_description,
44 | long_description_content_type="text/markdown",
45 | url="https://github.com/PTCInc/Kepware-ConfigAPI-SDK-Python",
46 | project_urls={},
47 | packages=setuptools.find_packages(),
48 | classifiers=[
49 | "Development Status :: 5 - Production/Stable",
50 | "License :: OSI Approved :: MIT License",
51 | "Programming Language :: Python",
52 | "Programming Language :: Python :: 3",
53 | "Programming Language :: Python :: 3.9",
54 | "Programming Language :: Python :: 3.10",
55 | "Programming Language :: Python :: 3.11",
56 | "Programming Language :: Python :: 3.12",
57 | "Programming Language :: Python :: 3.13",
58 | "Programming Language :: Python :: 3 :: Only",
59 | "Operating System :: OS Independent",
60 | "Intended Audience :: Manufacturing",
61 | "Intended Audience :: Developers",
62 | ],
63 | python_requires='>=3.9',
64 | )
65 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # Custom
8 | #*.gitignore
9 | *.pem
10 | _notes
11 | .vscode*
12 | .devcontainer*
13 |
14 | _new*
15 | _test*
16 | _build*
17 | _dev*
18 | # /docs
19 | mkdocs.yml
20 |
21 | # Byte-compiled / optimized / DLL files
22 | __pycache__/
23 | *.py[cod]
24 | *$py.class
25 |
26 | # C extensions
27 | *.so
28 |
29 | # Distribution / packaging
30 | .Python
31 | build/
32 | develop-eggs/
33 | dist/
34 | downloads/
35 | eggs/
36 | .eggs/
37 | lib/
38 | lib64/
39 | parts/
40 | sdist/
41 | var/
42 | wheels/
43 | pip-wheel-metadata/
44 | share/python-wheels/
45 | *.egg-info/
46 | .installed.cfg
47 | *.egg
48 | MANIFEST
49 |
50 | # PyInstaller
51 | # Usually these files are written by a python script from a template
52 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
53 | *.manifest
54 | *.spec
55 |
56 | # Installer logs
57 | pip-log.txt
58 | pip-delete-this-directory.txt
59 |
60 | # Unit test / coverage reports
61 | htmlcov/
62 | .tox/
63 | .nox/
64 | .coverage
65 | .coverage.*
66 | .cache
67 | nosetests.xml
68 | coverage.xml
69 | *.cover
70 | *.py,cover
71 | .hypothesis/
72 | .pytest_cache/
73 |
74 | # Translations
75 | *.mo
76 | *.pot
77 |
78 | # Django stuff:
79 | *.log
80 | local_settings.py
81 | db.sqlite3
82 | db.sqlite3-journal
83 |
84 | # Flask stuff:
85 | instance/
86 | .webassets-cache
87 |
88 | # Scrapy stuff:
89 | .scrapy
90 |
91 | # Sphinx documentation
92 | docs/_build/
93 |
94 | # PyBuilder
95 | target/
96 |
97 | # Jupyter Notebook
98 | .ipynb_checkpoints
99 |
100 | # IPython
101 | profile_default/
102 | ipython_config.py
103 |
104 | # pyenv
105 | .python-version
106 |
107 | # pipenv
108 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
109 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
110 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
111 | # install all needed dependencies.
112 | #Pipfile.lock
113 |
114 | # celery beat schedule file
115 | celerybeat-schedule
116 |
117 | # SageMath parsed files
118 | *.sage.py
119 |
120 | # Environments
121 | .env
122 | .venv
123 | env/
124 | venv/
125 | ENV/
126 | env.bak/
127 | venv.bak/
128 |
129 | # Spyder project settings
130 | .spyderproject
131 | .spyproject
132 |
133 | # Rope project settings
134 | .ropeproject
135 |
136 | # mkdocs documentation
137 | /site
138 |
139 | # mypy
140 | .mypy_cache/
141 | .dmypy.json
142 | dmypy.json
143 |
144 | # Pyre type checker
145 | .pyre/
146 |
147 |
--------------------------------------------------------------------------------
/kepconfig/ua_gateway/certificates.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) 2023 PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`certificates` exposes an API to allow read access to
8 | UA Gateway plug-in instance certificate objects within the Kepware Configuration API
9 | """
10 |
11 | from ..connection import server
12 | from ..error import KepHTTPError
13 | from ..ua_gateway.common import _INTER_TYPE, _create_url_cert, INSTANCE_CERTIFICATE
14 |
15 | import warnings
16 | from ..helpers.deprecation_utils import _deprecated
17 |
18 |
19 | # Enable DeprecationWarnings to be visible
20 | warnings.simplefilter('always', DeprecationWarning)
21 |
22 | @_deprecated("This function is deprecated and will be removed in a future release. Use `get_instance_certificate()` in UAG client or server module instead.")
23 | def get_instance_certificate(server: server) -> dict:
24 | '''
25 | DEPRECATED: This function is deprecated and will be removed in a future release. Use `get_instance_certificate()`
26 | in UAG client or server module instead for Kepware 6.18+.
27 |
28 | Returns the properties of the UAG instance certificate object in the UAG certificate store.
29 | These are UAG instance certificates that are used by UAG for trust purposes in the UA security model.
30 |
31 | :param server: instance of the `server` class
32 |
33 | :return: Dict of properties for the certificate requested
34 |
35 | :raises KepHTTPError: If urllib provides an HTTPError
36 | :raises KepURLError: If urllib provides an URLError
37 | '''
38 | r = server._config_get(server.url + _create_url_cert(_INTER_TYPE.CERTS, INSTANCE_CERTIFICATE))
39 | return r.payload
40 |
41 | @_deprecated("This function is deprecated and will be removed in a future release. Use `TBD` instead.")
42 | def reissue_self_signed_instance_certificate(server: server) -> bool:
43 | '''
44 | DEPRECATED: This function is deprecated and will be removed in a future release. Use `get_instance_certificate()`
45 | in UAG client or server module instead for Kepware 6.18+.
46 |
47 | Deletes and reissues a self-signed UAG instance certificate object in the UAG certificate store.
48 | This is the UAG instance certificate that are used by UAG for trust purposes in the UA security model.
49 |
50 | :param server: instance of the `server` class
51 |
52 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
53 |
54 | :raises KepHTTPError: If urllib provides an HTTPError
55 | :raises KepURLError: If urllib provides an URLError
56 | '''
57 | r = server._config_del(server.url + _create_url_cert(_INTER_TYPE.CERTS, INSTANCE_CERTIFICATE))
58 | if r.code == 200: return True
59 | else:
60 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
--------------------------------------------------------------------------------
/release.py:
--------------------------------------------------------------------------------
1 | import re
2 | import os
3 |
4 | # Constants to package __init__.py to update version of package
5 | file_location = "./kepconfig/__init__.py"
6 | version_re_string = "([0-9]+)\.([0-9]+)\.([0-9]+(?:[ab][0-9])?)"
7 | version_search = re.compile(r'__version__ = "'+ version_re_string + '"')
8 | version_check = re.compile(version_re_string)
9 |
10 | # Updates the version of package via __init__.py file
11 | def bump_version():
12 | with open(file_location) as f:
13 | s = f.read()
14 | m = version_search.search(s)
15 | v1, v2, v3 = m.groups()
16 | oldv = "{0}.{1}.{2}".format(v1, v2, v3)
17 |
18 | # Need to provide version explicitly before building and releasing
19 | ans = input("Current version of kepconfig is: {} \nUpdate new version to? (ctrl-c to exit): ".format(oldv))
20 | if ans:
21 | m = version_check.search(ans)
22 | if (m==None):
23 | print("Version must be in format major.minor.patch (1.0.0 or 1.0.0a1) format. Exiting...")
24 | exit()
25 | newv = ans
26 | else:
27 | print("Please enter updated version number. Exiting...")
28 | exit()
29 | print("\n"+ "Updating " + file_location + " version to {}.".format(newv))
30 | s = s.replace(oldv, newv)
31 | with open(file_location, "w") as f:
32 | f.write(s)
33 | return newv
34 |
35 |
36 | def release():
37 | # Update/confirm version of package
38 | v = bump_version()
39 |
40 | # Commit changes to git
41 | ans = input("Version updated, commit changes?(y/n)")
42 | if ans.lower() in ("y", "yes"):
43 | os.system("git add " + file_location)
44 | os.system('git commit -m \"{} Release\"'.format(v))
45 | os.system("git tag {0}".format(v))
46 |
47 | # This will push the committed changes and tag to Github for release
48 | ans = input("Change committed, push to Github server? (Y/n)")
49 | if ans.lower() in ("y", "yes"):
50 | os.system("git push --tags")
51 | # os.system("git push --follow-tags")
52 |
53 | # Build Distribution packages for external distribution
54 | ans = input("Build dist packages?(Y/n)")
55 | if ans.lower() in ("y", "yes"):
56 | # os.system("rm -rf dist/*") #Linux
57 | os.system("RMDIR /S /Q dist") #Windows
58 | os.system("python -m build")
59 |
60 | # Upload to pypi. Initially test with pypi test environment at test.pypi.org. Final release to pypi production
61 | # You'll be asked for credentials to authentice updating
62 | ans = input("Upload to pypi?(Y/n)")
63 | if ans.lower() in ("y", "yes"):
64 | ans = input("Push to production?(Y=production pypi/n=test pypi)")
65 | if ans.lower() in ("y", "yes"):
66 |
67 | #Production PyPi Server
68 | os.system("python -m twine upload dist/*")
69 | else:
70 |
71 | # Test PyPi Server
72 | os.system("python -m twine upload --repository testpypi dist/*")
73 |
74 |
75 | if __name__ == "__main__":
76 | release()
--------------------------------------------------------------------------------
/examples/kepware lls config example.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # Kepware LLS Config Example - Simple example on how to manage properties of a Kepware instance
8 | # to connect to a Local License Server through the Kepware Configuration API
9 |
10 | from kepconfig import connection, error
11 | from kepconfig.admin import lls
12 |
13 | def ErrorHandler(err):
14 | # Generic Handler for exception errors
15 | if isinstance(err, error.KepHTTPError):
16 | print(err.code)
17 | print(err.msg)
18 | print(err.url)
19 | print(err.hdrs)
20 | print(err.payload)
21 | elif isinstance(err, error.KepURLError):
22 | print(err.url)
23 | print(err.reason)
24 | elif isinstance(err, error.KepError):
25 | print(err.msg)
26 | else:
27 | print('Different Exception Received: {}'.format(err))
28 |
29 | # This creates a server reference that is used to target all modifications of
30 | # the Kepware configuration
31 | server = connection.server(host = '127.0.0.1', port = 57412, user = 'Administrator', pw = '')
32 |
33 |
34 |
35 | # Retreive the LLS properties from a Kepware instance and return a lls_config class object
36 | try:
37 | LLSconfig = lls.get_lls_config(server)
38 | print("{} - {}".format("Get Local License Server parameters for Kepware instance",LLSconfig))
39 | except Exception as err:
40 | ErrorHandler(err)
41 |
42 |
43 | # Create a lls_config class object from Dict parameters
44 | JSON_lls_config = {
45 | "libadminsettings.LICENSING_SERVER_PORT": 80,
46 | "libadminsettings.LICENSING_SERVER_NAME": "test_host",
47 | "libadminsettings.LICENSING_CHECK_PERIOD_MINS": 20,
48 | "libadminsettings.LICENSING_SERVER_SSL_PORT": 7777,
49 | "libadminsettings.LICENSING_SERVER_ALLOW_INSECURE_COMMS": True,
50 | "libadminsettings.LICENSING_SERVER_ALLOW_SELF_SIGNED_CERTS": True,
51 | "libadminsettings.LICENSING_CLIENT_ALIAS": "TestAliasName"
52 | }
53 |
54 | try:
55 | print("{} - {}".format("Create Local License Server parameters object from Dict", lls.lls_config(JSON_lls_config)))
56 | except Exception as err:
57 | ErrorHandler(err)
58 |
59 |
60 | # Update lls_config object with new values and update the Kepware instance with new parameters
61 | LLSconfig.server_name = 'HOSTNAME'
62 | try:
63 | print("{} - {}".format("Update Local License Server parameters for Kepware instance",lls.update_lls_config(server, LLSconfig)))
64 | except Exception as err:
65 | ErrorHandler(err)
66 |
67 | # Enable the LLS connection for the Kepware instance
68 | try:
69 | print("{} - {}".format("Enable Local License Server connection for Kepware instance",lls.enable_lls(server)))
70 | except Exception as err:
71 | ErrorHandler(err)
72 |
73 | # Disable the LLS connection for the Kepware instance
74 | try:
75 | print("{} - {}".format("Disable Local License Server connection for Kepware instance",lls.disable_lls(server)))
76 | except Exception as err:
77 | ErrorHandler(err)
--------------------------------------------------------------------------------
/kepconfig/structures.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`structures` provides general data structures to help manage
8 | various objects for Kepware's configuration
9 | """
10 | from enum import Enum
11 |
12 | class KepServiceResponse:
13 | '''A class to represent a return object when calling a "service" API of Kepware. This is
14 | used to return the responses when a "service" is executed appropriately
15 |
16 | :param code: HTTP code returned
17 |
18 | :param message: return from the "service" call
19 |
20 | :param href: URL reference to the JOB that is created by the service API
21 | '''
22 |
23 | def __init__(self, code: str = '', message: str = '', href: str = ''):
24 | self.code = code
25 | self.message = message
26 | self.href = href
27 |
28 | def __str__(self):
29 | return '{"code": %s, "message": %s, "href": %s}' % (self.code, self.message, self.href)
30 |
31 | class KepServiceStatus:
32 | '''A class to represent a status object when checking on a "service" API job state in Kepware. This is
33 | used to return the status of a "service" job
34 |
35 | :param complete: Boolean of service job completion status
36 |
37 | :param status: Status code of job
38 |
39 | :param message: Error message if service job fails
40 |
41 | '''
42 | def __init__(self, complete: bool = False, status: str = '', message: str = ''):
43 | self.status = status
44 | self.message = message
45 | self.complete = complete
46 |
47 | def __str__(self):
48 | return '{"complete": %s, "status": %s, "message": %s}' % (self.complete, self.status, self.message)
49 |
50 | class _HttpDataAbstract:
51 | def __init__(self):
52 | self.payload = ''
53 | self.code = ''
54 | self.reason = ''
55 |
56 | class FilterModifierEnum(Enum):
57 | '''Enum class to represent the various filter types that can be used in the Kepware API'''
58 | EQUAL = "eq"
59 | NOTEQUAL = "neq"
60 | GREATERTHAN = "gt"
61 | LESSTTHAN = "lt"
62 | GREATERTHANEQUAL = "gte"
63 | LESSTHANEQUAL = "lte"
64 | CONTAINS = "contains"
65 | NOTCONTAINS = "ncontains"
66 | STARTSWITH = "starts_with"
67 | NOTSTARTSWITH = "nstarts_with"
68 | ENDSWITH = "ends_with"
69 | NOTENDSWITH = "nends_with"
70 |
71 | class FilterFieldEnum(Enum):
72 | ID = "id"
73 | TIMESTAMP = "timestamp"
74 | ACTION = "action"
75 | USER = "user"
76 | INTERFACE = "interface"
77 | DETAILS = "details"
78 | DATA = "data"
79 |
80 | class Filter:
81 | '''A class to represent a filter object when calling the Kepware API. This is used to
82 | filter the results of a GET request for Audit Logs.
83 |
84 | :param name: Name of the object to filter on
85 |
86 | :param type: Type of filter to apply
87 |
88 | :param value: Value to filter on
89 | '''
90 | def __init__(self, field: FilterFieldEnum = FilterFieldEnum.ID, modifier: FilterModifierEnum = FilterModifierEnum.EQUAL, value: str = ''):
91 | self.field = field
92 | self.modifier = modifier
93 | self.value = value
94 |
95 | def __str__(self):
96 | return '{"field": %s, "modifier": %s, "value": %s}' % (self.field, self.modifier, self.value)
97 |
--------------------------------------------------------------------------------
/examples/ua server config example.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # UA Server Configuration Example - Simple example on how to manage a connection and
8 | # execute various calls for configuring the UA Server properties of the Kepware
9 | # Configuration API
10 |
11 | from kepconfig import connection, error
12 | import kepconfig.admin.ua_server as ua_server
13 |
14 | # UA Endpoints to be created with properties that can be configured
15 | uaendpoint1 = {
16 | "common.ALLTYPES_NAME": "DefaultEndpoint3",
17 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_ENABLE": True,
18 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_ADAPTER": "Default",
19 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_PORT": 49331,
20 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_SECURITY_NONE": False,
21 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_SECURITY_BASIC128_RSA15": 0,
22 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_SECURITY_BASIC256": 0,
23 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_SECURITY_BASIC256_SHA256": 2
24 | }
25 | uaendpoint2 = {
26 | "common.ALLTYPES_NAME": "DefaultEndpoint4",
27 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_ENABLE": True,
28 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_ADAPTER": "Default",
29 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_PORT": 49332,
30 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_SECURITY_NONE": False,
31 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_SECURITY_BASIC128_RSA15": 0,
32 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_SECURITY_BASIC256": 0,
33 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_SECURITY_BASIC256_SHA256": 2
34 | }
35 | def ErrorHandler(err):
36 | # Generic Handler for exception errors
37 | if isinstance(err, error.KepHTTPError):
38 | print(err.code)
39 | print(err.msg)
40 | print(err.url)
41 | print(err.hdrs)
42 | print(err.payload)
43 | elif isinstance(err, error.KepURLError):
44 | print(err.url)
45 | print(err.reason)
46 | elif isinstance(err, error.KepError):
47 | print(err.msg)
48 | else:
49 | print('Different Exception Received: {}'.format(err))
50 |
51 | # This creates a server reference that is used to target all modifications of
52 | # the Kepware configuration
53 | server = connection.server(host = 'localhost', port = 57513, user = 'Administrator', pw = '', https=True)
54 |
55 | # Disabling certificate validation (INSECURE)
56 | server.SSL_ignore_hostname = True
57 | server.SSL_trust_all_certs = True
58 |
59 | # Add the UA Endpoints to Kepware's UA server
60 | try:
61 | print("{} - {}".format("Adding multiple UA Endpoints",ua_server.add_endpoint(server,[uaendpoint1,uaendpoint2])))
62 | except Exception as err:
63 | ErrorHandler(err)
64 |
65 | # Modify Endpoint to disable all encryption and allow unencrypted connections
66 |
67 | modify_ua = {
68 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_SECURITY_NONE": True,
69 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_SECURITY_BASIC128_RSA15": 0,
70 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_SECURITY_BASIC256": 0,
71 | "libadminsettings.UACONFIGMANAGER_ENDPOINT_SECURITY_BASIC256_SHA256": 0
72 | }
73 |
74 | try:
75 | print("{} - {}".format("Modify UA Endpoint to remove all encrypted enpoints",ua_server.modify_endpoint(server, modify_ua ,uaendpoint1['common.ALLTYPES_NAME'])))
76 | except Exception as err:
77 | ErrorHandler(err)
78 |
79 | # Delete an Endpoint
80 | try:
81 | print("{} - {}".format("Delete an UA Endpoint",ua_server.del_endpoint(server,uaendpoint2['common.ALLTYPES_NAME'])))
82 | except Exception as err:
83 | ErrorHandler(err)
84 |
85 | # All changes will not update until Kepware is Reinitialized
86 | try:
87 | print("{} - {}".format("Reinitialize Kepware to update UA Endpoint Configuration",server.reinitialize()))
88 | except Exception as err:
89 | ErrorHandler(err)
90 |
91 | # Get all UA Endpoints that are configured
92 | try:
93 | print("{} - {}".format("Get all UA Endpoint Configurations",ua_server.get_all_endpoints(server)))
94 | except Exception as err:
95 | ErrorHandler(err)
96 |
97 | # Delete an Endpoint
98 | try:
99 | print("{} - {}".format("Delete an UA Endpoint",ua_server.del_endpoint(server,uaendpoint1['common.ALLTYPES_NAME'])))
100 | except Exception as err:
101 | ErrorHandler(err)
102 |
103 |
104 |
--------------------------------------------------------------------------------
/kepconfig/datalogger/mapping.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`mapping` exposes an API to allow modifications (add, delete, modify) to
8 | column mapping objects in a Datalogger log group within the Kepware Configuration API
9 | """
10 |
11 | from . import log_group as Log_Group
12 | from ..error import KepError, KepHTTPError
13 | from ..connection import server
14 | from ..utils import _url_parse_object
15 |
16 | MAPPING_ROOT = '/column_mappings'
17 |
18 | def _create_url(mapping = None):
19 | '''Creates url object for the "column_mappings" branch of Kepware's project tree. Used
20 | to build a part of Kepware Configuration API URL structure
21 |
22 | Returns the mapping specific url when a value is passed as the column_mapping name.
23 | '''
24 |
25 | if mapping == None:
26 | return '{}'.format(MAPPING_ROOT)
27 | else:
28 | return '{}/{}'.format(MAPPING_ROOT, _url_parse_object(mapping))
29 |
30 | def modify_mapping(server: server, log_group: str, DATA: dict, *, mapping: str = None, force: bool = False) -> bool:
31 | '''Modify a column `"mapping"` object and it's properties in Kepware. If a `"mapping"` is not provided as an input,
32 | you need to identify the column mapping in the *'common.ALLTYPES_NAME'* property field in the `"DATA"`. It will
33 | assume that is the column mapping that is to be modified.
34 |
35 | :param server: instance of the `server` class
36 | :param log_group: name of log group for the mapping
37 | :param DATA: Dict of the mapping properties to be modified.
38 | :param mapping: *(optional)* column mapping to modify in the log group. Only needed if not existing in `"DATA"`
39 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
40 |
41 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
42 |
43 | :raises KepHTTPError: If urllib provides an HTTPError
44 | :raises KepURLError: If urllib provides an URLError
45 | '''
46 |
47 | mapping_data = server._force_update_check(force, DATA)
48 |
49 | if mapping == None:
50 | try:
51 | r = server._config_update(server.url + Log_Group._create_url(log_group) + _create_url(mapping_data['common.ALLTYPES_NAME']), mapping_data)
52 | if r.code == 200: return True
53 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
54 | except KeyError as err:
55 | err_msg = 'Error: No column mapping identified in DATA | Key Error: {}'.format(err)
56 | raise KepError(err_msg)
57 | else:
58 | r = server._config_update(server.url + Log_Group._create_url(log_group) + _create_url(mapping), mapping_data)
59 | if r.code == 200: return True
60 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
61 |
62 | def get_mapping(server: server, log_group: str, mapping: str) -> dict:
63 | '''Returns the properties of the `"mapping"` object.
64 |
65 | :param server: instance of the `server` class
66 | :param log_group: name of log group for the mapping
67 | :param mapping: name of column mapping to retrieve properties
68 |
69 | :return: Dict of properties for the mapping object requested
70 |
71 | :raises KepHTTPError: If urllib provides an HTTPError
72 | :raises KepURLError: If urllib provides an URLError
73 | '''
74 | r = server._config_get(server.url + Log_Group._create_url(log_group) + _create_url(mapping))
75 | return r.payload
76 |
77 | def get_all_mappings(server: server, log_group: str, *, options: dict = None) -> list:
78 | '''Returns the properties of all column `"mapping"` objects for a log group.
79 |
80 | :param server: instance of the `server` class
81 | :param log_group: name of log group for the mapping
82 | :param options: *(optional)* Dict of parameters to filter, sort or pagenate the list of mapping items. Options are 'filter',
83 | 'sortOrder', 'sortProperty', 'pageNumber', and 'pageSize'. Only used when exchange_name is not defined.
84 |
85 | :return: list of properties for all mapping items in the log group requested
86 |
87 | :raises KepHTTPError: If urllib provides an HTTPError
88 | :raises KepURLError: If urllib provides an URLError
89 | '''
90 | r = server._config_get(f'{server.url}{Log_Group._create_url(log_group)}{_create_url()}', params= options)
91 | return r.payload
92 |
--------------------------------------------------------------------------------
/tests/udd_test.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # UDD Test - Test to exersice all UDD Profile Library related features
8 |
9 | import os, sys
10 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
11 | import kepconfig
12 | from kepconfig import error, connectivity
13 | import kepconfig.connectivity.udd.profile as uddprofile
14 | import time
15 | import datetime
16 | import pytest
17 |
18 |
19 | # Channel and Device name to be used
20 | profile1 = {
21 | "common.ALLTYPES_NAME": "Profile1",
22 | "common.ALLTYPES_DESCRIPTION": "",
23 | "libudcommon.LIBUDCOMMON_PROFILE_JAVASCRIPT": "function onProfileLoad () {return {version: \"2.0\", mode: \"Client\"};}\nfunction onValidateTag (info) {}\nfunction onTagsRequest (info) {}\nfunction onData (info) {}\n"
24 | }
25 | profile2 = {
26 | "common.ALLTYPES_NAME": "Profile2",
27 | "common.ALLTYPES_DESCRIPTION": "",
28 | "libudcommon.LIBUDCOMMON_PROFILE_JAVASCRIPT": "function onProfileLoad () {return {version: \"2.0\", mode: \"Client\"};}\nfunction onValidateTag (info) {}\nfunction onTagsRequest (info) {}\nfunction onData (info) {}\n"
29 | }
30 |
31 | def ErrorHandler(err):
32 | # Generic Handler for exception errors
33 | if isinstance(err, error.KepHTTPError):
34 | print(err.code)
35 | print(err.msg)
36 | print(err.url)
37 | print(err.hdrs)
38 | print(err.payload)
39 | elif isinstance(err, error.KepURLError):
40 | print(err.url)
41 | print(err.reason)
42 | elif isinstance(err, error.KepError):
43 | print(err.msg)
44 | else:
45 | print('Different Exception Received: {}'.format(err))
46 |
47 |
48 | def initialize(server: kepconfig.connection.server):
49 | if server_type == 'TKE': pytest.skip("UDD not configurable in {}.".format(server_type), allow_module_level=True)
50 |
51 | try:
52 | server._config_get(server.url +"/doc/drivers/Universal Device/channels")
53 | except Exception as err:
54 | pytest.skip("UDD Driver is not installed", allow_module_level=True)
55 |
56 | def complete(server):
57 | try:
58 | profiles_list = uddprofile.get_all_profiles(server)
59 | [uddprofile.del_profile(server, g['common.ALLTYPES_NAME']) for g in profiles_list]
60 | except Exception as err:
61 | ErrorHandler(err)
62 |
63 | @pytest.fixture(scope="module")
64 | def server(kepware_server):
65 | server = kepware_server[0]
66 | global server_type
67 | server_type = kepware_server[1]
68 |
69 | # Initialize any configuration before testing in module
70 | initialize(server)
71 |
72 | # Everything below yield is run after module tests are completed
73 | yield server
74 | complete(server)
75 |
76 | #
77 | # MAIN TEST SET
78 | #
79 |
80 |
81 | def test_profile_add(server: kepconfig.connection.server):
82 | # Add profile
83 | assert uddprofile.add_profile(server, profile1)
84 |
85 | # Add multi profiles with one failure
86 | assert type(uddprofile.add_profile(server, [profile1, profile2])) == list
87 |
88 | def test_profile_get(server: kepconfig.connection.server):
89 | # Get All Profiles
90 | assert type(uddprofile.get_all_profiles(server)) == list
91 |
92 | # Test Get with Options
93 | # Get All Profiles
94 | ret = uddprofile.get_all_profiles(server, options={'filter': '1'})
95 | assert type(ret) == list
96 | assert len(ret) == 1
97 |
98 | # Get All Profiles - alternate
99 | assert type(uddprofile.get_profile(server)) == list
100 |
101 | # Test Get with Options
102 | # Get All Profiles - alternate
103 | ret = uddprofile.get_profile(server, options={'filter': '1'})
104 | assert type(ret) == list
105 | assert len(ret) == 1
106 |
107 | # Get one profile
108 | assert type(uddprofile.get_profile(server, profile1['common.ALLTYPES_NAME'])) == dict
109 |
110 |
111 | def test_profile_modify(server: kepconfig.connection.server):
112 | # Modify Profile
113 | profile1['common.ALLTYPES_DESCRIPTION'] = 'Test Change'
114 | assert uddprofile.modify_profile(server, profile1)
115 |
116 | # Modify Profile with name parameter
117 | change = {'common.ALLTYPES_DESCRIPTION':'Test Change'}
118 | assert uddprofile.modify_profile(server, change, profile1['common.ALLTYPES_NAME'], force=True)
119 |
120 | def test_profile_delete(server: kepconfig.connection.server):
121 | # Modify Profile
122 | assert uddprofile.del_profile(server, profile1['common.ALLTYPES_NAME'])
--------------------------------------------------------------------------------
/kepconfig/ua_gateway/common.py:
--------------------------------------------------------------------------------
1 | from ..utils import _url_parse_object
2 | from ..connection import server
3 | from ..error import KepHTTPError
4 | from enum import Enum
5 |
6 | r"""`common` contains common internal functions and constants used by the
7 | `ua_gateway` module.
8 | """
9 |
10 | CERT_TRUST_KEY = 'ua_gateway.UA_CERTIFICATE_TRUST_STATUS'
11 |
12 | # URL Constants for UA Gateway Module
13 |
14 | UA_GATEWAY_ROOT = '/project/_ua_gateway'
15 | CERT_ROOT = f'{UA_GATEWAY_ROOT}/certificates'
16 | CLIENT_ROOT = f'{UA_GATEWAY_ROOT}/ua_client_interfaces/Client Interface'
17 | CONN_ROOT = f'{CLIENT_ROOT}/ua_client_connections'
18 | CLIENT_CERT_ROOT = f'{CLIENT_ROOT}/certificates'
19 | CLIENT_INST_CERT_ROOT = f'{CLIENT_ROOT}/client_instance_certificates'
20 | SERVER_ROOT = f'{UA_GATEWAY_ROOT}/ua_server_interfaces/Server Interface'
21 | ENDPOINT_ROOT = f'{SERVER_ROOT}/ua_server_endpoints'
22 | SERVER_CERT_ROOT = f'{SERVER_ROOT}/certificates'
23 | SERVER_INST_CERT_ROOT = f'{SERVER_ROOT}/server_instance_certificates'
24 |
25 |
26 | class _INTER_TYPE(Enum):
27 | SERVER = 0
28 | CLIENT = 1
29 | CERTS = 2
30 |
31 | # TODO: DEPRECATED: This constant is deprecated and will be removed in a future release.
32 | INSTANCE_CERTIFICATE = "Instance Certificate"
33 |
34 | def _create_url_cert(interface, certificate = None):
35 | '''Creates url object for the "certificate" branch of Kepware's UA Gateway. Used
36 | to build a part of Kepware Configuration API URL structure
37 | '''
38 | if interface == _INTER_TYPE.SERVER:
39 | if certificate == None:
40 | return SERVER_CERT_ROOT
41 | else:
42 | return f'{SERVER_CERT_ROOT}/{_url_parse_object(certificate)}'
43 | elif interface == _INTER_TYPE.CLIENT:
44 | if certificate == None:
45 | return CLIENT_CERT_ROOT
46 | else:
47 | return f'{CLIENT_CERT_ROOT}/{_url_parse_object(certificate)}'
48 | # TODO: DEPRECATED: This interface type is deprecated and will be removed in a future release.
49 | else:
50 | if certificate == None:
51 | return CERT_ROOT
52 | else:
53 | return '{}/{}'.format(CERT_ROOT,_url_parse_object(certificate))
54 |
55 | def _create_url_inst_cert(interface, certificate = None):
56 | '''Creates url object for the "instance certificate" branch of Kepware's UA Gateway interfaces. Used
57 | to build a part of Kepware Configuration API URL structure
58 | '''
59 | if interface == _INTER_TYPE.SERVER:
60 | if certificate == None:
61 | return SERVER_INST_CERT_ROOT
62 | else:
63 | return f'{SERVER_INST_CERT_ROOT}/{_url_parse_object(certificate)}'
64 | elif interface == _INTER_TYPE.CLIENT:
65 | if certificate == None:
66 | return CLIENT_INST_CERT_ROOT
67 | else:
68 | return f'{CLIENT_INST_CERT_ROOT}/{_url_parse_object(certificate)}'
69 | else:
70 | if certificate == None:
71 | return CERT_ROOT
72 | else:
73 | return '{}/{}'.format(CERT_ROOT,_url_parse_object(certificate))
74 |
75 | def _change_cert_trust(server: server, inter_type, certificate: str, trust: bool):
76 | DATA = {
77 | CERT_TRUST_KEY: int(trust)
78 | }
79 |
80 | cert_data = server._force_update_check(True, DATA)
81 | r = server._config_update(server.url + _create_url_cert(inter_type, certificate), cert_data)
82 | if r.code == 200: return True
83 | else:
84 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
85 |
86 | def _delete_cert_truststore(server: server, inter_type, certificate: str):
87 | r = server._config_del(server.url + _create_url_cert(inter_type, certificate))
88 | if r.code == 200: return True
89 | else:
90 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
91 |
92 | def _create_url_server(ua_server_endpoint = None):
93 | '''Creates url object for the "ua_server_endpoints" branch of Kepware's UA Gateway. Used
94 | to build a part of Kepware Configuration API URL structure
95 |
96 | Returns the UA Gateway client connections specific url when a value is passed as the ua client interface name.
97 | '''
98 | if ua_server_endpoint == None:
99 | return ENDPOINT_ROOT
100 | else:
101 | return f'{ENDPOINT_ROOT}/{_url_parse_object(ua_server_endpoint)}'
102 |
103 | def _create_url_client(ua_client_connection = None):
104 | '''Creates url object for the "ua_client_connections" branch of Kepware's UA Gateway. Used
105 | to build a part of Kepware Configuration API URL structure
106 |
107 | Returns the UA Gateway client connections specific url when a value is passed as the ua client interface name.
108 | '''
109 | if ua_client_connection == None:
110 | return CONN_ROOT
111 | else:
112 | return f'{CONN_ROOT}/{_url_parse_object(ua_client_connection)}'
--------------------------------------------------------------------------------
/examples/services and logs example.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # Services and Logs Example - Simple example on how to call services and
8 | # get logs from the Kepware Configuration API
9 |
10 | from kepconfig import connection, error
11 | from kepconfig.connectivity import channel, device
12 | import json
13 | import datetime
14 |
15 | # Channel and Device name to be used
16 | ch_name = 'ControlLogix_Channel'
17 | dev_name = 'Device1'
18 | device_IP = '192.168.1.100'
19 | # Service Response Global
20 | r = {}
21 |
22 | def ErrorHandler(err):
23 | # Generic Handler for exception errors
24 | if isinstance(err, error.KepHTTPError):
25 | print(err.code)
26 | print(err.msg)
27 | print(err.url)
28 | print(err.hdrs)
29 | print(err.payload)
30 | elif isinstance(err, error.KepURLError):
31 | print(err.url)
32 | print(err.reason)
33 | elif isinstance(err, error.KepError):
34 | print(err.msg)
35 | else:
36 | print('Different Exception Received: {}'.format(err))
37 |
38 | # This creates a server reference that is used to target all modifications of
39 | # the Kepware configuration
40 | server = connection.server(host = '127.0.0.1', port = 57412, user = 'Administrator', pw = '')
41 |
42 | # This Reinitializes Kepware's Server Runtime process, similar to manually reinitializing
43 | # using the Configuration UI or the Administrator tool.
44 | try:
45 | r = server.reinitialize()
46 | print('{} - {}'.format("Execute Reinitialize Service", r))
47 | except Exception as err:
48 | ErrorHandler(err)
49 |
50 | # This reads the Reinitialize Service Job Status using the returned KepServiceResponse
51 |
52 | try:
53 | print('{} - {}'.format("Read Reinitialize Service Status", server.service_status(r)))
54 | except Exception as err:
55 | ErrorHandler(err)
56 |
57 | # Add a Channel using the "ControlLogix Driver" with a ControlLogix 5500 family device.
58 | # This will be used to demonstrate the "Auto Tag Generation" service available.
59 | channel_data = {
60 | "common.ALLTYPES_NAME": ch_name,
61 | "common.ALLTYPES_DESCRIPTION": "This is the test channel created",
62 | "servermain.MULTIPLE_TYPES_DEVICE_DRIVER": "Allen-Bradley Controllogix Ethernet",
63 | "devices": [
64 | {
65 | "common.ALLTYPES_NAME": dev_name,
66 | "common.ALLTYPES_DESCRIPTION": "Hello, new description",
67 | "servermain.MULTIPLE_TYPES_DEVICE_DRIVER": "Allen-Bradley Controllogix Ethernet",
68 | "servermain.DEVICE_MODEL": 0,
69 | "servermain.DEVICE_ID_STRING": "<{}>,1,0".format(device_IP)
70 | }
71 | ]
72 | }
73 | try:
74 | print("{} - {}".format("Adding Controllogix Channel and Device", channel.add_channel(server,channel_data)))
75 | except Exception as err:
76 | ErrorHandler(err)
77 |
78 | # Execute the "TagGeneration" service available in the Kepware Configuration API
79 | try:
80 | r = device.auto_tag_gen(server, '{}.{}'.format(ch_name, dev_name))
81 | print("{} - {}".format("Executing ATG for Controllogix Device", r))
82 | except Exception as err:
83 | ErrorHandler(err)
84 |
85 | # This reads the TagGeneration Service Job Status using the returned KepServiceResponse
86 | try:
87 | print('{} - {}'.format("Read Service Status", server.service_status(r)))
88 | except Exception as err:
89 | ErrorHandler(err)
90 |
91 | # Get Event Log from Kepware instance.
92 | # Time parameters need to be UTC values.
93 | try:
94 | print("{} - {}".format("Here is the last Event Log Entry", json.dumps(server.get_event_log(1), indent=4)))
95 | except Exception as err:
96 | ErrorHandler(err)
97 | try:
98 | print("{} - {}".format("Here are the last 25 entries of the Event Log", json.dumps(server.get_event_log(25, datetime.datetime.fromisoformat('2019-11-03T23:35:23.000'), datetime.datetime.utcnow()), indent=4)))
99 | except Exception as err:
100 | ErrorHandler(err)
101 |
102 | try:
103 | print("{} - {}".format("Here are only the Information entries of the Event Log", json.dumps(server.get_event_log(None, datetime.datetime.fromisoformat('2019-11-03T23:35:23.000'), datetime.datetime.utcnow(), options= {'event': 'Information'}), indent=4)))
104 | except Exception as err:
105 | ErrorHandler(err)
106 |
107 | #Get Configuration API Transaction Log
108 | try:
109 | print("{} - {}".format("Here is the last API Transaction Log Entry", json.dumps(server.get_transaction_log(1), indent=4)))
110 | except Exception as err:
111 | ErrorHandler(err)
112 |
113 | # Export the Project Configuration as JSON from the Kepware Server instance. Provides the same information as saving the
114 | # project file as JSON in the Configuration UI locally.
115 | projectJson = server.export_project_configuration()
116 | try:
117 | print("{} - {}".format("Here is the full Project Configuration as JSON", json.dumps(projectJson, indent=4)))
118 | except Exception as err:
119 | ErrorHandler(err)
120 |
121 | # Import the Project Configuration from JSON. This service acts like a FILE->OPEN action and
122 | # stop communications while the new project replaces the current project in the Kepware runtime.
123 | try:
124 | r = server.import_project_configuration(projectJson)
125 | print("{} - {}".format("Executing Project Import service", r))
126 | print('{} - {}'.format("Read Service Status", server.service_status(r)))
127 | except Exception as err:
128 | ErrorHandler(err)
--------------------------------------------------------------------------------
/kepconfig/admin/ua_server.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c), PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`ua_server` exposes an API to allow modifications (add, delete, modify) to
8 | OPC UA Server endpoints within the Kepware Administration through the Kepware Configuration API
9 | """
10 | from typing import Union
11 | from ..error import KepHTTPError, KepError
12 | from ..connection import server
13 | from ..utils import _url_parse_object
14 |
15 |
16 | UA_ROOT = '/admin/ua_endpoints'
17 |
18 | def _create_url(endpoint = None):
19 | '''Creates url object for the "server_users" branch of Kepware's project tree. Used
20 | to build a part of Kepware Configuration API URL structure
21 |
22 | Returns the user specific url when a value is passed as the user name.
23 | '''
24 |
25 | if endpoint == None:
26 | return UA_ROOT
27 | else:
28 | return '{}/{}'.format(UA_ROOT, _url_parse_object(endpoint))
29 |
30 | def add_endpoint(server: server, DATA: Union[dict, list]) -> Union[bool, list]:
31 | '''Add an `"endpoint"` or multiple `"endpoint"` objects to Kepware UA Server by passing a
32 | list of endpoints to be added all at once.
33 |
34 | :param server: instance of the `server` class
35 | :param DATA: Dict or List of Dicts of the UA Endpoints to add
36 |
37 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
38 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
39 | endpoints added that failed.
40 |
41 | :raises KepHTTPError: If urllib provides an HTTPError
42 | :raises KepURLError: If urllib provides an URLError
43 | '''
44 |
45 | r = server._config_add(server.url + _create_url(), DATA)
46 | if r.code == 201: return True
47 | elif r.code == 207:
48 | errors = []
49 | for item in r.payload:
50 | if item['code'] != 201:
51 | errors.append(item)
52 | return errors
53 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
54 |
55 | def del_endpoint(server: server, endpoint: str) -> bool:
56 | '''Delete an `"endpoint"` object in Kepware UA Server
57 |
58 | :param server: instance of the `server` class
59 | :param endpoint: name of endpoint to delete
60 |
61 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
62 |
63 | :raises KepHTTPError: If urllib provides an HTTPError
64 | :raises KepURLError: If urllib provides an URLError
65 | '''
66 |
67 | r = server._config_del(server.url + _create_url(endpoint))
68 | if r.code == 200: return True
69 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
70 |
71 | def modify_endpoint(server: server, DATA: dict, endpoint: str = None) -> bool:
72 | '''Modify a `"endpoint"` object and it's properties in Kepware UA Server. If a `"endpoint"` is not provided as an input,
73 | you need to identify the endpoint in the *'common.ALLTYPES_NAME'* property field in the `"DATA"`. It will
74 | assume that is the endpoint that is to be modified.
75 |
76 | :param server: instance of the `server` class
77 | :param DATA: Dict of the UA endpoint properties to be modified.
78 | :param endpoint: *(optional)* name of endpoint to modify. Only needed if not existing in `"DATA"`
79 |
80 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
81 |
82 | :raises KepHTTPError: If urllib provides an HTTPError
83 | :raises KepURLError: If urllib provides an URLError
84 | '''
85 |
86 | if endpoint == None:
87 | try:
88 | r = server._config_update(server.url + _create_url(DATA['common.ALLTYPES_NAME']), DATA)
89 | if r.code == 200: return True
90 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
91 | except KeyError as err:
92 | err_msg = 'Error: No UA Endpoint identified in DATA | Key Error: {}'.format(err)
93 | raise KepError(err_msg)
94 | else:
95 | r = server._config_update(server.url + _create_url(endpoint), DATA)
96 | if r.code == 200: return True
97 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
98 |
99 | def get_endpoint(server: server, endpoint: str) -> dict:
100 | '''Returns the properties of the `"endpoint"` object.
101 |
102 | :param server: instance of the `server` class
103 | :param endpoint: name of endpoint to retrieve
104 |
105 | :return: Dict of properties for the UA endpoint requested
106 |
107 | :raises KepHTTPError: If urllib provides an HTTPError
108 | :raises KepURLError: If urllib provides an URLError
109 | '''
110 |
111 | r = server._config_get(server.url + _create_url(endpoint))
112 | return r.payload
113 |
114 | def get_all_endpoints(server: server, *, options: dict = None) -> list:
115 | '''Returns list of all `"endpoint"` objects and their properties.
116 |
117 | :param server: instance of the `server` class
118 | :param options: *(optional)* Dict of parameters to filter, sort or pagenate the list of UA endpoints. Options are 'filter',
119 | 'sortOrder', 'sortProperty', 'pageNumber', and 'pageSize.
120 |
121 | :return: List of properties for all UA endpoints requested
122 |
123 | :raises KepHTTPError: If urllib provides an HTTPError
124 | :raises KepURLError: If urllib provides an URLError
125 | '''
126 |
127 | r = server._config_get(f'{server.url}{_create_url()}', params= options)
128 | return r.payload
--------------------------------------------------------------------------------
/kepconfig/connectivity/udd/profile.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 |
8 | r"""`profile` exposes an API to allow modifications (add, delete, modify) to
9 | profile objects for the UDD Profile Library plug-in within the Kepware Configuration API
10 | """
11 |
12 | from ...connection import server
13 | from ...error import KepHTTPError, KepError
14 | from typing import Union
15 |
16 | PROFILE_ROOT = '/project/_profile_library/profiles'
17 |
18 | def add_profile(server: server, DATA: Union[dict, list]) -> Union[bool, list]:
19 | '''Add a `"profile"` or a list of `"profile"` objects to the UDD Profile Library plug-in for Kepware.
20 |
21 | :param server: instance of the `server` class
22 | :param DATA: Dict or List of Dicts of the profiles to add to the Profile Library
23 | through Kepware Configuration API
24 |
25 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
26 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
27 | profiles added that failed.
28 |
29 | :raises KepHTTPError: If urllib provides an HTTPError
30 | :raises KepURLError: If urllib provides an URLError
31 | '''
32 |
33 | r = server._config_add(f'{server.url}{PROFILE_ROOT}', DATA)
34 | if r.code == 201: return True
35 | elif r.code == 207:
36 | errors = []
37 | for item in r.payload:
38 | if item['code'] != 201:
39 | errors.append(item)
40 | return errors
41 | else:
42 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
43 |
44 | def del_profile(server: server, profile_name: str) -> bool:
45 | '''Delete a `"profile"` object in UDD Profile Library plug-in for Kepware.
46 |
47 | :param server: instance of the `server` class
48 | :param profile_name: name of profile
49 |
50 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
51 |
52 | :raises KepHTTPError: If urllib provides an HTTPError
53 | :raises KepURLError: If urllib provides an URLError
54 | '''
55 |
56 | r = server._config_del(f'{server.url}{PROFILE_ROOT}/{profile_name}')
57 | if r.code == 200: return True
58 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
59 |
60 | def modify_profile(server: server, DATA: dict, profile_name: str = None, force: bool = False) -> bool:
61 | '''Modify a `"profile"` object and it's properties in Kepware. If a `"profile_name"` is not provided as an input,
62 | you need to identify the profile in the *'common.ALLTYPES_NAME'* property field in the `"DATA"`. It will
63 | assume that is the profile that is to be modified.
64 |
65 | :param server: instance of the `server` class
66 | :param DATA: Dict or List of Dicts of the profile properties to be modified.
67 | :param profile_name: *(optional)* name of profile to modify. Only needed if not existing in `"DATA"`
68 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
69 |
70 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
71 |
72 | :raises KepHTTPError: If urllib provides an HTTPError
73 | :raises KepURLError: If urllib provides an URLError
74 | '''
75 |
76 | profile_data = server._force_update_check(force, DATA)
77 | if profile_name == None:
78 | try:
79 | r = server._config_update(f"{server.url}{PROFILE_ROOT}/{profile_data['common.ALLTYPES_NAME']}", profile_data)
80 | if r.code == 200: return True
81 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
82 | except KeyError as err:
83 | err_msg = 'Error: No profile identified in DATA | Key Error: {}'.format(err)
84 | raise KepError(err_msg)
85 | else:
86 | r = server._config_update(f'{server.url}{PROFILE_ROOT}/{profile_name}', profile_data)
87 | if r.code == 200: return True
88 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
89 |
90 | def get_profile(server: server, profile_name: str = None, *, options: dict = None) -> Union[dict, list]:
91 | '''Returns the properties of the profile object or a list of all profiles and their
92 | properties. Will return a list if `"profile_name"` is not provided.
93 |
94 | INPUTS:
95 |
96 | :param server: instance of the `server` class
97 | :param profile_name: *(optional)* name of profile. If not defined, will get all profiles
98 | :param options: *(optional)* Dict of parameters to filter, sort or pagenate when getting a list of profiles. Options are `filter`,
99 | `sortOrder`, `sortProperty`, `pageNumber`, and `pageSize`. Only used when profile_name is not defined.
100 |
101 | :return: Dict of the profile properties or List of Dicts for all profiles and their properties in the Profile Library
102 |
103 | :raises KepHTTPError: If urllib provides an HTTPError
104 | :raises KepURLError: If urllib provides an URLError
105 | '''
106 |
107 | if profile_name == None:
108 | r = server._config_get(f'{server.url}{PROFILE_ROOT}', params= options)
109 | else:
110 | r = server._config_get(f'{server.url}{PROFILE_ROOT}/{profile_name}')
111 | return r.payload
112 |
113 | def get_all_profiles(server: server, *, options: dict = None):
114 | '''Returns list of all profile objects and their properties. Returned object is JSON list.
115 |
116 | :param server: instance of the `server` class
117 | :param options: *(optional)* Dict of parameters to filter, sort or pagenate when getting a list of profiles. Options are `filter`,
118 | `sortOrder`, `sortProperty`, `pageNumber`, and `pageSize`. Only used when profile_name is not defined.
119 |
120 | :return: List of data for all profiles and their properties in the Profile Library
121 |
122 | :raises KepHTTPError: If urllib provides an HTTPError
123 | :raises KepURLError: If urllib provides an URLError
124 | '''
125 | return get_profile(server, options= options)
--------------------------------------------------------------------------------
/kepconfig/admin/users.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c), PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`users` exposes an API to allow modifications (add, delete, modify) to
8 | users within the Kepware Administration User Management through the Kepware Configuration API
9 | """
10 | from typing import Union
11 | from ..error import KepError, KepHTTPError
12 | from ..connection import server
13 | from ..utils import _url_parse_object
14 |
15 |
16 | USERS_ROOT = '/admin/server_users'
17 | ENABLE_PROPERTY = 'libadminsettings.USERMANAGER_USER_ENABLED'
18 |
19 | def _create_url(user = None):
20 | '''Creates url object for the "server_users" branch of Kepware's project tree. Used
21 | to build a part of Kepware Configuration API URL structure
22 |
23 | Returns the user specific url when a value is passed as the user name.
24 | '''
25 |
26 | if user == None:
27 | return USERS_ROOT
28 | else:
29 | return '{}/{}'.format(USERS_ROOT, _url_parse_object(user))
30 |
31 | def add_user(server: server, DATA: Union[dict, list]) -> Union[bool, list]:
32 | '''Add a `"user"` or multiple `"user"` objects to Kepware User Manager by passing a
33 | list of users to be added all at once.
34 |
35 | :param server: instance of the `server` class
36 | :param DATA: Dict or List of Dicts of the users to add
37 |
38 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
39 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
40 | endpoints added that failed.
41 |
42 | :raises KepHTTPError: If urllib provides an HTTPError
43 | :raises KepURLError: If urllib provides an URLError
44 | '''
45 |
46 | r = server._config_add(server.url + _create_url(), DATA)
47 | if r.code == 201: return True
48 | elif r.code == 207:
49 | errors = []
50 | for item in r.payload:
51 | if item['code'] != 201:
52 | errors.append(item)
53 | return errors
54 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
55 |
56 | def del_user(server: server, user: str) -> bool:
57 | '''Delete a `"user"` object in Kepware User Manager
58 |
59 | :param server: instance of the `server` class
60 | :param user: name of user to delete
61 |
62 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
63 |
64 | :raises KepHTTPError: If urllib provides an HTTPError
65 | :raises KepURLError: If urllib provides an URLError
66 | '''
67 |
68 | r = server._config_del(server.url + _create_url(user))
69 | if r.code == 200: return True
70 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
71 |
72 | def modify_user(server: server , DATA: dict, *, user: str = None) -> bool:
73 | '''Modify a `"user object"` and it's properties in Kepware User Manager. If a `"user"` is not provided as an input,
74 | you need to identify the user in the *'common.ALLTYPES_NAME'* property field in the `"DATA"`. It will
75 | assume that is the user that is to be modified.
76 |
77 | :param server: instance of the `server` class
78 | :param DATA: Dict of the user properties to be modified.
79 | :param user: *(optional)* name of user to modify. Only needed if not existing in `"DATA"`
80 |
81 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
82 |
83 | :raises KepHTTPError: If urllib provides an HTTPError
84 | :raises KepURLError: If urllib provides an URLError
85 | '''
86 |
87 | if user == None:
88 | try:
89 | r = server._config_update(server.url + _create_url(DATA['common.ALLTYPES_NAME']), DATA)
90 | if r.code == 200: return True
91 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
92 | except KeyError as err:
93 | err_msg = 'Error: No User identified in DATA | Key Error: {}'.format(err)
94 | raise KepError(err_msg)
95 | else:
96 | r = server._config_update(server.url + _create_url(user), DATA)
97 | if r.code == 200: return True
98 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
99 |
100 | def get_user(server: server, user: str) -> dict:
101 | '''Returns the properties of the `"user"` object.
102 |
103 | :param server: instance of the `server` class
104 | :param user: name of user to retrieve
105 |
106 | :return: Dict of properties for the user requested
107 |
108 | :raises KepHTTPError: If urllib provides an HTTPError
109 | :raises KepURLError: If urllib provides an URLError
110 | '''
111 |
112 | r = server._config_get(server.url + _create_url(user))
113 | return r.payload
114 |
115 | def get_all_users(server: server, *, options: dict = None) -> list:
116 | '''Returns list of all `"user"` objects and their properties.
117 |
118 | :param server: instance of the `server` class
119 | :param options: *(optional)* Dict of parameters to filter, sort or pagenate the list of users. Options are 'filter',
120 | 'sortOrder', 'sortProperty', 'pageNumber', and 'pageSize.
121 |
122 | :return: List of properties for all users
123 |
124 | :raises KepHTTPError: If urllib provides an HTTPError
125 | :raises KepURLError: If urllib provides an URLError
126 | '''
127 |
128 | r = server._config_get(f'{server.url}{_create_url()}', params= options)
129 | return r.payload
130 |
131 | def enable_user(server: server, user: str) -> bool:
132 | '''Enable the `"user"`.
133 |
134 | :param server: instance of the `server` class
135 | :param user: name of user
136 |
137 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
138 |
139 | :raises KepHTTPError: If urllib provides an HTTPError
140 | :raises KepURLError: If urllib provides an URLError
141 | '''
142 | DATA = {ENABLE_PROPERTY: True}
143 | return modify_user(server, DATA, user= user)
144 |
145 | def disable_user(server: server, user: str) -> bool:
146 | '''Disable the `"user"`.
147 |
148 | :param server: instance of the `server` class
149 | :param user: name of user
150 |
151 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
152 |
153 | :raises KepHTTPError: If urllib provides an HTTPError
154 | :raises KepURLError: If urllib provides an URLError
155 | '''
156 | DATA = {ENABLE_PROPERTY: False}
157 | return modify_user(server, DATA, user= user)
--------------------------------------------------------------------------------
/kepconfig/datalogger/log_items.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`log_items` exposes an API to allow modifications (add, delete, modify) to
8 | log item (tag) objects in a Datalogger log group within the Kepware Configuration API
9 | """
10 | from typing import Union
11 | from . import log_group as Log_Group
12 | from ..error import KepError, KepHTTPError
13 | from ..connection import server
14 | from ..utils import _url_parse_object
15 |
16 | LOG_ITEMS_ROOT = '/log_items'
17 |
18 | def _create_url(log_item = None):
19 | '''Creates url object for the "log_item" branch of Kepware's project tree. Used
20 | to build a part of Kepware Configuration API URL structure
21 |
22 | Returns the log_item specific url when a value is passed as the log_item name.
23 | '''
24 |
25 | if log_item == None:
26 | return '{}'.format(LOG_ITEMS_ROOT)
27 | else:
28 | return '{}/{}'.format(LOG_ITEMS_ROOT, _url_parse_object(log_item))
29 |
30 |
31 | def add_log_item(server: server, log_group: str, DATA: Union[dict, list]) -> Union[bool, list]:
32 | '''Add a `"log item"` or multiple `"log item"` objects to a log group in Kepware's Datalogger. It can
33 | be used to pass a list of log items to be added all at once.
34 |
35 | :param server: instance of the `server` class
36 | :param log_group: name of log group that the log items will be added
37 | :param DATA: Dict or a list of the log items to add through Kepware Configuration API
38 |
39 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
40 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
41 | log items added that failed.
42 |
43 | :raises KepHTTPError: If urllib provides an HTTPError
44 | :raises KepURLError: If urllib provides an URLError
45 | '''
46 |
47 | r = server._config_add(server.url + Log_Group._create_url(log_group) + _create_url(), DATA)
48 | if r.code == 201: return True
49 | elif r.code == 207:
50 | errors = []
51 | for item in r.payload:
52 | if item['code'] != 201:
53 | errors.append(item)
54 | return errors
55 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
56 |
57 | def del_log_item(server: server, log_group: str, log_item: str) -> bool:
58 | '''Delete a `"log item"` object of a log group in Kepware's Datalogger.
59 |
60 | :param server: instance of the `server` class
61 | :param log_group: name of log group that log item exists
62 | :param log_item: name of log item to delete
63 |
64 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
65 |
66 | :raises KepHTTPError: If urllib provides an HTTPError
67 | :raises KepURLError: If urllib provides an URLError
68 | '''
69 | r = server._config_del(server.url + Log_Group._create_url(log_group) + _create_url(log_item))
70 | if r.code == 200: return True
71 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
72 |
73 | def modify_log_item(server: server, log_group: str, DATA: dict, *, log_item: str = None, force: bool = False) -> bool:
74 | '''Modify a `"log_item"` object and it's properties in Kepware. If a `"log_item"` is not provided as an input,
75 | you need to identify the log_item in the *'common.ALLTYPES_NAME'* property field in the `"DATA"`. It will
76 | assume that is the log_item that is to be modified.
77 |
78 | :param server: instance of the `server` class
79 | :param log_group: name of log group that log item exists
80 | :param DATA: Dict of the log item properties to be modified.
81 | :param log_item: *(optional)* name of log item to modify. Only needed if not existing in `"DATA"`
82 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
83 |
84 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
85 |
86 | :raises KepHTTPError: If urllib provides an HTTPError
87 | :raises KepURLError: If urllib provides an URLError
88 | '''
89 |
90 | log_item_data = server._force_update_check(force, DATA)
91 |
92 | if log_item == None:
93 | try:
94 | r = server._config_update(server.url + Log_Group._create_url(log_group) + _create_url(log_item_data['common.ALLTYPES_NAME']), log_item_data)
95 | if r.code == 200: return True
96 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
97 | except KeyError as err:
98 | err_msg ='Error: No log item identified in DATA | Key Error: {}'.format(err)
99 | raise KepError(err_msg)
100 | else:
101 | r = server._config_update(server.url + Log_Group._create_url(log_group) + _create_url(log_item), log_item_data)
102 | if r.code == 200: return True
103 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
104 |
105 | def get_log_item(server, log_group, log_item) -> dict:
106 | '''Returns the properties of the `"log item"` object.
107 |
108 | :param server: instance of the `server` class
109 | :param log_group: name of log group that log item exists
110 | :param log_item: name of log item to retrieve
111 |
112 | :return: Dict of properties for the log group requested
113 |
114 | :raises KepHTTPError: If urllib provides an HTTPError
115 | :raises KepURLError: If urllib provides an URLError
116 | '''
117 | r = server._config_get(server.url + Log_Group._create_url(log_group) + _create_url(log_item))
118 | return r.payload
119 |
120 | def get_all_log_items(server: server, log_group: str, *, options: dict = None) -> list:
121 | '''Returns the properties of all `"log item"` objects for a log group.
122 |
123 | :param server: instance of the `server` class
124 | :param log_group: name of log group that log item exists
125 | :param options: *(optional)* Dict of parameters to filter, sort or pagenate the list of log groups. Options are 'filter',
126 | 'sortOrder', 'sortProperty', 'pageNumber', and 'pageSize'. Only used when exchange_name is not defined.
127 |
128 | :return: list of properties for all log items in the log group requested
129 |
130 | :raises KepHTTPError: If urllib provides an HTTPError
131 | :raises KepURLError: If urllib provides an URLError
132 | '''
133 | r = server._config_get(f'{server.url}{Log_Group._create_url(log_group)}{_create_url()}', params= options)
134 | return r.payload
135 |
--------------------------------------------------------------------------------
/kepconfig/connectivity/egd/name.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 |
8 | r"""`names` exposes an API to allow modifications (add, delete, modify) to
9 | name resolution objects for EGD devices within the Kepware Configuration API
10 | """
11 |
12 | from ...utils import _url_parse_object, path_split
13 | from ...connection import server
14 | from ...error import KepHTTPError, KepError
15 | from typing import Union
16 | from .. import channel, device
17 |
18 | NAMES_ROOT = '/name_resolution_groups/Name Resolutions/name_resolutions'
19 |
20 | def _create_url(device_path, name = None):
21 | '''Creates url object for the "name resolution" branch of Kepware's project
22 | tree. Used to build a part of Kepware Configuration API URL structure
23 |
24 | Returns the name resolution specific url when a value is passed.
25 | '''
26 | path_obj = path_split(device_path)
27 | device_root = channel._create_url(path_obj['channel']) + device._create_url(path_obj['device'])
28 |
29 | if name == None:
30 | return '{}/{}'.format(device_root, NAMES_ROOT)
31 | else:
32 | return '{}/{}/{}'.format(device_root, NAMES_ROOT, _url_parse_object(name))
33 |
34 | def add_name_resolution(server: server, device_path: str, DATA: Union[dict, list]) -> Union[bool, list]:
35 | '''Add a `"name resolution"` or multiple `"name resolution"` objects to Kepware. This allows you to
36 | create a name resolution or multiple name resolutions all in one function, if desired.
37 |
38 | :param server: instance of the `server` class
39 | :param device_path: path to EGD device. Standard Kepware address decimal
40 | notation string such as `"channel1.device1"`
41 | :param DATA: Dict or List of Dicts of name resolutions
42 | expected by Kepware Configuration API
43 |
44 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
45 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
46 | name resolutions added that failed.
47 |
48 | :raises KepHTTPError: If urllib provides an HTTPError
49 | :raises KepURLError: If urllib provides an URLError
50 | '''
51 |
52 | r = server._config_add(server.url + _create_url(device_path), DATA)
53 | if r.code == 201: return True
54 | elif r.code == 207:
55 | errors = []
56 | for item in r.payload:
57 | if item['code'] != 201:
58 | errors.append(item)
59 | return errors
60 | else:
61 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
62 |
63 | def del_name_resolution(server: server, device_path: str, name: str) -> bool:
64 | '''Delete a `"name resolution"` object in Kepware.
65 |
66 | :param server: instance of the `server` class
67 | :param device_path: path to EGD device. Standard Kepware address decimal
68 | notation string such as `"channel1.device1"`
69 | :param name: name of name resolution to delete
70 |
71 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
72 |
73 | :raises KepHTTPError: If urllib provides an HTTPError
74 | :raises KepURLError: If urllib provides an URLError
75 | '''
76 |
77 | r = server._config_del(server.url + _create_url(device_path, name))
78 | if r.code == 200: return True
79 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
80 |
81 | def modify_name_resolution(server: server, device_path: str, DATA: dict, *, name: str = None, force: bool = False) -> bool:
82 | '''Modify a `"name resolution"` object and it's properties in Kepware. If a `"name"` is not provided as an input,
83 | you need to identify the name resolution in the *'common.ALLTYPES_NAME'* property field in the `"DATA"`. It will
84 | assume that is the name resolution that is to be modified.
85 |
86 | :param server: instance of the `server` class
87 | :param device_path: path to EGD device. Standard Kepware address decimal
88 | notation string such as `"channel1.device1"`
89 | :param DATA: Dict of name resolution properties to be modified
90 | :param name: *(optional)* name of name resolution to modify. Only needed if not existing in `"DATA"`
91 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
92 |
93 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
94 |
95 | :raises KepHTTPError: If urllib provides an HTTPError
96 | :raises KepURLError: If urllib provides an URLError
97 | '''
98 |
99 | name_data = server._force_update_check(force, DATA)
100 | if name == None:
101 | try:
102 | r = server._config_update(server.url + _create_url(device_path, name_data['common.ALLTYPES_NAME']), name_data)
103 | if r.code == 200: return True
104 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
105 | except KeyError as err:
106 | err_msg = f'Error: No name resolution identified in DATA | Key Error: {type(DATA)}'
107 | raise KepError(err_msg)
108 | else:
109 | r = server._config_update(server.url + _create_url(device_path, name), name_data)
110 | if r.code == 200: return True
111 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
112 |
113 | def get_name_resolution(server: server, device_path: str, name: str = None, *, options: dict = None) -> Union[dict, list]:
114 | '''Returns the properties of the `"name resolution"` object or a list of all name resolutions.
115 |
116 | :param server: instance of the `server` class
117 | :param device_path: path to EGD device. Standard Kepware address decimal
118 | notation string such as `"channel1.device1"`
119 | :param DATA: Dict of name resolution properties to be modified
120 | :param name: *(optional)* name of name resolution to retrieve. If not defined, will get all name resolutions
121 | :param options: *(optional)* Dict of parameters to filter, sort or pagenate when getting a list of profiles. Options are `filter`,
122 | `sortOrder`, `sortProperty`, `pageNumber`, and `pageSize`. Only used when `"name"` is not defined.
123 |
124 | :return: Dict of the name resolution properties or List of Dicts for all name resolutions and their properties
125 |
126 | :raises KepHTTPError: If urllib provides an HTTPError
127 | :raises KepURLError: If urllib provides an URLError
128 | '''
129 |
130 | if name == None:
131 | r = server._config_get(f'{server.url}{_create_url(device_path)}', params= options)
132 | else:
133 | r = server._config_get(f'{server.url}{_create_url(device_path, name)}')
134 | return r.payload
--------------------------------------------------------------------------------
/kepconfig/adv_tags/link_tags.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # Note: The code within this file was created in total or in part
8 | # with the use of AI tools.
9 |
10 | r"""`link_tags` exposes an API to allow modifications (add, delete, modify) to
11 | link tag objects within the Kepware Configuration API
12 | """
13 |
14 | from ..connection import server
15 | from ..error import KepError, KepHTTPError
16 | from ..utils import _url_parse_object
17 | from typing import Union
18 | from .. import adv_tags
19 |
20 | LINK_TAGS_ROOT = '/link_tags'
21 |
22 | def _get_link_tags_url(tag: str = None) -> str:
23 | '''Creates url object for the "link_tags" branch of Kepware's project tree.
24 |
25 | Returns the link tag specific url when a value is passed as the tag name.
26 | '''
27 | if tag is None:
28 | return LINK_TAGS_ROOT
29 | else:
30 | return f'{LINK_TAGS_ROOT}/{_url_parse_object(tag)}'
31 |
32 | def add_link_tag(server: server, adv_tag_group_path: str, DATA: Union[dict, list]) -> Union[bool, list]:
33 | '''Add `"link_tag"` or multiple `"link_tag"` objects to a specific path in Kepware.
34 | Can be used to pass a list of link tags to be added at one path location.
35 |
36 | :param server: instance of the `server` class
37 | :param adv_tag_group_path: path identifying where to add link tag(s). Standard Kepware address decimal
38 | notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
39 | :param DATA: Dict or List of Dicts of the link tag(s) to add
40 |
41 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
42 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
43 | link tags added that failed.
44 |
45 | :raises KepHTTPError: If urllib provides an HTTPError
46 | :raises KepURLError: If urllib provides an URLError
47 | '''
48 | path_obj = adv_tags._adv_tag_path_split(adv_tag_group_path, isItem=False)
49 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_link_tags_url()
50 |
51 | r = server._config_add(url, DATA)
52 | if r.code == 201:
53 | return True
54 | elif r.code == 207:
55 | errors = [item for item in r.payload if item['code'] != 201]
56 | return errors
57 | else:
58 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
59 |
60 | def modify_link_tag(server: server, link_tag_path: str, DATA: dict, force: bool = False) -> bool:
61 | '''Modify a `"link_tag"` object and its properties in Kepware.
62 |
63 | :param server: instance of the `server` class
64 | :param link_tag_path: path identifying location and link tag to modify. Standard Kepware address decimal
65 | notation string including the link tag such as "_advancedtags.AdvTagGroup1.LinkTag1"
66 | :param DATA: Dict of the `link_tag` properties to be modified
67 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
68 |
69 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
70 |
71 | :raises KepHTTPError: If urllib provides an HTTPError
72 | :raises KepURLError: If urllib provides an URLError
73 | '''
74 | link_tag_data = server._force_update_check(force, DATA)
75 | path_obj = adv_tags._adv_tag_path_split(link_tag_path, isItem=True)
76 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_link_tags_url(path_obj['item'])
77 |
78 | r = server._config_update(url, link_tag_data)
79 | if r.code == 200:
80 | return True
81 | else:
82 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
83 |
84 | def del_link_tag(server: server, link_tag_path: str) -> bool:
85 | '''Delete `"link_tag"` object at a specific path in Kepware.
86 |
87 | :param server: instance of the `server` class
88 | :param link_tag_path: path identifying location and link tag to delete. Standard Kepware address decimal
89 | notation string including the link tag such as "_advancedtags.AdvTagGroup1.LinkTag1"
90 |
91 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
92 |
93 | :raises KepHTTPError: If urllib provides an HTTPError
94 | :raises KepURLError: If urllib provides an URLError
95 | '''
96 | path_obj = adv_tags._adv_tag_path_split(link_tag_path, isItem=True)
97 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_link_tags_url(path_obj['item'])
98 |
99 | r = server._config_del(url)
100 | if r.code == 200:
101 | return True
102 | else:
103 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
104 |
105 | def get_link_tag(server: server, link_tag_path: str) -> dict:
106 | '''Returns the properties of the `"link_tag"` object at a specific path in Kepware.
107 |
108 | :param server: instance of the `server` class
109 | :param link_tag_path: path identifying location and link tag to retrieve. Standard Kepware address decimal
110 | notation string including the link tag such as "_advancedtags.AdvTagGroup1.LinkTag1"
111 |
112 | :return: Dict of data for the link tag requested
113 |
114 | :raises KepHTTPError: If urllib provides an HTTPError
115 | :raises KepURLError: If urllib provides an URLError
116 | '''
117 | path_obj = adv_tags._adv_tag_path_split(link_tag_path, isItem=True)
118 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_link_tags_url(path_obj['item'])
119 |
120 | r = server._config_get(url)
121 | return r.payload
122 |
123 | def get_all_link_tags(server: server, adv_tag_group_path: str, *, options: dict = None) -> list:
124 | '''Returns the properties of all `"link_tag"` objects at a specific path in Kepware.
125 |
126 | :param server: instance of the `server` class
127 | :param adv_tag_group_path: path identifying location to retrieve link tag list. Standard Kepware address decimal
128 | notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
129 | :param options: *(optional)* Dict of parameters to filter, sort or paginate the list of link tags. Options are `filter`,
130 | `sortOrder`, `sortProperty`, `pageNumber`, and `pageSize`
131 |
132 | :return: List of data for all link tags
133 |
134 | :raises KepHTTPError: If urllib provides an HTTPError
135 | :raises KepURLError: If urllib provides an URLError
136 | '''
137 | path_obj = adv_tags._adv_tag_path_split(adv_tag_group_path, isItem=False)
138 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_link_tags_url()
139 |
140 | r = server._config_get(url, params=options)
141 | return r.payload
--------------------------------------------------------------------------------
/examples/user management example.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # User Management Example - Simple example on how to manage a connection and
8 | # execute various calls for configuring the user and user group properties of the Kepware
9 | # Configuration API
10 |
11 | from kepconfig import connection, error
12 | from kepconfig.admin import user_groups, users
13 |
14 | # User Groups
15 | group1 = {
16 | 'common.ALLTYPES_NAME': 'Operators',
17 | "libadminsettings.USERMANAGER_GROUP_ENABLED": True,
18 | "libadminsettings.USERMANAGER_IO_TAG_READ": True,
19 | "libadminsettings.USERMANAGER_IO_TAG_WRITE": True,
20 | "libadminsettings.USERMANAGER_IO_TAG_DYNAMIC_ADDRESSING": True,
21 | "libadminsettings.USERMANAGER_SYSTEM_TAG_READ": True,
22 | "libadminsettings.USERMANAGER_SYSTEM_TAG_WRITE": True,
23 | "libadminsettings.USERMANAGER_INTERNAL_TAG_READ": True,
24 | "libadminsettings.USERMANAGER_INTERNAL_TAG_WRITE": True,
25 | "libadminsettings.USERMANAGER_SERVER_MANAGE_LICENSES": True,
26 | "libadminsettings.USERMANAGER_SERVER_RESET_OPC_DIAGS_LOG": True,
27 | "libadminsettings.USERMANAGER_SERVER_RESET_COMM_DIAGS_LOG": True,
28 | "libadminsettings.USERMANAGER_SERVER_MODIFY_SERVER_SETTINGS": True,
29 | "libadminsettings.USERMANAGER_SERVER_DISCONNECT_CLIENTS": True,
30 | "libadminsettings.USERMANAGER_SERVER_RESET_EVENT_LOG": True,
31 | "libadminsettings.USERMANAGER_SERVER_OPCUA_DOTNET_CONFIGURATION": True,
32 | "libadminsettings.USERMANAGER_SERVER_CONFIG_API_LOG_ACCESS": True,
33 | "libadminsettings.USERMANAGER_SERVER_REPLACE_RUNTIME_PROJECT": True,
34 | "libadminsettings.USERMANAGER_BROWSE_BROWSENAMESPACE": True
35 | }
36 | group2 = {
37 | 'common.ALLTYPES_NAME': 'UA Users',
38 | "libadminsettings.USERMANAGER_GROUP_ENABLED": True,
39 | "libadminsettings.USERMANAGER_IO_TAG_READ": True,
40 | "libadminsettings.USERMANAGER_IO_TAG_WRITE": True,
41 | "libadminsettings.USERMANAGER_IO_TAG_DYNAMIC_ADDRESSING": True,
42 | "libadminsettings.USERMANAGER_SYSTEM_TAG_READ": True,
43 | "libadminsettings.USERMANAGER_SYSTEM_TAG_WRITE": True,
44 | "libadminsettings.USERMANAGER_INTERNAL_TAG_READ": True,
45 | "libadminsettings.USERMANAGER_INTERNAL_TAG_WRITE": True,
46 | "libadminsettings.USERMANAGER_SERVER_MANAGE_LICENSES": False,
47 | "libadminsettings.USERMANAGER_SERVER_RESET_OPC_DIAGS_LOG": False,
48 | "libadminsettings.USERMANAGER_SERVER_RESET_COMM_DIAGS_LOG": False,
49 | "libadminsettings.USERMANAGER_SERVER_MODIFY_SERVER_SETTINGS": True,
50 | "libadminsettings.USERMANAGER_SERVER_DISCONNECT_CLIENTS": False,
51 | "libadminsettings.USERMANAGER_SERVER_RESET_EVENT_LOG": False,
52 | "libadminsettings.USERMANAGER_SERVER_OPCUA_DOTNET_CONFIGURATION": False,
53 | "libadminsettings.USERMANAGER_SERVER_CONFIG_API_LOG_ACCESS": False,
54 | "libadminsettings.USERMANAGER_SERVER_REPLACE_RUNTIME_PROJECT": False,
55 | "libadminsettings.USERMANAGER_BROWSE_BROWSENAMESPACE": True
56 | }
57 |
58 | # Users
59 | user1 = {
60 | "common.ALLTYPES_NAME": "Client1",
61 | "libadminsettings.USERMANAGER_USER_GROUPNAME": "Operators",
62 | "libadminsettings.USERMANAGER_USER_ENABLED": True,
63 | "libadminsettings.USERMANAGER_USER_PASSWORD": "Password123456"
64 | }
65 | user2 = {
66 | "common.ALLTYPES_NAME": "Client2",
67 | "libadminsettings.USERMANAGER_USER_GROUPNAME": "UA Users",
68 | "libadminsettings.USERMANAGER_USER_ENABLED": True,
69 | "libadminsettings.USERMANAGER_USER_PASSWORD": "Password123456"
70 | }
71 |
72 | def ErrorHandler(err):
73 | # Generic Handler for exception errors
74 | if isinstance(err, error.KepHTTPError):
75 | print(err.code)
76 | print(err.msg)
77 | print(err.url)
78 | print(err.hdrs)
79 | print(err.payload)
80 | elif isinstance(err, error.KepURLError):
81 | print(err.url)
82 | print(err.reason)
83 | elif isinstance(err, error.KepError):
84 | print(err.msg)
85 | else:
86 | print('Different Exception Received: {}'.format(err))
87 |
88 | # This creates a server reference that is used to target all modifications of
89 | # the Kepware configuration
90 | server = connection.server(host = '127.0.0.1', port = 57412, user = 'Administrator', pw = '')
91 |
92 | # ---------------------------------------------
93 | # User Group Methods
94 | # ---------------------------------------------
95 |
96 | # Add the User Groups with the appropriate parameters
97 | try:
98 | print("{} - {}".format("Add new User Groups", user_groups.add_user_group(server, [group1, group2])))
99 | except Exception as err:
100 | ErrorHandler(err)
101 |
102 | # Modify permissions on a User Group
103 | # Ex: Prevent Write access for user group
104 |
105 | modify_group = {
106 | "libadminsettings.USERMANAGER_IO_TAG_WRITE": False,
107 | "libadminsettings.USERMANAGER_SYSTEM_TAG_WRITE": False,
108 | "libadminsettings.USERMANAGER_INTERNAL_TAG_WRITE": False
109 | }
110 |
111 | try:
112 | print("{} - {}".format("Modify User Group properties to prevent 'Writes'",user_groups.modify_user_group(server, modify_group, user_group= group1['common.ALLTYPES_NAME'])))
113 | except Exception as err:
114 | ErrorHandler(err)
115 |
116 | # Disable and Enable a user groups
117 | try:
118 | print("{} - {}".format("Disable User Group",user_groups.disable_user_group(server, group1['common.ALLTYPES_NAME'])))
119 | except Exception as err:
120 | ErrorHandler(err)
121 |
122 | try:
123 | print("{} - {}".format("Enable User Group",user_groups.enable_user_group(server, group1['common.ALLTYPES_NAME'])))
124 | except Exception as err:
125 | ErrorHandler(err)
126 |
127 | # ---------------------------------------------
128 | # User Methods
129 | # ---------------------------------------------
130 |
131 | # Add new users with the appropriate parameters
132 | try:
133 | print("{} - {}".format("Add new Users", users.add_user(server, [user1, user2])))
134 | except Exception as err:
135 | ErrorHandler(err)
136 |
137 | # Modify new user parameters - New Password
138 | modify_pass = {
139 | "libadminsettings.USERMANAGER_USER_PASSWORD": "NewPassword123"
140 | }
141 |
142 | try:
143 | print("{} - {}".format("Updated a user password", users.modify_user(server,modify_pass, user= user1['common.ALLTYPES_NAME'])))
144 | except Exception as err:
145 | ErrorHandler(err)
146 |
147 | # Disable and Enable a user
148 | try:
149 | print("{} - {}".format("Disable a user", users.disable_user(server, user1['common.ALLTYPES_NAME'])))
150 | except Exception as err:
151 | ErrorHandler(err)
152 | try:
153 | print("{} - {}".format("Enable a user", users.enable_user(server, user1['common.ALLTYPES_NAME'])))
154 | except Exception as err:
155 | ErrorHandler(err)
--------------------------------------------------------------------------------
/kepconfig/datalogger/triggers.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`triggers` exposes an API to allow modifications (add, delete, modify) to
8 | trigger objects in a Datalogger log group within the Kepware Configuration API
9 | """
10 |
11 | from typing import Union
12 | from . import log_group as Log_Group
13 | from ..error import KepError, KepHTTPError
14 | from ..connection import server
15 | from ..utils import _url_parse_object
16 |
17 | TRIGGERS_ROOT = '/triggers'
18 |
19 | def _create_url(trigger = None):
20 | '''Creates url object for the "trigger" branch of Kepware's project tree. Used
21 | to build a part of Kepware Configuration API URL structure
22 |
23 | Returns the trigger specific url when a value is passed as the trigger name.
24 | '''
25 |
26 | if trigger == None:
27 | return '{}'.format(TRIGGERS_ROOT)
28 | else:
29 | return '{}/{}'.format(TRIGGERS_ROOT, _url_parse_object(trigger))
30 |
31 |
32 | def add_trigger(server: server, log_group: str, DATA: Union[dict, list]) -> Union[bool, list]:
33 | '''Add a `"trigger"` or multiple `"trigger"` objects to a log group in Kepware's Datalogger. It can
34 | be used to pass a list of triggers to be added all at once.
35 |
36 | :param server: instance of the `server` class
37 | :param log_group: name of log group for the trigger items
38 | :param DATA: Dict or a list of the trigger items to add through Kepware Configuration API
39 |
40 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
41 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
42 | triggers added that failed.
43 |
44 | :raises KepHTTPError: If urllib provides an HTTPError
45 | :raises KepURLError: If urllib provides an URLError
46 | '''
47 |
48 | r = server._config_add(server.url + Log_Group._create_url(log_group) + _create_url(), DATA)
49 | if r.code == 201: return True
50 | elif r.code == 207:
51 | errors = []
52 | for item in r.payload:
53 | if item['code'] != 201:
54 | errors.append(item)
55 | return errors
56 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
57 |
58 | def del_trigger(server: server, log_group: str, trigger: str) -> bool:
59 | '''Delete a `"trigger"` object of a log group in Kepware's Datalogger.
60 |
61 | :param server: instance of the `server` class
62 | :param log_group: name of log group for the trigger items
63 | :param trigger: name of trigger to delete
64 |
65 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
66 |
67 | :raises KepHTTPError: If urllib provides an HTTPError
68 | :raises KepURLError: If urllib provides an URLError
69 | '''
70 | r = server._config_del(server.url + Log_Group._create_url(log_group) + _create_url(trigger))
71 | if r.code == 200: return True
72 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
73 |
74 | def modify_trigger(server: server, log_group: str, DATA: dict, *, trigger: str = None, force: bool = False) -> bool:
75 | '''Modify a `"trigger"` object and it's properties in Kepware. If a `"trigger"` is not provided as an input,
76 | you need to identify the trigger in the *'common.ALLTYPES_NAME'* property field in the `"DATA"`. It will
77 | assume that is the trigger that is to be modified.
78 |
79 | :param server: instance of the `server` class
80 | :param log_group: name of log group for the trigger items
81 | :param DATA: Dict of the trigger properties to be modified.
82 | :param trigger: *(optional)* name of trigger to modify in the log group. Only needed if not existing in `"DATA"`
83 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
84 |
85 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
86 |
87 | :raises KepHTTPError: If urllib provides an HTTPError
88 | :raises KepURLError: If urllib provides an URLError
89 | '''
90 |
91 | trigger_data = server._force_update_check(force, DATA)
92 |
93 | if trigger == None:
94 | try:
95 | r = server._config_update(server.url + Log_Group._create_url(log_group) + _create_url(trigger_data['common.ALLTYPES_NAME']), trigger_data)
96 | if r.code == 200: return True
97 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
98 | except KeyError as err:
99 | err_msg = 'Error: No trigger identified in DATA | Key Error: {}'.format(err)
100 | raise KepError(err_msg)
101 | # except:
102 | # return 'Error: Error with {}'.format(inspect.currentframe().f_code.co_name)
103 | else:
104 | r = server._config_update(server.url + Log_Group._create_url(log_group) + _create_url(trigger), trigger_data)
105 | if r.code == 200: return True
106 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
107 |
108 | def get_trigger(server, log_group, trigger) -> dict:
109 | '''Returns the properties of the `"trigger"` object.
110 |
111 | :param server: instance of the `server` class
112 | :param log_group: name of log group for the trigger items
113 | :param trigger: name of trigger to retrieve
114 |
115 | :return: Dict of properties for the trigger requested
116 |
117 | :raises KepHTTPError: If urllib provides an HTTPError
118 | :raises KepURLError: If urllib provides an URLError
119 | '''
120 | r = server._config_get(server.url + Log_Group._create_url(log_group) + _create_url(trigger))
121 | return r.payload
122 |
123 | def get_all_triggers(server: server, log_group: str, *, options: dict = None) -> list:
124 | '''Returns the properties of all `"trigger"` objects for a log group.
125 |
126 | :param server: instance of the `server` class
127 | :param log_group: name of log group for the trigger items
128 |
129 | :return: Dict of properties for the trigger requested
130 |
131 | :raises KepHTTPError: If urllib provides an HTTPError
132 | :raises KepURLError: If urllib provides an URLError
133 | :param options: *(optional)* Dict of parameters to filter, sort or pagenate the list of triggers. Options are 'filter',
134 | 'sortOrder', 'sortProperty', 'pageNumber', and 'pageSize'. Only used when exchange_name is not defined.
135 |
136 | :return: list of properties for all triggers in the log group requested
137 |
138 | :raises KepHTTPError: If urllib provides an HTTPError
139 | :raises KepURLError: If urllib provides an URLError
140 | '''
141 | r = server._config_get(f'{server.url}{Log_Group._create_url(log_group)}{_create_url()}', params= options)
142 | return r.payload
143 |
--------------------------------------------------------------------------------
/kepconfig/admin/user_groups.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c), PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`user_groups` exposes an API to allow modifications (add, delete, modify) to
8 | user groups within the Kepware Administration User Manager through the Kepware Configuration API
9 | """
10 | from typing import Union
11 | from ..error import KepHTTPError, KepError
12 | from ..connection import server
13 | from ..utils import _url_parse_object
14 |
15 |
16 | USERGROUPS_ROOT = '/admin/server_usergroups'
17 | ENABLE_PROPERTY = 'libadminsettings.USERMANAGER_GROUP_ENABLED'
18 |
19 | def _create_url(user_group = None):
20 | '''Creates url object for the "server_usergroups" branch of Kepware's project tree. Used
21 | to build a part of Kepware Configuration API URL structure
22 |
23 | Returns the user group specific url when a value is passed as the user_group name.
24 | '''
25 |
26 | if user_group == None:
27 | return USERGROUPS_ROOT
28 | else:
29 | return '{}/{}'.format(USERGROUPS_ROOT, _url_parse_object(user_group))
30 |
31 | def add_user_group(server: server, DATA: Union[dict, list]) -> Union[bool, list]:
32 | '''Add a `"user group"` or multiple `"user group"` objects to Kepware User Manager by passing a
33 | list of user groups to be added all at once.
34 |
35 | :param server: instance of the `server` class
36 | :param DATA: Dict or List of Dicts of the user groups to add
37 |
38 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
39 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
40 | endpoints added that failed.
41 |
42 | :raises KepHTTPError: If urllib provides an HTTPError
43 | :raises KepURLError: If urllib provides an URLError
44 | '''
45 |
46 | r = server._config_add(server.url + _create_url(), DATA)
47 | if r.code == 201: return True
48 | elif r.code == 207:
49 | errors = []
50 | for item in r.payload:
51 | if item['code'] != 201:
52 | errors.append(item)
53 | return errors
54 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
55 |
56 | def del_user_group(server: server, user_group: str) -> bool:
57 | '''Delete a `"user group"` object in Kepware User Manager
58 |
59 | :param server: instance of the `server` class
60 | :param user_group: name of user group to delete
61 |
62 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
63 |
64 | :raises KepHTTPError: If urllib provides an HTTPError
65 | :raises KepURLError: If urllib provides an URLError
66 | '''
67 |
68 | r = server._config_del(server.url + _create_url(user_group))
69 | if r.code == 200: return True
70 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
71 |
72 | def modify_user_group(server: server, DATA: dict, *, user_group: str = None) -> bool:
73 | '''Modify a `"user group"` object and it's properties in Kepware User Manager. If a `"user group"` is not provided as an input,
74 | you need to identify the user group in the *'common.ALLTYPES_NAME'* property field in the `"DATA"`. It will
75 | assume that is the user group that is to be modified.
76 |
77 | :param server: instance of the `server` class
78 | :param DATA: Dict of the user group properties to be modified.
79 | :param user_group: *(optional)* name of user group to modify. Only needed if not existing in `"DATA"`
80 |
81 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
82 |
83 | :raises KepHTTPError: If urllib provides an HTTPError
84 | :raises KepURLError: If urllib provides an URLError
85 | '''
86 |
87 | if user_group == None:
88 | try:
89 | r = server._config_update(server.url + _create_url(DATA['common.ALLTYPES_NAME']), DATA)
90 | if r.code == 200: return True
91 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
92 | except KeyError as err:
93 | err_msg = 'Error: No User Group identified in DATA | Key Error: {}'.format(err)
94 | raise KepError(err_msg)
95 | else:
96 | r = server._config_update(server.url + _create_url(user_group), DATA)
97 | if r.code == 200: return True
98 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
99 |
100 | def get_user_group(server: server, user_group: str) -> dict:
101 | '''Returns the properties of the `"user group"` object.
102 |
103 | :param server: instance of the `server` class
104 | :param user_group: name of user group to retrieve
105 |
106 | :return: Dict of properties for the user group requested
107 |
108 | :raises KepHTTPError: If urllib provides an HTTPError
109 | :raises KepURLError: If urllib provides an URLError
110 | '''
111 |
112 | r = server._config_get(server.url + _create_url(user_group))
113 | return r.payload
114 |
115 | def get_all_user_groups(server: server, *, options: dict = None) -> list:
116 | '''Returns list of all `"user group"` objects and their properties.
117 |
118 | :param server: instance of the `server` class
119 | :param options: *(optional)* Dict of parameters to filter, sort or pagenate the list of user groups. Options are 'filter',
120 | 'sortOrder', 'sortProperty', 'pageNumber', and 'pageSize.
121 |
122 | :return: List of properties for all user groups
123 |
124 | :raises KepHTTPError: If urllib provides an HTTPError
125 | :raises KepURLError: If urllib provides an URLError
126 | '''
127 |
128 | r = server._config_get(f'{server.url}{_create_url()}', params= options)
129 | return r.payload
130 |
131 | def enable_user_group(server: server, user_group: str) -> bool:
132 | '''Enable the `"user group"`.
133 |
134 | :param server: instance of the `server` class
135 | :param user_group: name of user group
136 |
137 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
138 |
139 | :raises KepHTTPError: If urllib provides an HTTPError
140 | :raises KepURLError: If urllib provides an URLError
141 | '''
142 | DATA = {ENABLE_PROPERTY: True}
143 | return modify_user_group(server, DATA, user_group= user_group)
144 |
145 | def disable_user_group(server: server, user_group: str) -> bool:
146 | '''Disable the `"user group"`.
147 |
148 | :param server: instance of the `server` class
149 | :param user_group: name of user group
150 |
151 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
152 |
153 | :raises KepHTTPError: If urllib provides an HTTPError
154 | :raises KepURLError: If urllib provides an URLError
155 | '''
156 | DATA = {ENABLE_PROPERTY: False}
157 | return modify_user_group(server, DATA, user_group= user_group)
--------------------------------------------------------------------------------
/kepconfig/adv_tags/max_tags.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # Note: The code within this file was created in total or in part
8 | # with the use of AI tools.
9 |
10 | r"""`maximum_tags` exposes an API to allow modifications (add, delete, modify) to
11 | maximum tag objects within the Kepware Configuration API
12 | """
13 |
14 | from ..connection import server
15 | from ..error import KepError, KepHTTPError
16 | from ..utils import _url_parse_object
17 | from typing import Union
18 | from .. import adv_tags
19 |
20 | MAXIMUM_TAGS_ROOT = '/maximum_tags'
21 |
22 | def _get_maximum_tags_url(tag: str = None) -> str:
23 | '''Creates url object for the "maximum_tags" branch of Kepware's project tree.
24 |
25 | Returns the maximum tag specific url when a value is passed as the tag name.
26 | '''
27 | if tag is None:
28 | return MAXIMUM_TAGS_ROOT
29 | else:
30 | return f'{MAXIMUM_TAGS_ROOT}/{_url_parse_object(tag)}'
31 |
32 | def add_maximum_tag(server: server, adv_tag_group_path: str, DATA: Union[dict, list]) -> Union[bool, list]:
33 | '''Add `"maximum_tag"` or multiple `"maximum_tag"` objects to a specific path in Kepware.
34 | Can be used to pass a list of maximum tags to be added at one path location.
35 |
36 | :param server: instance of the `server` class
37 | :param adv_tag_group_path: path identifying where to add maximum tag(s). Standard Kepware address decimal
38 | notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
39 | :param DATA: Dict or List of Dicts of the maximum tag(s) to add
40 |
41 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
42 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
43 | maximum tags added that failed.
44 |
45 | :raises KepHTTPError: If urllib provides an HTTPError
46 | :raises KepURLError: If urllib provides an URLError
47 | '''
48 | path_obj = adv_tags._adv_tag_path_split(adv_tag_group_path, isItem=False)
49 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_maximum_tags_url()
50 |
51 | r = server._config_add(url, DATA)
52 | if r.code == 201:
53 | return True
54 | elif r.code == 207:
55 | errors = [item for item in r.payload if item['code'] != 201]
56 | return errors
57 | else:
58 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
59 |
60 | def modify_maximum_tag(server: server, max_tag_path: str, DATA: dict, force: bool = False) -> bool:
61 | '''Modify a `"maximum_tag"` object and its properties in Kepware.
62 |
63 | :param server: instance of the `server` class
64 | :param max_tag_path: path identifying location and maximum tag to modify. Standard Kepware address decimal
65 | notation string including the maximum tag such as "_advancedtags.AdvTagGroup1.MaxTag1"
66 | :param DATA: Dict of the `maximum_tag` properties to be modified
67 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
68 |
69 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
70 |
71 | :raises KepHTTPError: If urllib provides an HTTPError
72 | :raises KepURLError: If urllib provides an URLError
73 | '''
74 | max_tag_data = server._force_update_check(force, DATA)
75 | path_obj = adv_tags._adv_tag_path_split(max_tag_path, isItem=True)
76 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_maximum_tags_url(path_obj['item'])
77 |
78 | r = server._config_update(url, max_tag_data)
79 | if r.code == 200:
80 | return True
81 | else:
82 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
83 |
84 | def del_maximum_tag(server: server, max_tag_path: str) -> bool:
85 | '''Delete `"maximum_tag"` object at a specific path in Kepware.
86 |
87 | :param server: instance of the `server` class
88 | :param max_tag_path: path identifying location and maximum tag to delete. Standard Kepware address decimal
89 | notation string including the maximum tag such as "_advancedtags.AdvTagGroup1.MaxTag1"
90 |
91 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
92 |
93 | :raises KepHTTPError: If urllib provides an HTTPError
94 | :raises KepURLError: If urllib provides an URLError
95 | '''
96 | path_obj = adv_tags._adv_tag_path_split(max_tag_path, isItem=True)
97 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_maximum_tags_url(path_obj['item'])
98 |
99 | r = server._config_del(url)
100 | if r.code == 200:
101 | return True
102 | else:
103 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
104 |
105 | def get_maximum_tag(server: server, max_tag_path: str) -> dict:
106 | '''Returns the properties of the `"maximum_tag"` object at a specific path in Kepware.
107 |
108 | :param server: instance of the `server` class
109 | :param max_tag_path: path identifying location and maximum tag to retrieve. Standard Kepware address decimal
110 | notation string including the maximum tag such as "_advancedtags.AdvTagGroup1.MaxTag1"
111 |
112 | :return: Dict of data for the maximum tag requested
113 |
114 | :raises KepHTTPError: If urllib provides an HTTPError
115 | :raises KepURLError: If urllib provides an URLError
116 | '''
117 | path_obj = adv_tags._adv_tag_path_split(max_tag_path, isItem=True)
118 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_maximum_tags_url(path_obj['item'])
119 |
120 | r = server._config_get(url)
121 | return r.payload
122 |
123 | def get_all_maximum_tags(server: server, adv_tag_group_path: str, *, options: dict = None) -> list:
124 | '''Returns the properties of all `"maximum_tag"` objects at a specific path in Kepware.
125 |
126 | :param server: instance of the `server` class
127 | :param adv_tag_group_path: path identifying location to retrieve maximum tag list. Standard Kepware address decimal
128 | notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
129 | :param options: *(optional)* Dict of parameters to filter, sort or paginate the list of maximum tags. Options are `filter`,
130 | `sortOrder`, `sortProperty`, `pageNumber`, and `pageSize`
131 |
132 | :return: List of data for all maximum tags
133 |
134 | :raises KepHTTPError: If urllib provides an HTTPError
135 | :raises KepURLError: If urllib provides an URLError
136 | '''
137 | path_obj = adv_tags._adv_tag_path_split(adv_tag_group_path, isItem=False)
138 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_maximum_tags_url()
139 |
140 | r = server._config_get(url, params=options)
141 | return r.payload
--------------------------------------------------------------------------------
/kepconfig/adv_tags/min_tags.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # Note: The code within this file was created in total or in part
8 | # with the use of AI tools.
9 |
10 | r"""`minimum_tags` exposes an API to allow modifications (add, delete, modify) to
11 | minimum tag objects within the Kepware Configuration API
12 | """
13 |
14 | from ..connection import server
15 | from ..error import KepError, KepHTTPError
16 | from ..utils import _url_parse_object
17 | from typing import Union
18 | from .. import adv_tags
19 |
20 | MINIMUM_TAGS_ROOT = '/minimum_tags'
21 |
22 | def _get_minimum_tags_url(tag: str = None) -> str:
23 | '''Creates url object for the "minimum_tags" branch of Kepware's project tree.
24 |
25 | Returns the minimum tag specific url when a value is passed as the tag name.
26 | '''
27 | if tag is None:
28 | return MINIMUM_TAGS_ROOT
29 | else:
30 | return f'{MINIMUM_TAGS_ROOT}/{_url_parse_object(tag)}'
31 |
32 | def add_minimum_tag(server: server, adv_tag_group_path: str, DATA: Union[dict, list]) -> Union[bool, list]:
33 | '''Add `"minimum_tag"` or multiple `"minimum_tag"` objects to a specific path in Kepware.
34 | Can be used to pass a list of minimum tags to be added at one path location.
35 |
36 | :param server: instance of the `server` class
37 | :param adv_tag_group_path: path identifying where to add minimum tag(s). Standard Kepware address decimal
38 | notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
39 | :param DATA: Dict or List of Dicts of the minimum tag(s) to add
40 |
41 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
42 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
43 | minimum tags added that failed.
44 |
45 | :raises KepHTTPError: If urllib provides an HTTPError
46 | :raises KepURLError: If urllib provides an URLError
47 | '''
48 | path_obj = adv_tags._adv_tag_path_split(adv_tag_group_path, isItem=False)
49 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_minimum_tags_url()
50 |
51 | r = server._config_add(url, DATA)
52 | if r.code == 201:
53 | return True
54 | elif r.code == 207:
55 | errors = [item for item in r.payload if item['code'] != 201]
56 | return errors
57 | else:
58 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
59 |
60 | def modify_minimum_tag(server: server, min_tag_path: str, DATA: dict, force: bool = False) -> bool:
61 | '''Modify a `"minimum_tag"` object and its properties in Kepware.
62 |
63 | :param server: instance of the `server` class
64 | :param min_tag_path: path identifying location and minimum tag to modify. Standard Kepware address decimal
65 | notation string including the minimum tag such as "_advancedtags.AdvTagGroup1.MinTag1"
66 | :param DATA: Dict of the `minimum_tag` properties to be modified
67 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
68 |
69 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
70 |
71 | :raises KepHTTPError: If urllib provides an HTTPError
72 | :raises KepURLError: If urllib provides an URLError
73 | '''
74 | min_tag_data = server._force_update_check(force, DATA)
75 | path_obj = adv_tags._adv_tag_path_split(min_tag_path, isItem=True)
76 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_minimum_tags_url(path_obj['item'])
77 |
78 | r = server._config_update(url, min_tag_data)
79 | if r.code == 200:
80 | return True
81 | else:
82 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
83 |
84 | def del_minimum_tag(server: server, min_tag_path: str) -> bool:
85 | '''Delete `"minimum_tag"` object at a specific path in Kepware.
86 |
87 | :param server: instance of the `server` class
88 | :param min_tag_path: path identifying location and minimum tag to delete. Standard Kepware address decimal
89 | notation string including the minimum tag such as "_advancedtags.AdvTagGroup1.MinTag1"
90 |
91 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
92 |
93 | :raises KepHTTPError: If urllib provides an HTTPError
94 | :raises KepURLError: If urllib provides an URLError
95 | '''
96 | path_obj = adv_tags._adv_tag_path_split(min_tag_path, isItem=True)
97 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_minimum_tags_url(path_obj['item'])
98 |
99 | r = server._config_del(url)
100 | if r.code == 200:
101 | return True
102 | else:
103 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
104 |
105 | def get_minimum_tag(server: server, min_tag_path: str) -> dict:
106 | '''Returns the properties of the `"minimum_tag"` object at a specific path in Kepware.
107 |
108 | :param server: instance of the `server` class
109 | :param min_tag_path: path identifying location and minimum tag to retrieve. Standard Kepware address decimal
110 | notation string including the minimum tag such as "_advancedtags.AdvTagGroup1.MinTag1"
111 |
112 | :return: Dict of data for the minimum tag requested
113 |
114 | :raises KepHTTPError: If urllib provides an HTTPError
115 | :raises KepURLError: If urllib provides an URLError
116 | '''
117 | path_obj = adv_tags._adv_tag_path_split(min_tag_path, isItem=True)
118 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_minimum_tags_url(path_obj['item'])
119 |
120 | r = server._config_get(url)
121 | return r.payload
122 |
123 | def get_all_minimum_tags(server: server, adv_tag_group_path: str, *, options: dict = None) -> list:
124 | '''Returns the properties of all `"minimum_tag"` objects at a specific path in Kepware.
125 |
126 | :param server: instance of the `server` class
127 | :param adv_tag_group_path: path identifying location to retrieve minimum tag list. Standard Kepware address decimal
128 | notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
129 | :param options: *(optional)* Dict of parameters to filter, sort or paginate the list of minimum tags. Options are `filter`,
130 | `sortOrder`, `sortProperty`, `pageNumber`, and `pageSize`
131 |
132 | :return: List of data for all minimum tags
133 |
134 | :raises KepHTTPError: If urllib provides an HTTPError
135 | :raises KepURLError: If urllib provides an URLError
136 | '''
137 | path_obj = adv_tags._adv_tag_path_split(adv_tag_group_path, isItem=False)
138 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_minimum_tags_url()
139 |
140 | r = server._config_get(url, params=options)
141 | return r.payload
--------------------------------------------------------------------------------
/kepconfig/adv_tags/complex_tags.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # Note: The code within this file was created in total or in part
8 | # with the use of AI tools.
9 |
10 | r"""`complex_tags` exposes an API to allow modifications (add, delete, modify) to
11 | complex tag objects within the Kepware Configuration API
12 | """
13 |
14 | from ..connection import server
15 | from ..error import KepError, KepHTTPError
16 | from ..utils import _url_parse_object
17 | from typing import Union
18 | from .. import adv_tags
19 |
20 | COMPLEX_TAGS_ROOT = '/complex_tags'
21 |
22 | def _get_complex_tags_url(tag: str = None) -> str:
23 | '''Creates url object for the "complex_tags" branch of Kepware's project tree.
24 |
25 | Returns the complex tag specific url when a value is passed as the tag name.
26 | '''
27 | if tag is None:
28 | return COMPLEX_TAGS_ROOT
29 | else:
30 | return f'{COMPLEX_TAGS_ROOT}/{_url_parse_object(tag)}'
31 |
32 | def add_complex_tag(server: server, adv_tag_group_path: str, DATA: Union[dict, list]) -> Union[bool, list]:
33 | '''Add `"complex_tag"` or multiple `"complex_tag"` objects to a specific path in Kepware.
34 | Can be used to pass a list of complex tags to be added at one path location.
35 |
36 | :param server: instance of the `server` class
37 | :param adv_tag_group_path: path identifying where to add complex tag(s). Standard Kepware address decimal
38 | notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
39 | :param DATA: Dict or List of Dicts of the complex tag(s) to add
40 |
41 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
42 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
43 | complex tags added that failed.
44 |
45 | :raises KepHTTPError: If urllib provides an HTTPError
46 | :raises KepURLError: If urllib provides an URLError
47 | '''
48 | path_obj = adv_tags._adv_tag_path_split(adv_tag_group_path, isItem=False)
49 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_complex_tags_url()
50 |
51 | r = server._config_add(url, DATA)
52 | if r.code == 201:
53 | return True
54 | elif r.code == 207:
55 | errors = [item for item in r.payload if item['code'] != 201]
56 | return errors
57 | else:
58 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
59 |
60 | def modify_complex_tag(server: server, complex_tag_path: str, DATA: dict, force: bool = False) -> bool:
61 | '''Modify a `"complex_tag"` object and its properties in Kepware.
62 |
63 | :param server: instance of the `server` class
64 | :param complex_tag_path: path identifying location and complex tag to modify. Standard Kepware address decimal
65 | notation string including the complex tag such as "_advancedtags.AdvTagGroup1.ComplexTag1"
66 | :param DATA: Dict of the `complex_tag` properties to be modified
67 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
68 |
69 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
70 |
71 | :raises KepHTTPError: If urllib provides an HTTPError
72 | :raises KepURLError: If urllib provides an URLError
73 | '''
74 | complex_tag_data = server._force_update_check(force, DATA)
75 | path_obj = adv_tags._adv_tag_path_split(complex_tag_path, isItem=True)
76 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_complex_tags_url(path_obj['item'])
77 |
78 | r = server._config_update(url, complex_tag_data)
79 | if r.code == 200:
80 | return True
81 | else:
82 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
83 |
84 | def del_complex_tag(server: server, complex_tag_path: str) -> bool:
85 | '''Delete `"complex_tag"` object at a specific path in Kepware.
86 |
87 | :param server: instance of the `server` class
88 | :param complex_tag_path: path identifying location and complex tag to delete. Standard Kepware address decimal
89 | notation string including the complex tag such as "_advancedtags.AdvTagGroup1.ComplexTag1"
90 |
91 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
92 |
93 | :raises KepHTTPError: If urllib provides an HTTPError
94 | :raises KepURLError: If urllib provides an URLError
95 | '''
96 | path_obj = adv_tags._adv_tag_path_split(complex_tag_path, isItem=True)
97 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_complex_tags_url(path_obj['item'])
98 |
99 | r = server._config_del(url)
100 | if r.code == 200:
101 | return True
102 | else:
103 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
104 |
105 | def get_complex_tag(server: server, complex_tag_path: str) -> dict:
106 | '''Returns the properties of the `"complex_tag"` object at a specific path in Kepware.
107 |
108 | :param server: instance of the `server` class
109 | :param complex_tag_path: path identifying location and complex tag to retrieve. Standard Kepware address decimal
110 | notation string including the complex tag such as "_advancedtags.AdvTagGroup1.ComplexTag1"
111 |
112 | :return: Dict of data for the complex tag requested
113 |
114 | :raises KepHTTPError: If urllib provides an HTTPError
115 | :raises KepURLError: If urllib provides an URLError
116 | '''
117 | path_obj = adv_tags._adv_tag_path_split(complex_tag_path, isItem=True)
118 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_complex_tags_url(path_obj['item'])
119 |
120 | r = server._config_get(url)
121 | return r.payload
122 |
123 | def get_all_complex_tags(server: server, adv_tag_group_path: str, *, options: dict = None) -> list:
124 | '''Returns the properties of all `"complex_tag"` objects at a specific path in Kepware.
125 |
126 | :param server: instance of the `server` class
127 | :param adv_tag_group_path: path identifying location to retrieve complex tag list. Standard Kepware address decimal
128 | notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
129 | :param options: *(optional)* Dict of parameters to filter, sort or paginate the list of complex tags. Options are `filter`,
130 | `sortOrder`, `sortProperty`, `pageNumber`, and `pageSize`
131 |
132 | :return: List of data for all complex tags
133 |
134 | :raises KepHTTPError: If urllib provides an HTTPError
135 | :raises KepURLError: If urllib provides an URLError
136 | '''
137 | path_obj = adv_tags._adv_tag_path_split(adv_tag_group_path, isItem=False)
138 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_complex_tags_url()
139 |
140 | r = server._config_get(url, params=options)
141 | return r.payload
--------------------------------------------------------------------------------
/kepconfig/adv_tags/derived_tags.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # Note: The code within this file was created in total or in part
8 | # with the use of AI tools.
9 |
10 | r"""`derived_tags` exposes an API to allow modifications (add, delete, modify) to
11 | derived tag objects within the Kepware Configuration API
12 | """
13 |
14 | from ..connection import server
15 | from ..error import KepError, KepHTTPError
16 | from ..utils import _url_parse_object
17 | from typing import Union
18 | from .. import adv_tags
19 |
20 | DERIVED_TAGS_ROOT = '/derived_tags'
21 |
22 | def _get_derived_tags_url(tag: str = None) -> str:
23 | '''Creates url object for the "derived_tags" branch of Kepware's project tree.
24 |
25 | Returns the derived tag specific url when a value is passed as the tag name.
26 | '''
27 | if tag is None:
28 | return DERIVED_TAGS_ROOT
29 | else:
30 | return f'{DERIVED_TAGS_ROOT}/{_url_parse_object(tag)}'
31 |
32 | def add_derived_tag(server: server, adv_tag_group_path: str, DATA: Union[dict, list]) -> Union[bool, list]:
33 | '''Add `"derived_tag"` or multiple `"derived_tag"` objects to a specific path in Kepware.
34 | Can be used to pass a list of derived tags to be added at one path location.
35 |
36 | :param server: instance of the `server` class
37 | :param adv_tag_group_path: path identifying where to add derived tag(s). Standard Kepware address decimal
38 | notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
39 | :param DATA: Dict or List of Dicts of the derived tag(s) to add
40 |
41 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
42 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
43 | derived tags added that failed.
44 |
45 | :raises KepHTTPError: If urllib provides an HTTPError
46 | :raises KepURLError: If urllib provides an URLError
47 | '''
48 | path_obj = adv_tags._adv_tag_path_split(adv_tag_group_path, isItem=False)
49 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_derived_tags_url()
50 |
51 | r = server._config_add(url, DATA)
52 | if r.code == 201:
53 | return True
54 | elif r.code == 207:
55 | errors = [item for item in r.payload if item['code'] != 201]
56 | return errors
57 | else:
58 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
59 |
60 | def modify_derived_tag(server: server, derived_tag_path: str, DATA: dict, force: bool = False) -> bool:
61 | '''Modify a `"derived_tag"` object and its properties in Kepware.
62 |
63 | :param server: instance of the `server` class
64 | :param derived_tag_path: path identifying location and derived tag to modify. Standard Kepware address decimal
65 | notation string including the derived tag such as "_advancedtags.AdvTagGroup1.DerivedTag1"
66 | :param DATA: Dict of the `derived_tag` properties to be modified
67 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
68 |
69 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
70 |
71 | :raises KepHTTPError: If urllib provides an HTTPError
72 | :raises KepURLError: If urllib provides an URLError
73 | '''
74 | derived_tag_data = server._force_update_check(force, DATA)
75 | path_obj = adv_tags._adv_tag_path_split(derived_tag_path, isItem=True)
76 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_derived_tags_url(path_obj['item'])
77 |
78 | r = server._config_update(url, derived_tag_data)
79 | if r.code == 200:
80 | return True
81 | else:
82 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
83 |
84 | def del_derived_tag(server: server, derived_tag_path: str) -> bool:
85 | '''Delete `"derived_tag"` object at a specific path in Kepware.
86 |
87 | :param server: instance of the `server` class
88 | :param derived_tag_path: path identifying location and derived tag to delete. Standard Kepware address decimal
89 | notation string including the derived tag such as "_advancedtags.AdvTagGroup1.DerivedTag1"
90 |
91 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
92 |
93 | :raises KepHTTPError: If urllib provides an HTTPError
94 | :raises KepURLError: If urllib provides an URLError
95 | '''
96 | path_obj = adv_tags._adv_tag_path_split(derived_tag_path, isItem=True)
97 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_derived_tags_url(path_obj['item'])
98 |
99 | r = server._config_del(url)
100 | if r.code == 200:
101 | return True
102 | else:
103 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
104 |
105 | def get_derived_tag(server: server, derived_tag_path: str) -> dict:
106 | '''Returns the properties of the `"derived_tag"` object at a specific path in Kepware.
107 |
108 | :param server: instance of the `server` class
109 | :param derived_tag_path: path identifying location and derived tag to retrieve. Standard Kepware address decimal
110 | notation string including the derived tag such as "_advancedtags.AdvTagGroup1.DerivedTag1"
111 |
112 | :return: Dict of data for the derived tag requested
113 |
114 | :raises KepHTTPError: If urllib provides an HTTPError
115 | :raises KepURLError: If urllib provides an URLError
116 | '''
117 | path_obj = adv_tags._adv_tag_path_split(derived_tag_path, isItem=True)
118 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_derived_tags_url(path_obj['item'])
119 |
120 | r = server._config_get(url)
121 | return r.payload
122 |
123 | def get_all_derived_tags(server: server, adv_tag_group_path: str, *, options: dict = None) -> list:
124 | '''Returns the properties of all `"derived_tag"` objects at a specific path in Kepware.
125 |
126 | :param server: instance of the `server` class
127 | :param adv_tag_group_path: path identifying location to retrieve derived tag list. Standard Kepware address decimal
128 | notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
129 | :param options: *(optional)* Dict of parameters to filter, sort or paginate the list of derived tags. Options are `filter`,
130 | `sortOrder`, `sortProperty`, `pageNumber`, and `pageSize`
131 |
132 | :return: List of data for all derived tags
133 |
134 | :raises KepHTTPError: If urllib provides an HTTPError
135 | :raises KepURLError: If urllib provides an URLError
136 | '''
137 | path_obj = adv_tags._adv_tag_path_split(adv_tag_group_path, isItem=False)
138 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_derived_tags_url()
139 |
140 | r = server._config_get(url, params=options)
141 | return r.payload
--------------------------------------------------------------------------------
/kepconfig/adv_tags/average_tags.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # Note: The code within this file was created in total or in part
8 | # with the use of AI tools.
9 |
10 | r"""`average_tags` exposes an API to allow modifications (add, delete, modify) to
11 | average tag objects within the Kepware Configuration API
12 | """
13 |
14 | from ..connection import server
15 | from ..error import KepError, KepHTTPError
16 | from ..utils import _url_parse_object
17 | from typing import Union
18 | from .. import adv_tags
19 |
20 | AVERAGE_TAGS_ROOT = '/average_tags'
21 |
22 | def _get_average_tags_url(tag: str = None) -> str:
23 | '''Creates url object for the "average_tags" branch of Kepware's project tree.
24 |
25 | Returns the average tag specific url when a value is passed as the tag name.
26 | '''
27 | if tag is None:
28 | return AVERAGE_TAGS_ROOT
29 | else:
30 | return f'{AVERAGE_TAGS_ROOT}/{_url_parse_object(tag)}'
31 |
32 | def add_average_tag(server: server, adv_tag_group_path: str, DATA: Union[dict, list]) -> Union[bool, list]:
33 | '''Add `"average_tag"` or multiple `"average_tag"` objects to a specific path in Kepware.
34 | Can be used to pass a list of average tags to be added at one path location.
35 |
36 | :param server: instance of the `server` class
37 | :param adv_tag_group_path: path identifying where to add average tag(s). Standard Kepware address decimal
38 | notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
39 | :param DATA: Dict or List of Dicts of the average tag(s) to add
40 |
41 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
42 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
43 | average tags added that failed.
44 |
45 | :raises KepHTTPError: If urllib provides an HTTPError
46 | :raises KepURLError: If urllib provides an URLError
47 | '''
48 | path_obj = adv_tags._adv_tag_path_split(adv_tag_group_path, isItem=False)
49 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_average_tags_url()
50 |
51 | r = server._config_add(url, DATA)
52 | if r.code == 201:
53 | return True
54 | elif r.code == 207:
55 | errors = []
56 | for item in r.payload:
57 | if item['code'] != 201:
58 | errors.append(item)
59 | return errors
60 | else:
61 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
62 |
63 | def modify_average_tag(server: server, avg_tag_path: str, DATA: dict, force: bool = False) -> bool:
64 | '''Modify an `"average_tag"` object and its properties in Kepware.
65 |
66 | :param server: instance of the `server` class
67 | :param avg_tag_path: path identifying location and average tag to modify. Standard Kepware address decimal
68 | notation string including the average tag such as "_advancedtags.AdvTagGroup1.AvgTag1"
69 | :param DATA: Dict of the `average_tag` properties to be modified
70 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
71 |
72 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
73 |
74 | :raises KepHTTPError: If urllib provides an HTTPError
75 | :raises KepURLError: If urllib provides an URLError
76 | '''
77 | avg_tag_data = server._force_update_check(force, DATA)
78 | path_obj = adv_tags._adv_tag_path_split(avg_tag_path, isItem=True)
79 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_average_tags_url(path_obj['item'])
80 |
81 | r = server._config_update(url, avg_tag_data)
82 | if r.code == 200:
83 | return True
84 | else:
85 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
86 |
87 | def del_average_tag(server: server, avg_tag_path: str) -> bool:
88 | '''Delete `"average_tag"` object at a specific path in Kepware.
89 |
90 | :param server: instance of the `server` class
91 | :param avg_tag_path: path identifying location and average tag to delete. Standard Kepware address decimal
92 | notation string including the average tag such as "_advancedtags.AdvTagGroup1.AvgTag1"
93 |
94 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
95 |
96 | :raises KepHTTPError: If urllib provides an HTTPError
97 | :raises KepURLError: If urllib provides an URLError
98 | '''
99 | path_obj = adv_tags._adv_tag_path_split(avg_tag_path, isItem=True)
100 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_average_tags_url(path_obj['item'])
101 |
102 | r = server._config_del(url)
103 | if r.code == 200:
104 | return True
105 | else:
106 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
107 |
108 | def get_average_tag(server: server, avg_tag_path: str) -> dict:
109 | '''Returns the properties of the `"average_tag"` object at a specific path in Kepware.
110 |
111 | :param server: instance of the `server` class
112 | :param avg_tag_path: path identifying location and average tag to retrieve. Standard Kepware address decimal
113 | notation string including the average tag such as "_advancedtags.AdvTagGroup1.AvgTag1"
114 |
115 | :return: Dict of data for the average tag requested
116 |
117 | :raises KepHTTPError: If urllib provides an HTTPError
118 | :raises KepURLError: If urllib provides an URLError
119 | '''
120 | path_obj = adv_tags._adv_tag_path_split(avg_tag_path, isItem=True)
121 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_average_tags_url(path_obj['item'])
122 |
123 | r = server._config_get(url)
124 | return r.payload
125 |
126 | def get_all_average_tags(server: server, adv_tag_group_path: str, *, options: dict = None) -> list:
127 | '''Returns the properties of all `"average_tag"` objects at a specific path in Kepware.
128 |
129 | :param server: instance of the `server` class
130 | :param adv_tag_group_path: path identifying location to retrieve average tag list. Standard Kepware address decimal
131 | notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
132 | :param options: *(optional)* Dict of parameters to filter, sort or paginate the list of average tags. Options are `filter`,
133 | `sortOrder`, `sortProperty`, `pageNumber`, and `pageSize`
134 |
135 | :return: List of data for all average tags
136 |
137 | :raises KepHTTPError: If urllib provides an HTTPError
138 | :raises KepURLError: If urllib provides an URLError
139 | '''
140 | path_obj = adv_tags._adv_tag_path_split(adv_tag_group_path, isItem=False)
141 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_average_tags_url()
142 |
143 | r = server._config_get(url, params=options)
144 | return r.payload
--------------------------------------------------------------------------------
/kepconfig/admin/lls.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c), PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`lls` exposes an API to allow modifications to Local License Server parameters in
8 | the Kepware Administration through the Kepware Configuration API
9 | """
10 | from .. import connection
11 | from typing import Union
12 | from ..error import KepHTTPError, KepError
13 |
14 |
15 |
16 | LLS_ROOT = '/admin'
17 | FORCE_CHECK_URL = '/project/services/ForceLicenseCheck'
18 | LICENSING_SERVER_PORT = "libadminsettings.LICENSING_SERVER_PORT"
19 | LICENSING_SERVER_NAME = "libadminsettings.LICENSING_SERVER_NAME"
20 | LICENSING_SERVER_ENABLE = "libadminsettings.LICENSING_SERVER_ENABLE"
21 | LICENSING_CHECK_PERIOD_MINS = "libadminsettings.LICENSING_CHECK_PERIOD_MINS"
22 | LICENSING_SERVER_SSL_PORT = "libadminsettings.LICENSING_SERVER_SSL_PORT"
23 | LICENSING_SERVER_ALLOW_INSECURE_COMMS = "libadminsettings.LICENSING_SERVER_ALLOW_INSECURE_COMMS"
24 | LICENSING_SERVER_ALLOW_SELF_SIGNED_CERTS = "libadminsettings.LICENSING_SERVER_ALLOW_SELF_SIGNED_CERTS"
25 | LICENSING_CLIENT_ALIAS = "libadminsettings.LICENSING_CLIENT_ALIAS"
26 |
27 | class lls_config:
28 | '''A class to represent a admin properties for the Local License Server connection from an instance of Kepware.
29 | This object is used to easily manage the LLS parameters for a Kepware instance.
30 |
31 | :param server_name: Host name or IP address of the LLS server
32 | :param server_port: HTTP/non-SSL port to target for the LLS server
33 | :param check_period: Period that Kepware checks licensing status
34 | :param server_port_SSL: HTTPS/SSL port to target for the LLS server
35 | :param allow_insecure_comms: When True, use HTTP/non-SSL connection to LLS
36 | :param allow_self_signed_certs: Allow for self signed certificates to be used during HTTPS/SSL connections to the LLS
37 | :param instance_alias_name: Alias name for LLS to use as reference to this Kepware instance
38 | '''
39 |
40 | def __init__(self, config = {}):
41 | self.server_name = config[LICENSING_SERVER_NAME] if LICENSING_SERVER_NAME in config else ''
42 | self.server_port = config[LICENSING_SERVER_PORT] if LICENSING_SERVER_PORT in config else 7070
43 | self.check_period = config[LICENSING_CHECK_PERIOD_MINS] if LICENSING_CHECK_PERIOD_MINS in config else 5
44 | self.server_port_SSL = config[LICENSING_SERVER_SSL_PORT] if LICENSING_SERVER_SSL_PORT in config else 1883
45 | self.allow_insecure_comms = config[LICENSING_SERVER_ALLOW_INSECURE_COMMS] if LICENSING_SERVER_ALLOW_INSECURE_COMMS in config else False
46 | self.allow_self_signed_certs = config[LICENSING_SERVER_ALLOW_SELF_SIGNED_CERTS] if LICENSING_SERVER_ALLOW_SELF_SIGNED_CERTS in config else False
47 | self.instance_alias_name = config[LICENSING_CLIENT_ALIAS] if LICENSING_CLIENT_ALIAS in config else ''
48 |
49 | def _get_dict(self):
50 | return {
51 | LICENSING_SERVER_PORT: self.server_port,
52 | LICENSING_SERVER_NAME: self.server_name,
53 | LICENSING_CHECK_PERIOD_MINS: self.check_period,
54 | LICENSING_SERVER_SSL_PORT: self.server_port_SSL,
55 | LICENSING_SERVER_ALLOW_INSECURE_COMMS: self.allow_insecure_comms,
56 | LICENSING_SERVER_ALLOW_SELF_SIGNED_CERTS: self.allow_self_signed_certs,
57 | LICENSING_CLIENT_ALIAS: self.instance_alias_name
58 | }
59 |
60 | def __str__(self) -> str:
61 | return "{}".format(self._get_dict())
62 |
63 | def get_lls_config(server: connection.server) -> lls_config:
64 | '''Returns the properties of the Local License server connection properties. Returned object is `lls_config` class object.
65 |
66 | :param server: instance of the `server` class
67 |
68 | :return: `lls_config` class object with lls connection configuration
69 |
70 | :raises KepHTTPError: If urllib provides an HTTPError
71 | :raises KepURLError: If urllib provides an URLError
72 | '''
73 |
74 | r = server._config_get(server.url + LLS_ROOT)
75 | return lls_config(r.payload)
76 |
77 | def update_lls_config(server: connection.server, config: lls_config) -> bool:
78 | '''Updates the Local License Server connection properties for Kepware.
79 |
80 | :param server: instance of the `server` class
81 | :param config: `lls_config` class object with lls connection configuration
82 |
83 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
84 |
85 | :raises KepHTTPError: If urllib provides an HTTPError
86 | :raises KepURLError: If urllib provides an URLError
87 | '''
88 |
89 | DATA = config._get_dict()
90 | r = server._config_update(server.url + LLS_ROOT, DATA)
91 | if r.code == 200: return True
92 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
93 |
94 | def enable_lls(server: connection.server) -> bool:
95 | '''Enables the Local License Server connection for Kepware.
96 |
97 | :param server: instance of the `server` class
98 |
99 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
100 |
101 | :raises KepHTTPError: If urllib provides an HTTPError
102 | :raises KepURLError: If urllib provides an URLError
103 | '''
104 |
105 | r = server._config_update(server.url + LLS_ROOT, {LICENSING_SERVER_ENABLE: True})
106 | if r.code == 200: return True
107 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
108 |
109 | def disable_lls(server: connection.server) -> bool:
110 | '''Disables the Local License Server connection for Kepware.
111 |
112 | :param server: instance of the `server` class
113 |
114 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
115 |
116 | :raises KepHTTPError: If urllib provides an HTTPError
117 | :raises KepURLError: If urllib provides an URLError
118 | '''
119 |
120 | r = server._config_update(server.url + LLS_ROOT, {LICENSING_SERVER_ENABLE: False})
121 | if r.code == 200: return True
122 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
123 |
124 | def force_license_check(server: connection.server, job_ttl: int = None):
125 | '''Executes a ForceLicenseCheck service call to the Kepware instance. This triggers the server to verify the
126 | license state of the license received from the Local License Server.
127 |
128 | :param server: instance of the `server` class
129 | :param job_ttl: *(optional)* Determines the number of seconds a job instance will exist following completion.
130 |
131 | :return: `KepServiceResponse` instance with job information
132 |
133 | :raises KepHTTPError: If urllib provides an HTTPError (If not HTTP code 202 [Accepted] or 429 [Too Busy] returned)
134 | :raises KepURLError: If urllib provides an URLError
135 | '''
136 |
137 | url = f'{server.url}{FORCE_CHECK_URL}'
138 | job = server._kep_service_execute(url, None, job_ttl)
139 | return job
--------------------------------------------------------------------------------
/kepconfig/adv_tags/cumulative_tags.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # Note: The code within this file was created in total or in part
8 | # with the use of AI tools.
9 |
10 | r"""`cumulative_tags` exposes an API to allow modifications (add, delete, modify) to
11 | cumulative tag objects within the Kepware Configuration API
12 | """
13 |
14 | from ..connection import server
15 | from ..error import KepError, KepHTTPError
16 | from ..utils import _url_parse_object
17 | from typing import Union
18 | from .. import adv_tags
19 |
20 | CUMULATIVE_TAGS_ROOT = '/cumulative_tags'
21 |
22 | def _get_cumulative_tags_url(tag: str = None) -> str:
23 | '''Creates url object for the "cumulative_tags" branch of Kepware's project tree.
24 |
25 | Returns the cumulative tag specific url when a value is passed as the tag name.
26 | '''
27 | if tag is None:
28 | return CUMULATIVE_TAGS_ROOT
29 | else:
30 | return f'{CUMULATIVE_TAGS_ROOT}/{_url_parse_object(tag)}'
31 |
32 | def add_cumulative_tag(server: server, adv_tag_group_path: str, DATA: Union[dict, list]) -> Union[bool, list]:
33 | '''Add `"cumulative_tag"` or multiple `"cumulative_tag"` objects to a specific path in Kepware.
34 | Can be used to pass a list of cumulative tags to be added at one path location.
35 |
36 | :param server: instance of the `server` class
37 | :param adv_tag_group_path: path identifying where to add cumulative tag(s). Standard Kepware address decimal
38 | notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
39 | :param DATA: Dict or List of Dicts of the cumulative tag(s) to add
40 |
41 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
42 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
43 | cumulative tags added that failed.
44 |
45 | :raises KepHTTPError: If urllib provides an HTTPError
46 | :raises KepURLError: If urllib provides an URLError
47 | '''
48 | path_obj = adv_tags._adv_tag_path_split(adv_tag_group_path, isItem=False)
49 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_cumulative_tags_url()
50 |
51 | r = server._config_add(url, DATA)
52 | if r.code == 201:
53 | return True
54 | elif r.code == 207:
55 | errors = [item for item in r.payload if item['code'] != 201]
56 | return errors
57 | else:
58 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
59 |
60 | def modify_cumulative_tag(server: server, cumulative_tag_path: str, DATA: dict, force: bool = False) -> bool:
61 | '''Modify a `"cumulative_tag"` object and its properties in Kepware.
62 |
63 | :param server: instance of the `server` class
64 | :param cumulative_tag_path: path identifying location and cumulative tag to modify. Standard Kepware address decimal
65 | notation string including the cumulative tag such as "_advancedtags.AdvTagGroup1.CumulativeTag1"
66 | :param DATA: Dict of the `cumulative_tag` properties to be modified
67 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
68 |
69 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
70 |
71 | :raises KepHTTPError: If urllib provides an HTTPError
72 | :raises KepURLError: If urllib provides an URLError
73 | '''
74 | cum_tag_data = server._force_update_check(force, DATA)
75 | path_obj = adv_tags._adv_tag_path_split(cumulative_tag_path, isItem=True)
76 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_cumulative_tags_url(path_obj['item'])
77 |
78 | r = server._config_update(url, cum_tag_data)
79 | if r.code == 200:
80 | return True
81 | else:
82 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
83 |
84 | def del_cumulative_tag(server: server, cumulative_tag_path: str) -> bool:
85 | '''Delete `"cumulative_tag"` object at a specific path in Kepware.
86 |
87 | :param server: instance of the `server` class
88 | :param cumulative_tag_path: path identifying location and cumulative tag to delete. Standard Kepware address decimal
89 | notation string including the cumulative tag such as "_advancedtags.AdvTagGroup1.CumulativeTag1"
90 |
91 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
92 |
93 | :raises KepHTTPError: If urllib provides an HTTPError
94 | :raises KepURLError: If urllib provides an URLError
95 | '''
96 | path_obj = adv_tags._adv_tag_path_split(cumulative_tag_path, isItem=True)
97 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_cumulative_tags_url(path_obj['item'])
98 |
99 | r = server._config_del(url)
100 | if r.code == 200:
101 | return True
102 | else:
103 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
104 |
105 | def get_cumulative_tag(server: server, cumulative_tag_path: str) -> dict:
106 | '''Returns the properties of the `"cumulative_tag"` object at a specific path in Kepware.
107 |
108 | :param server: instance of the `server` class
109 | :param cumulative_tag_path: path identifying location and cumulative tag to retrieve. Standard Kepware address decimal
110 | notation string including the cumulative tag such as "_advancedtags.AdvTagGroup1.CumulativeTag1"
111 |
112 | :return: Dict of data for the cumulative tag requested
113 |
114 | :raises KepHTTPError: If urllib provides an HTTPError
115 | :raises KepURLError: If urllib provides an URLError
116 | '''
117 | path_obj = adv_tags._adv_tag_path_split(cumulative_tag_path, isItem=True)
118 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_cumulative_tags_url(path_obj['item'])
119 |
120 | r = server._config_get(url)
121 | return r.payload
122 |
123 | def get_all_cumulative_tags(server: server, adv_tag_group_path: str, *, options: dict = None) -> list:
124 | '''Returns the properties of all `"cumulative_tag"` objects at a specific path in Kepware.
125 |
126 | :param server: instance of the `server` class
127 | :param adv_tag_group_path: path identifying location to retrieve cumulative tag list. Standard Kepware address decimal
128 | notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
129 | :param options: *(optional)* Dict of parameters to filter, sort or paginate the list of cumulative tags. Options are `filter`,
130 | `sortOrder`, `sortProperty`, `pageNumber`, and `pageSize`
131 |
132 | :return: List of data for all cumulative tags
133 |
134 | :raises KepHTTPError: If urllib provides an HTTPError
135 | :raises KepURLError: If urllib provides an URLError
136 | '''
137 | path_obj = adv_tags._adv_tag_path_split(adv_tag_group_path, isItem=False)
138 | url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_cumulative_tags_url()
139 |
140 | r = server._config_get(url, params=options)
141 | return r.payload
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Kepware Configuration API SDK for Python
2 |
3 | [](https://pypi.org/project/kepconfig) [](https://pypi.org/project/kepconfig)  
4 |
5 | This is a package to help create Python applications to conduct operations with the Kepware Configuration API. This package is designed to work with all versions of Kepware that support the Configuration API including Thingworx Kepware Server (TKS), Thingworx Kepware Edge (TKE) and KEPServerEX (KEP). For reference, Kepware Server in this documentation will refer to both TKS and KEP versions.
6 |
7 | **API reference documentation is available on [Github Pages](https://ptcinc.github.io/Kepware-ConfigAPI-SDK-Python)**
8 |
9 | ## Prerequisites
10 |
11 | Package supported and tested on Python 3.9 or later. Older versions support earlier Python 3 environments but have less functionality. All HTTP communication is handled by the [urllib](https://docs.python.org/3/library/urllib.html#module-urllib) Python standard library.
12 |
13 | ## Features
14 |
15 | - Supports both HTTP and HTTPS connections with certificate validation options
16 |
17 | Package allows for *GET*, *ADD*, *DELETE*, and *MODIFY* functions for the following Kepware configuration objects:
18 |
19 | | Features | TKS/KEP | TKE |
20 | | :----------: | :----------: | :----------: |
21 | | **Project Properties**
*(Get and Modify Only)* | Y | Y |
22 | | **Connectivity**
*(Channel, Devices, Tags, Tag Groups)* | Y | Y |
23 | | **IoT Gateway**
*(Agents, IoT Items)* | Y | Y |
24 | | **Datalogger**
*(Log Groups, Items, Mapping, Triggers, Reset Mapping Service)* | Y | N |
25 | | **UA Gateway**
*(Certificates, Server Endpoints, Client Connections, Server Interface parameters)* | Y*** | N |
26 | | **Advanced Tags**
*(All tag types and tag groups)*| Y****** | N |
27 | | **Administration**
*(User Groups, Users, UA Endpoints, Local License Server)* | Y* | Y |
28 | | **Product Info and Health Status\*\*** | Y | Y |
29 | | **Import Project (via JsonProjectLoad Service) / Export Project\*\*\*\***| Y | Y |
30 | | **Backup Project (via CreateBackup Service) / Export Project\*\*\*\*\***| Y | Y |
31 |
32 | - Note (*) - UA Endpoints and Local License Server supported for TKE only
33 | - Note (**) - Added to Kepware Server v6.13 / TKE v1.5 and later builds
34 | - Note (***) - TKS only v6.16 and later
35 | - Note (****) - Added to Kepware Server v6.17 / TKE v1.10 and later builds
36 | - Note (*****) - Added to Kepware Server v6.18 / TKE v1.11 and later builds
37 |
38 | Driver specific features:
39 |
40 | | Driver | Features |
41 | | :----------: | :----------: |
42 | |GE Ethernet Global Data|Exchanges, Ranges and Name Resolutions|
43 | |Universal Device Driver|Profile Library|
44 |
45 | Methods to read the following logs:
46 |
47 | | Logs | TKS/KEP | TKE |
48 | | :----------: | :----------: | :----------: |
49 | | **Event Log** | Y | Y |
50 | | **API Transaction Log** | Y | Y |
51 | | **Audit Log*** | Y | Y |
52 |
53 | - Note (*) - Implemented for Kepware Server v6.18+ and TKE 1.11+
54 |
55 | Configuration API *Services* implemented:
56 |
57 | | Services | TKS/KEP | TKE |
58 | | :----------: | :----------: | :----------: |
59 | | **TagGeneration**
*(for supported drivers)* | Y | Y |
60 | | **ReinitializeRuntime** | Y* | Y |
61 | | **ProjectLoad and ProjectSave**| Y | Y |
62 | | **JsonProjectLoad\*\***
*(used for import project feature)*| Y | Y |
63 | | **CreateBackup\*\*\***| Y | Y |
64 |
65 | - Note (*) - Reinitialize service was implemented for Kepware Server v6.8+
66 | - Note (**) - Added to Kepware Server v6.17 / TKE v1.10 and later builds
67 | - Note (***) - Added to Kepware Server v6.18 / TKE v1.11 and later builds
68 |
69 | Filtering, sorting and pagination query options are added for any collections methods (ex: `get_all_devices()` or `get_all_channel()`).
70 |
71 | Generic REST methods are provided to use for functions not developed in SDK package. These are found in the `Server` class in [connection.py](./kepconfig/connection.py)
72 |
73 | ## Known Limitations
74 |
75 | - Other property configuration for more complex drivers with objects besides channels, devices, tags and tag groups are not always explicitly defined
76 | - Other supported plug-ins (EFM Exporter, Scheduler, etc) are not defined
77 | - When using hostnames (not IP addresses) for connections, delays may occur under certain network configurations as the connection may attempt IPv6 connections first. IPv6 is not supported by Kepware servers at this time.
78 |
79 | ## Installation
80 |
81 | Package can be installed with `pip` using the following:
82 |
83 | ```cmd
84 | pip install kepconfig
85 | ```
86 |
87 | ## Key Concepts
88 |
89 | **NOTE:** Detailed examples can also be found in the [examples](./examples/) folder.
90 |
91 | ### Create server connection instance
92 |
93 | ```python
94 | from kepconfig import connection
95 |
96 | server = connection.server(host = '127.0.0.1', port = 57412, user = 'Administrator', pw = '')
97 |
98 | # For HTTPS connections:
99 | server = connection.server(host = '127.0.0.1', port = 57512, user = 'Administrator', pw = '', https=True)
100 |
101 | ```
102 |
103 | For certificate validation, the SDK uses the OS/systems trusted certificate store. The connection uses the `create_default_context()` function as part of urllib as described at the following links:
104 |
105 | - [ssl.create_default_context](https://docs.python.org/3/library/ssl.html#ssl.create_default_context)
106 | - [ssl.SSLContext.load_default_certs](https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_default_certs)
107 | - [set_default_verify_paths](https://docs.python.org/3/library/ssl.html#ssl.SSLContext.set_default_verify_paths)
108 |
109 | For Windows OSes, the Kepware Server's instance certificate can be loaded into the hosts "Trusted Root Certificate Authorities" store.
110 |
111 | ### Create an object
112 |
113 | Objects such as channels or devices can be created either singularly or with children included.
114 |
115 | ### Ex: Add Single channel
116 |
117 | ```python
118 | from kepconfig.connectivity import channel
119 |
120 | channel_data = {"common.ALLTYPES_NAME": "Channel1","servermain.MULTIPLE_TYPES_DEVICE_DRIVER": "Simulator"}
121 | result = channel.add_channel(server,channel_data)
122 | ```
123 |
124 | ### Ex: Add Multiple tags
125 |
126 | ```python
127 | from kepconfig.connectivity import tag
128 |
129 | tag_info = [
130 | {
131 | "common.ALLTYPES_NAME": "Temp",
132 | "servermain.TAG_ADDRESS": "R0"
133 | },
134 | {
135 | "common.ALLTYPES_NAME": "Temp2",
136 | "servermain.TAG_ADDRESS": "R1"
137 | }
138 | ]
139 | tag_path = '{}.{}.{}'.format(ch_name, dev_name, tag_group_path)
140 | result = tag.add_tag(server, tag_path, tag_info)
141 |
142 | ```
143 |
144 | ## Need More Information
145 |
146 | **Visit:**
147 |
148 | - [Kepconfig Package Documentation on Github Pages](https://ptcinc.github.io/Kepware-ConfigAPI-SDK-Python)
149 | - [Kepware.com](https://www.kepware.com/)
150 | - [PTC.com](https://www.ptc.com/)
151 |
--------------------------------------------------------------------------------
/kepconfig/iot_gateway/iot_items.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 |
8 | r"""`iot_items` exposes an API to allow modifications (add, delete, modify) to
9 | iot_items objects within the Kepware Configuration API
10 | """
11 |
12 | from typing import Union
13 | from .. import utils
14 | from ..connection import server
15 | from .. import iot_gateway as IOT
16 | from ..error import KepError, KepHTTPError
17 | from ..utils import _url_parse_object
18 |
19 | IOT_ITEMS_ROOT = '/iot_items'
20 |
21 | def _create_url(tag = None):
22 | '''Creates url object for the "iot items" branch of Kepware's IoT Agents property model. Used
23 | to build a part of Kepware Configuration API URL structure
24 |
25 | Returns the device specific url when a value is passed as the iot item name.
26 | '''
27 | if tag == None:
28 | return IOT_ITEMS_ROOT
29 | else:
30 | normalized_tag = utils._address_dedecimal(tag)
31 | return '{}/{}'.format(IOT_ITEMS_ROOT, _url_parse_object(normalized_tag))
32 |
33 |
34 | def add_iot_item(server: server, DATA: Union[dict, list], agent: str, agent_type: str) -> Union[bool, list]:
35 | '''Add a `"iot item"` or multiple `"iot item"` objects to Kepware's IoT Gateway agent. Additionally
36 | it can be used to pass a list of iot items to be added to an agent all at once.
37 |
38 | :param server: instance of the `server` class
39 | :param DATA: Dict or List of Dicts of the iot item or list of items
40 | expected by Kepware Configuration API
41 | :param agent: name of IoT Agent
42 | :param agent_type: agent type. Valid values are `MQTT Client`, `REST Client` or `REST Server`
43 |
44 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
45 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
46 | iot items added that failed.
47 |
48 | :raises KepHTTPError: If urllib provides an HTTPError
49 | :raises KepURLError: If urllib provides an URLError
50 | '''
51 | r = server._config_add(server.url + IOT.agent._create_url(agent_type, agent) + _create_url(), DATA)
52 | if r.code == 201: return True
53 | elif r.code == 207:
54 | errors = []
55 | for item in r.payload:
56 | if item['code'] != 201:
57 | errors.append(item)
58 | return errors
59 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
60 |
61 | def del_iot_item(server: server, iot_item: str, agent: str, agent_type: str) -> bool:
62 | '''Delete an `"iot item"` object in Kepware.
63 |
64 | :param server: instance of the `server` class
65 | :param iot_item: IoT item to delete
66 | :param agent: name of IoT Agent
67 | :param agent_type: agent type. Valid values are `MQTT Client`, `REST Client` or `REST Server`
68 |
69 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
70 |
71 | :raises KepHTTPError: If urllib provides an HTTPError
72 | :raises KepURLError: If urllib provides an URLError
73 | '''
74 | r = server._config_del(server.url + IOT.agent._create_url(agent_type, agent) + _create_url(iot_item))
75 | if r.code == 200: return True
76 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
77 |
78 | def modify_iot_item(server: server, DATA: dict, agent: str, agent_type: str, *, iot_item: str = None, force: bool = False) -> bool:
79 | '''Modify an `"iot item"` object and it's properties in Kepware. If a `"iot item"` is not provided as an input,
80 | you need to identify the iot item in the *'common.ALLTYPES_NAME'* property field in the `"DATA"`. It will
81 | assume that is the iot item that is to be modified.
82 |
83 | :param server: instance of the `server` class
84 | :param DATA: Dict of the iot item properties to be modified.
85 | :param agent: name of IoT Agent
86 | :param agent_type: agent type. Valid values are `MQTT Client`, `REST Client` or `REST Server`
87 | :param iot_item: *(optional)* name of IoT item to modify. Only needed if not existing in `"DATA"`
88 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
89 |
90 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
91 |
92 | :raises KepHTTPError: If urllib provides an HTTPError
93 | :raises KepURLError: If urllib provides an URLError
94 | '''
95 |
96 | agent_data = server._force_update_check(force, DATA)
97 | if iot_item == None:
98 | try:
99 | r = server._config_update(server.url + IOT.agent._create_url(agent_type, agent) + _create_url(agent_data['common.ALLTYPES_NAME']), agent_data)
100 | if r.code == 200: return True
101 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
102 | except KeyError as err:
103 | err_msg = 'Error: No agent identified in DATA | Key Error: {}'.format(err)
104 | raise KepError(err_msg)
105 | else:
106 | r = server._config_update(server.url + IOT.agent._create_url(agent_type, agent) + _create_url(iot_item), agent_data)
107 | if r.code == 200: return True
108 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
109 |
110 | def get_iot_item(server: server, iot_item: str, agent: str, agent_type: str)-> dict:
111 | '''Returns the properties of the `"iot item"` object.
112 |
113 | :param server: instance of the `server` class
114 | :param iot_item: name of IoT item to retrieve properties
115 | :param agent: name of IoT Agent
116 | :param agent_type: agent type. Valid values are `MQTT Client`, `REST Client` or `REST Server`
117 |
118 | :return: Dict of properties for the iot item requested
119 |
120 | :raises KepHTTPError: If urllib provides an HTTPError
121 | :raises KepURLError: If urllib provides an URLError
122 | '''
123 | r = server._config_get(server.url + IOT.agent._create_url(agent_type, agent) + _create_url(iot_item))
124 | return r.payload
125 |
126 | def get_all_iot_items(server: server, agent: str, agent_type: str, *, options: dict = None) -> list:
127 | '''Returns the properties of all `"iot item"` objects for an agent.
128 |
129 | :param server: instance of the `server` class
130 | :param iot_item: name of IoT item to retrieve properties
131 | :param agent: name of IoT Agent
132 | :param agent_type: agent type. Valid values are `MQTT Client`, `REST Client` or `REST Server`
133 | :param options: *(optional)* Dict of parameters to filter, sort or pagenate the list of IoT items. Options are 'filter',
134 | 'sortOrder', 'sortProperty', 'pageNumber', and 'pageSize'. Only used when exchange_name is not defined.
135 |
136 | :return: list of properties for all IoT items
137 |
138 | :raises KepHTTPError: If urllib provides an HTTPError
139 | :raises KepURLError: If urllib provides an URLError
140 | '''
141 | r = server._config_get(f'{server.url}{IOT.agent._create_url(agent_type, agent)}{_create_url()}', params= options)
142 | return r.payload
--------------------------------------------------------------------------------
/kepconfig/connectivity/egd/range.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 |
8 | r"""`ranges` exposes an API to allow modifications (add, delete, modify) to
9 | range objects in exchanges for EGD devices within the Kepware Configuration API
10 | """
11 |
12 | from typing import Union
13 | from .. import egd as EGD
14 | from ...connection import server
15 | from ...error import KepError, KepHTTPError
16 | from ...utils import _url_parse_object
17 |
18 | RANGES_ROOT = '/ranges'
19 |
20 | def _create_url(device_path, ex_type, exchange_name, range = None):
21 | '''Creates url object for the "range" branch of Kepware's project tree. Used
22 | to build a part of Kepware Configuration API URL structure
23 |
24 | Returns the range specific url when a value is passed as the range name.
25 | '''
26 | exchange_root = EGD.exchange._create_url(device_path, ex_type, exchange_name)
27 |
28 | if range == None:
29 | return '{}{}'.format(exchange_root, RANGES_ROOT)
30 | else:
31 | return '{}{}/{}'.format(exchange_root, RANGES_ROOT, _url_parse_object(range))
32 |
33 | def add_range(server: server, device_path: str, ex_type: str, exchange_name: str, DATA: Union[dict, list]) -> Union[bool, list]:
34 | '''Add a `"range"` or multiple `"range"` objects to Kepware. This allows you to
35 | create a range or multiple ranges all in one function, if desired.
36 |
37 | When passing multiple ranges, they will be populated in the same order
38 | in the list sent. Ensure you provide the list in the order desired.
39 |
40 | :param server: instance of the `server` class
41 | :param device_path: path to EGD device. Standard Kepware address decimal
42 | notation string such as `"channel1.device1"`
43 | :param ex_type: type of exchange, either `CONSUMER` or `PRODUCER`
44 | :param exchange_name: name of exchange that range is located
45 | :param DATA: Dict or List of Dicts of the range(s) to add
46 |
47 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
48 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
49 | ranges added that failed.
50 |
51 | :raises KepHTTPError: If urllib provides an HTTPError
52 | :raises KepURLError: If urllib provides an URLError
53 | '''
54 |
55 | r = server._config_add(server.url + _create_url(device_path, ex_type, exchange_name), DATA)
56 | if r.code == 201: return True
57 | elif r.code == 207:
58 | errors = []
59 | for item in r.payload:
60 | if item['code'] != 201:
61 | errors.append(item)
62 | return errors
63 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
64 |
65 | def del_range(server: server, device_path: str, ex_type: str, exchange_name: str, range_name: str) -> bool:
66 | '''Delete a `"range"` object in Kepware.
67 |
68 | :param server: instance of the `server` class
69 | :param device_path: path to EGD device. Standard Kepware address decimal
70 | notation string such as `"channel1.device1"`
71 | :param ex_type: type of exchange, either `CONSUMER` or `PRODUCER`
72 | :param exchange_name: name of exchange that range is located
73 | :param range_name: name of range to delete
74 |
75 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
76 |
77 | :raises KepHTTPError: If urllib provides an HTTPError
78 | :raises KepURLError: If urllib provides an URLError
79 | '''
80 |
81 | r = server._config_del(server.url + _create_url(device_path, ex_type, exchange_name, range_name))
82 | if r.code == 200: return True
83 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
84 |
85 | def modify_range(server: server, device_path: str, ex_type: str, exchange_name: str, DATA: dict, *, range_name: str = None, force: bool = False) -> bool:
86 | '''Modify a `"range"` object and it's properties in Kepware. If a `"range_name"` is not provided as an input,
87 | you need to identify the range in the *'common.ALLTYPES_NAME'* property field in the `"DATA"`. It will
88 | assume that is the range that is to be modified.
89 |
90 | :param server: instance of the `server` class
91 | :param device_path: path to EGD device. Standard Kepware address decimal
92 | notation string such as `"channel1.device1"`
93 | :param ex_type: type of exchange, either `CONSUMER` or `PRODUCER`
94 | :param exchange_name: name of exchange that range is located
95 | :param DATA: Dict of the range properties to be modified.
96 | :param range_name: *(optional)* name of range to to modify. Only needed if not existing in `"DATA"`
97 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
98 |
99 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
100 |
101 | :raises KepHTTPError: If urllib provides an HTTPError
102 | :raises KepURLError: If urllib provides an URLError
103 | '''
104 |
105 | range_data = server._force_update_check(force, DATA)
106 | if range_name == None:
107 | try:
108 | r = server._config_update(server.url + _create_url(device_path, ex_type, exchange_name, range_data['common.ALLTYPES_NAME']), range_data)
109 | if r.code == 200: return True
110 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
111 | except KeyError as err:
112 | err_msg = 'Error: No range identified in DATA | Key Error: {}'.format(err)
113 | raise KepError(err_msg)
114 | else:
115 | r = server._config_update(server.url + _create_url(device_path, ex_type, exchange_name, range_name), range_data)
116 | if r.code == 200: return True
117 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
118 |
119 | def get_range(server: server, device_path: str, ex_type: str, exchange_name: str, range_name: str = None, *, options: dict = None) -> Union[dict, list]:
120 | '''Returns the properties of the `"range"` object or a list of all ranges.
121 |
122 | :param server: instance of the `server` class
123 | :param device_path: path to EGD device. Standard Kepware address decimal
124 | notation string such as `"channel1.device1"`
125 | :param ex_type: type of exchange, either `CONSUMER` or `PRODUCER`
126 | :param exchange_name: name of exchange that range is located
127 | :param DATA: Dict of the range properties to be modified.
128 | :param range_name: *(optional)* name of range to retrieve. If not defined, get all ranges
129 | :param options: *(optional)* Dict of parameters to filter, sort or pagenate the list of exchanges. Options are 'filter',
130 | 'sortOrder', 'sortProperty', 'pageNumber', and 'pageSize'. Only used when range_name is not defined.
131 |
132 | :return: Dict of properties for the range requested or a List of ranges and their properties
133 |
134 | :raises KepHTTPError: If urllib provides an HTTPError
135 | :raises KepURLError: If urllib provides an URLError
136 | '''
137 | if range_name == None:
138 | r = server._config_get(f'{server.url}{_create_url(device_path, ex_type, exchange_name)}', params= options)
139 | else:
140 | r = server._config_get(f'{server.url}{_create_url(device_path, ex_type, exchange_name, range_name)}')
141 | return r.payload
--------------------------------------------------------------------------------
/kepconfig/datalogger/log_group.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | r"""`log_group` exposes an API to allow modifications (add, delete, modify) to
8 | log group objects in DataLogger within the Kepware Configuration API
9 | """
10 | from typing import Union
11 | from ..connection import KepServiceResponse, server
12 | from ..error import KepError, KepHTTPError
13 | from ..utils import _url_parse_object
14 |
15 | ENABLE_PROPERTY = 'datalogger.LOG_GROUP_ENABLED'
16 | LOG_GROUP_ROOT = '/project/_datalogger/log_groups'
17 | SERVICES_ROOT = '/services'
18 | def _create_url(log_group = None):
19 | '''Creates url object for the "log_group" branch of Kepware's project tree. Used
20 | to build a part of Kepware Configuration API URL structure
21 |
22 | Returns the agent specific url when a value is passed as the agent name.
23 | '''
24 |
25 | if log_group == None:
26 | return '{}'.format(LOG_GROUP_ROOT)
27 | else:
28 | return '{}/{}'.format(LOG_GROUP_ROOT, _url_parse_object(log_group))
29 |
30 |
31 | def add_log_group(server: server, DATA: Union[dict, list]) -> Union[bool, list]:
32 | '''Add a `"log group"` or multiple `"log groups"` objects to Kepware's DataLogger. It can be used
33 | to pass a list of log groups to be added all at once.
34 |
35 | :param server: instance of the `server` class
36 | :param DATA: Dict or a list of the log groups to add through Kepware Configuration API
37 |
38 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
39 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
40 | log groups added that failed.
41 |
42 | :raises KepHTTPError: If urllib provides an HTTPError
43 | :raises KepURLError: If urllib provides an URLError
44 | '''
45 |
46 | r = server._config_add(server.url + _create_url(), DATA)
47 | if r.code == 201: return True
48 | elif r.code == 207:
49 | errors = []
50 | for item in r.payload:
51 | if item['code'] != 201:
52 | errors.append(item)
53 | return errors
54 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
55 |
56 | def del_log_group(server: server, log_group: str) -> bool:
57 | '''Delete a `"log group"` object in Kepware's Datalogger.
58 |
59 | :param server: instance of the `server` class
60 | :param log_group: name of log group to delete
61 |
62 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
63 |
64 | :raises KepHTTPError: If urllib provides an HTTPError
65 | :raises KepURLError: If urllib provides an URLError
66 | '''
67 | r = server._config_del(server.url + _create_url(log_group))
68 | if r.code == 200: return True
69 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
70 |
71 | def modify_log_group(server: server, DATA: dict, *, log_group: str = None, force: bool = False) -> bool:
72 | '''Modify a `"log group"` object and it's properties in Kepware's Datalogger. If a `"log group"` is not provided as an input,
73 | you need to identify the log group in the *'common.ALLTYPES_NAME'* property field in the `"DATA"`. It will
74 | assume that is the log group that is to be modified.
75 |
76 | :param server: instance of the `server` class
77 | :param DATA: Dict of the log group properties to be modified.
78 | :param log_group: *(optional)* name of log group to modify. Only needed if not existing in `"DATA"`
79 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
80 |
81 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
82 |
83 | :raises KepHTTPError: If urllib provides an HTTPError
84 | :raises KepURLError: If urllib provides an URLError
85 | '''
86 |
87 | log_group_data = server._force_update_check(force, DATA)
88 |
89 | if log_group == None:
90 | try:
91 | r = server._config_update(server.url + _create_url(log_group_data['common.ALLTYPES_NAME']), log_group_data)
92 | if r.code == 200: return True
93 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
94 | except KeyError as err:
95 | err_msg = 'Error: No log group identified in DATA | Key Error: {}'.format(err)
96 | raise KepError(err_msg)
97 | else:
98 | r = server._config_update(server.url + _create_url(log_group), log_group_data)
99 | if r.code == 200: return True
100 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
101 |
102 | def get_log_group(server: server, log_group: str) -> dict:
103 | '''Returns the properties of the `"log group"` object.
104 |
105 | :param server: instance of the `server` class
106 | :param log_group: name of log group to retrieve
107 |
108 | :return: Dict of properties for the log group requested
109 |
110 | :raises KepHTTPError: If urllib provides an HTTPError
111 | :raises KepURLError: If urllib provides an URLError
112 | '''
113 | r = server._config_get(server.url + _create_url(log_group))
114 | return r.payload
115 |
116 | def get_all_log_groups(server: server, *, options: dict = None) -> list:
117 | '''Returns the properties of all log group objects for Kepware's Datalogger. Returned object is JSON list.
118 |
119 | :param server: instance of the `server` class
120 | :param options: *(optional)* Dict of parameters to filter, sort or pagenate the list of log groups. Options are 'filter',
121 | 'sortOrder', 'sortProperty', 'pageNumber', and 'pageSize'. Only used when exchange_name is not defined.
122 |
123 | :return: list of properties for all log groups requested
124 |
125 | :raises KepHTTPError: If urllib provides an HTTPError
126 | :raises KepURLError: If urllib provides an URLError
127 | '''
128 | r = server._config_get(f'{server.url}{_create_url()}', params= options)
129 | return r.payload
130 |
131 | def enable_log_group(server: server, log_group: str) -> bool:
132 | '''Enable the `"log group"`.
133 |
134 | :param server: instance of the `server` class
135 | :param log_group: name of log group to enable
136 |
137 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
138 |
139 | :raises KepHTTPError: If urllib provides an HTTPError
140 | :raises KepURLError: If urllib provides an URLError
141 | '''
142 | DATA = {ENABLE_PROPERTY: True}
143 | return modify_log_group(server, DATA, log_group= log_group)
144 |
145 | def disable_log_group(server: server, log_group: str) -> bool:
146 | '''Disable the log group. Returned object is JSON.
147 |
148 | :param server: instance of the `server` class
149 | :param log_group: name of log group to enable
150 |
151 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
152 |
153 | :raises KepHTTPError: If urllib provides an HTTPError
154 | :raises KepURLError: If urllib provides an URLError
155 | '''
156 | DATA = {ENABLE_PROPERTY: False}
157 | return modify_log_group(server, DATA, log_group= log_group)
158 |
159 | def reset_column_mapping_service(server: server, log_group: str, job_ttl: int = None) -> KepServiceResponse:
160 | '''Executes a ResetColumnMapping serivce call to the log group
161 |
162 | :param server: instance of the `server` class
163 | :param log_group: name of log group to enable
164 | :param job_ttl: *(optional)* Determines the number of seconds a job instance will exist following completion.
165 |
166 | :return: `KepServiceResponse` instance with job information
167 |
168 | :raises KepHTTPError: If urllib provides an HTTPError (If not HTTP code 202 [Accepted] or 429 [Too Busy] returned)
169 | :raises KepURLError: If urllib provides an URLError
170 | '''
171 |
172 | url = server.url + _create_url(log_group) + SERVICES_ROOT + '/ResetColumnMapping'
173 | job = server._kep_service_execute(url, None, job_ttl)
174 | return job
--------------------------------------------------------------------------------
/examples/iot gateway example.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 | # IoT Gateway Example - Simple example on how to manage a connection and
8 | # execute various calls for the IoT Gateway components of the Kepware
9 | # Configuration API
10 |
11 | from kepconfig import connection, error
12 | from kepconfig.connectivity import channel
13 | import kepconfig.iot_gateway as iot
14 |
15 | # Agent name and Type to be used - constants from kepconfig.iotgateway
16 | # can be used to identify the type of agent
17 | agent_name = 'MQTT Agent 1'
18 | agent_type = iot.MQTT_CLIENT_AGENT
19 |
20 | #Tag Address to add to the IoT agent
21 | iot_item_name = "Channel1.Device1.Tag1"
22 |
23 | def ErrorHandler(err):
24 | # Generic Handler for exception errors
25 | if isinstance(err, error.KepHTTPError):
26 | print(err.code)
27 | print(err.msg)
28 | print(err.url)
29 | print(err.hdrs)
30 | print(err.payload)
31 | elif isinstance(err, error.KepURLError):
32 | print(err.url)
33 | print(err.reason)
34 | elif isinstance(err, error.KepError):
35 | print(err.msg)
36 | else:
37 | print('Different Exception Received: {}'.format(err))
38 |
39 | # This creates a server reference that is used to target all modifications of
40 | # the Kepware configuration
41 | server = connection.server(host = '127.0.0.1', port = 57412, user = 'Administrator', pw = '')
42 |
43 |
44 | # Add a Channel using the "Simulator Driver" with device and tags.
45 | # These tags will be added to the IoT Agent.
46 | channel_data = {
47 | "common.ALLTYPES_NAME": "Channel1",
48 | "common.ALLTYPES_DESCRIPTION": "This is the test channel created",
49 | "servermain.MULTIPLE_TYPES_DEVICE_DRIVER": "Simulator",
50 | "devices": [
51 | {
52 | "common.ALLTYPES_NAME": "Device1",
53 | "common.ALLTYPES_DESCRIPTION": "Hello, new description",
54 | "servermain.MULTIPLE_TYPES_DEVICE_DRIVER": "Simulator",
55 | "servermain.DEVICE_MODEL": 0,
56 | "tags": [
57 | {
58 | "common.ALLTYPES_NAME": "Tag1",
59 | "common.ALLTYPES_DESCRIPTION": "Ramping Read/Write tag used to verify client connection",
60 | "servermain.TAG_ADDRESS": "R0001",
61 | "servermain.TAG_DATA_TYPE": 5,
62 | "servermain.TAG_READ_WRITE_ACCESS": 1,
63 | "servermain.TAG_SCAN_RATE_MILLISECONDS": 100,
64 | "servermain.TAG_SCALING_TYPE": 0
65 | },
66 | {
67 | "common.ALLTYPES_NAME": "Tag2",
68 | "common.ALLTYPES_DESCRIPTION": "Constant Read/Write tag used to verify client connection",
69 | "servermain.TAG_ADDRESS": "K0001",
70 | "servermain.TAG_DATA_TYPE": 5,
71 | "servermain.TAG_READ_WRITE_ACCESS": 1,
72 | "servermain.TAG_SCAN_RATE_MILLISECONDS": 100,
73 | "servermain.TAG_SCALING_TYPE": 0
74 | }
75 | ]
76 | }
77 | ]
78 | }
79 | try:
80 | print("{} - {}".format("Adding Channel, Device and tags", channel.add_channel(server,channel_data)))
81 | except Exception as err:
82 | ErrorHandler(err)
83 |
84 |
85 | # Add the MQTT Agent with the appropriate parameters
86 | agent_data = {
87 | "common.ALLTYPES_NAME": agent_name,
88 | "iot_gateway.AGENTTYPES_ENABLED": True,
89 | "iot_gateway.MQTT_CLIENT_URL": "tcp://localhost:1883",
90 | "iot_gateway.MQTT_CLIENT_TOPIC": "iotgateway",
91 | "iot_gateway.MQTT_CLIENT_QOS": 1,
92 | "iot_gateway.AGENTTYPES_RATE_MS": 10000,
93 | "iot_gateway.AGENTTYPES_PUBLISH_FORMAT": 0,
94 | "iot_gateway.AGENTTYPES_MAX_EVENTS": 1000,
95 | "iot_gateway.AGENTTYPES_TIMEOUT_S": 5,
96 | "iot_gateway.AGENTTYPES_MESSAGE_FORMAT": 0,
97 | "iot_gateway.MQTT_CLIENT_CLIENT_ID": "",
98 | "iot_gateway.MQTT_CLIENT_USERNAME": "",
99 | "iot_gateway.MQTT_CLIENT_PASSWORD": ""
100 | }
101 | try:
102 | print("{} - {}".format("Add the MQTT Agent", iot.agent.add_iot_agent(server, agent_data, agent_type)))
103 | except Exception as err:
104 | ErrorHandler(err)
105 |
106 | # Modify a property of the Agent
107 | agent_data = {
108 | }
109 | agent_data['common.ALLTYPES_DESCRIPTION'] = 'This is the test agent created'
110 | try:
111 | print("{} - {}".format("Modify property in the MQTT Agent", iot.agent.modify_iot_agent(server,agent_data, agent= agent_name, agent_type= agent_type)))
112 | except Exception as err:
113 | ErrorHandler(err)
114 |
115 | # Get Agent the properties for the agent that was created. It will return the
116 | # JSON of the properties
117 | try:
118 | print("{} - {}".format("Read properties of the MQTT Agent", iot.agent.get_iot_agent(server, agent_name, agent_type)))
119 | except Exception as err:
120 | ErrorHandler(err)
121 |
122 | # Get a list of all MQTT Agents that are configured
123 | try:
124 | print("{} - {}".format("Getting list of MQTT Agents", iot.agent.get_all_iot_agents(server, agent_type)))
125 | except Exception as err:
126 | ErrorHandler(err)
127 |
128 | # Add an tag or IoT Item to the MQTT Agent to start publishing
129 | iot_item_data = {
130 | "common.ALLTYPES_NAME": iot_item_name,
131 | "common.ALLTYPES_DESCRIPTION": "",
132 | "iot_gateway.IOT_ITEM_SERVER_TAG": iot_item_name,
133 | "iot_gateway.IOT_ITEM_USE_SCAN_RATE": True,
134 | "iot_gateway.IOT_ITEM_SCAN_RATE_MS": 1000,
135 | "iot_gateway.IOT_ITEM_SEND_EVERY_SCAN": False,
136 | "iot_gateway.IOT_ITEM_DEADBAND_PERCENT": 0,
137 | "iot_gateway.IOT_ITEM_ENABLED": True,
138 | "iot_gateway.IOT_ITEM_DATA_TYPE": 5
139 | }
140 | try:
141 | print("{} - {}".format("Add new tag to the MQTT Agent", iot.iot_items.add_iot_item(server, iot_item_data, agent_name, agent_type)))
142 | except Exception as err:
143 | ErrorHandler(err)
144 |
145 | # Modify properties of the tag or IoT Item. If the "common.ALLTYPES_Name" is defined
146 | # the "modify_iot_item" function does not need have the agent name as an input
147 | modify_iot_item = {
148 | "common.ALLTYPES_NAME": iot_item_name,
149 | "iot_gateway.IOT_ITEM_SCAN_RATE_MS": 500
150 | }
151 | try:
152 | print("{} - {}".format("Modify the tag or IoT Item added", iot.iot_items.modify_iot_item(server, modify_iot_item, agent_name, agent_type)))
153 | except Exception as err:
154 | ErrorHandler(err)
155 |
156 | # Modify properties of the tag or IoT Item. (Version 2) It is not necessary to pass JSON
157 | # with the "common.ALLTYPES_Name" of the tag to modify. It can be passed as a input
158 | # for the "modify_iot_item" function. "Force" will force the
159 | # update to the Kepware Server, if "FORCE_UPDATE" not provided in the JSON data.
160 | modify_iot_item = {
161 | "iot_gateway.IOT_ITEM_SCAN_RATE_MS": 2000
162 | }
163 | try:
164 | print("{} - {}".format("Modify the tag or IoT Item added again", iot.iot_items.modify_iot_item(server, modify_iot_item, agent_name, agent_type, iot_item= iot_item_name, force = True)))
165 | except Exception as err:
166 | ErrorHandler(err)
167 |
168 | # Read the tag or IoT Item configured in the MQTT Agent
169 | try:
170 | print("{} - {}".format("Read the properties of the IoT Item", iot.iot_items.get_iot_item(server, iot_item_name, agent_name, agent_type)))
171 | except Exception as err:
172 | ErrorHandler(err)
173 |
174 | # Get a list of all tags or IoT Items configured in the MQTT Agent
175 | try:
176 | print("{} - {}".format("Get a list of all the IoT Items configured in the MQTT Agent", iot.iot_items.get_all_iot_items(server, agent_name, agent_type)))
177 | except Exception as err:
178 | ErrorHandler(err)
179 |
180 | # Delete a tag or IoT Item configured in the MQTT Agent
181 | try:
182 | print("{} - {}".format("Delete the IoT Item", iot.iot_items.del_iot_item(server, iot_item_name, agent_name, agent_type)))
183 | except Exception as err:
184 | ErrorHandler(err)
185 |
186 | # Delete the MQTT Agent
187 | try:
188 | print("{} - {}".format("Delete the MQTT Agent", iot.agent.del_iot_agent(server, agent_name, agent_type)))
189 | except Exception as err:
190 | ErrorHandler(err)
--------------------------------------------------------------------------------
/kepconfig/connectivity/channel.py:
--------------------------------------------------------------------------------
1 | # -------------------------------------------------------------------------
2 | # Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3 | # See License.txt in the project root for
4 | # license information.
5 | # --------------------------------------------------------------------------
6 |
7 |
8 | r"""`channel` exposes an API to allow modifications (add, delete, modify) to
9 | channel objects within the Kepware Configuration API
10 | """
11 |
12 |
13 | import inspect
14 | from ..connection import server
15 | from ..error import KepHTTPError, KepError
16 | from ..utils import _url_parse_object
17 | from typing import Union
18 | from . import device
19 |
20 | CHANNEL_ROOT = '/project/channels'
21 |
22 | def _create_url(channel = None):
23 | '''Creates url object for the "channel" branch of Kepware's project tree. Used
24 | to build a part of Kepware Configuration API URL structure
25 |
26 | Returns the channel specific url when a value is passed as the channel name.
27 | '''
28 |
29 | if channel == None:
30 | return CHANNEL_ROOT
31 | else:
32 | return '{}/{}'.format(CHANNEL_ROOT,_url_parse_object(channel))
33 |
34 | def add_channel(server: server, DATA: Union[dict, list]) -> Union[bool, list]:
35 | '''Add a `"channel"` or multiple `"channel"` objects to Kepware. Can be used to pass children of a channel object
36 | such as devices and tags/tag groups. This allows you to create a channel, it's devices and tags
37 | all in one function, if desired.
38 |
39 | Additionally it can be used to pass a list of channels and it's children to be added all at once.
40 |
41 | :param server: instance of the `server` class
42 | :param DATA: Dict of the channel and it's children
43 | expected by Kepware Configuration API
44 |
45 | :return: True - If a "HTTP 201 - Created" is received from Kepware server
46 | :return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
47 | channels added that failed.
48 |
49 | :raises KepHTTPError: If urllib provides an HTTPError
50 | :raises KepURLError: If urllib provides an URLError
51 | '''
52 |
53 | r = server._config_add(server.url + _create_url(), DATA)
54 | if r.code == 201: return True
55 | elif r.code == 207:
56 | errors = []
57 | for item in r.payload:
58 | if item['code'] != 201:
59 | errors.append(item)
60 | return errors
61 | else:
62 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
63 |
64 | def del_channel(server: server, channel: str) -> bool:
65 | '''Delete a `"channel"` object in Kepware. This will delete all children as well
66 |
67 | :param server: instance of the `server` class
68 | :param channel: name of channel
69 |
70 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
71 |
72 | :raises KepHTTPError: If urllib provides an HTTPError
73 | :raises KepURLError: If urllib provides an URLError
74 | '''
75 |
76 | r = server._config_del(server.url + _create_url(channel))
77 | if r.code == 200: return True
78 | else:
79 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
80 |
81 | def modify_channel(server: server, DATA: dict, *, channel: str = None, force: bool = False) -> bool:
82 | '''Modify a channel object and it's properties in Kepware. If a `"channel"` is not provided as an input,
83 | you need to identify the channel in the *'common.ALLTYPES_NAME'* property field in `"DATA"`. It will
84 | assume that is the channel that is to be modified.
85 |
86 | :param server: instance of the `server` class
87 | :param DATA: Dict of the `channel` properties to be modified
88 | :param channel: *(optional)* name of channel to modify. Only needed if not existing in `"DATA"`
89 | :param force: *(optional)* if True, will force the configuration update to the Kepware server
90 |
91 | :return: True - If a "HTTP 200 - OK" is received from Kepware server
92 |
93 | :raises KepHTTPError: If urllib provides an HTTPError
94 | :raises KepURLError: If urllib provides an URLError
95 | '''
96 |
97 | channel_data = server._force_update_check(force, DATA)
98 | if channel == None:
99 | try:
100 | r = server._config_update(server.url + _create_url(channel_data['common.ALLTYPES_NAME']), channel_data)
101 | if r.code == 200: return True
102 | else: raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
103 | except KeyError as err:
104 | err_msg = 'Error: No Channel identified in DATA | Key Error: {}'.format(err)
105 | raise KepError(err_msg)
106 | # except Exception as e:
107 | # return 'Error: Error with {}: {}'.format(inspect.currentframe().f_code.co_name, str(e))
108 | else:
109 | r = server._config_update(server.url + _create_url(channel), channel_data)
110 | if r.code == 200: return True
111 | else:
112 | raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
113 |
114 | def get_channel(server: server, channel: str) -> dict:
115 | '''Returns the properties of the channel object.
116 |
117 | :param server: instance of the `server` class
118 | :param channel: name of channel
119 |
120 | :return: Dict of data for the channel requested
121 |
122 | :raises KepHTTPError: If urllib provides an HTTPError
123 | :raises KepURLError: If urllib provides an URLError
124 | '''
125 |
126 | r = server._config_get(server.url + _create_url(channel))
127 | return r.payload
128 |
129 | def get_all_channels(server: server, *, options: dict = None) -> list:
130 | '''Returns list of all channel objects and their properties.
131 |
132 | :param server: instance of the `server` class
133 | :param options: *(optional)* Dict of parameters to filter, sort or pagenate the list of channels. Options are `filter`,
134 | `sortOrder`, `sortProperty`, `pageNumber`, and `pageSize`
135 |
136 | :return: List of data for all channels in Kepware server
137 |
138 | :raises KepHTTPError: If urllib provides an HTTPError
139 | :raises KepURLError: If urllib provides an URLError
140 | '''
141 |
142 | r = server._config_get(server.url + _create_url(), params= options)
143 | return r.payload
144 |
145 | def get_channel_structure(server: server, channel: str) -> dict:
146 | '''Returns the properties of `"channel"` and includes all `"devices"` and the `"tag"` and `"tag group"` objects for a
147 | channel in Kepware. Returned object is a dict of channel properties including a device list with
148 | tag lists and tag group lists.
149 |
150 | The returned object resembles the example below, nested based on how many
151 | levels the tag_group namespace has tags or tag_groups:
152 |
153 | Example return:
154 |
155 | {
156 | channel_properties,
157 | 'devices: [
158 | {
159 | device1_properties,
160 | 'tags': [tag1_dict, tag2_dict,...],
161 | 'tag_groups':[
162 | {
163 | tag_group1_properties,
164 | 'tags': [tag1_dict, tag2_dict,...]
165 | 'tag_groups':[sub_group1, subgroup2,...]
166 | },
167 | {
168 | tag_group2_properties,
169 | 'tags': [tag1_dict, tag2_dict,...]
170 | 'tag_groups':[sub_group1, subgroup2,...]
171 | },...]
172 | },...]
173 | }
174 |
175 | :param server: instance of the `server` class
176 | :param channel: name of channel
177 |
178 | :return: Dict of data for the channel structure requested for `"channel"`
179 |
180 | :raises KepHTTPError: If urllib provides an HTTPError
181 | :raises KepURLError: If urllib provides an URLError
182 | '''
183 |
184 | channel_properties = get_channel(server, channel)
185 | device_list = device.get_all_devices(server,channel)
186 | for dev in device_list:
187 | device_properties = []
188 | dev_struct = device.get_device_structure(server,channel + '.' + dev['common.ALLTYPES_NAME'])
189 | device_properties.append(dev_struct)
190 | return {**channel_properties,'device': device_properties}
--------------------------------------------------------------------------------