enapter 0.6.3__py3-none-any.whl → 0.12.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- enapter/__init__.py +4 -4
- enapter/async_/__init__.py +1 -4
- enapter/async_/generator.py +5 -2
- enapter/async_/routine.py +21 -53
- enapter/http/__init__.py +3 -0
- enapter/http/api/__init__.py +5 -0
- enapter/http/api/client.py +31 -0
- enapter/http/api/config.py +23 -0
- enapter/http/api/devices/__init__.py +21 -0
- enapter/http/api/devices/authorized_role.py +11 -0
- enapter/http/api/devices/client.py +25 -0
- enapter/http/api/devices/communication_config.py +45 -0
- enapter/http/api/devices/device.py +32 -0
- enapter/http/api/devices/device_type.py +10 -0
- enapter/http/api/devices/mqtt_credentials.py +13 -0
- enapter/http/api/devices/mqtt_protocol.py +7 -0
- enapter/http/api/devices/mqtts_credentials.py +18 -0
- enapter/http/api/devices/time_sync_protocol.py +6 -0
- enapter/log/json_formatter.py +14 -4
- enapter/mdns/resolver.py +14 -11
- enapter/mqtt/__init__.py +5 -12
- enapter/mqtt/api/__init__.py +6 -0
- enapter/mqtt/api/client.py +62 -0
- enapter/mqtt/api/config.py +77 -0
- enapter/mqtt/api/device/__init__.py +21 -0
- enapter/mqtt/api/device/channel.py +56 -0
- enapter/mqtt/api/device/command_request.py +38 -0
- enapter/mqtt/api/device/command_response.py +28 -0
- enapter/mqtt/api/device/command_state.py +8 -0
- enapter/mqtt/api/device/log.py +31 -0
- enapter/mqtt/api/device/log_severity.py +9 -0
- enapter/mqtt/api/device/message.py +24 -0
- enapter/mqtt/api/device/properties.py +24 -0
- enapter/mqtt/api/device/telemetry.py +28 -0
- enapter/mqtt/client.py +88 -119
- enapter/mqtt/errors.py +3 -0
- enapter/mqtt/message.py +3 -0
- enapter/standalone/__init__.py +25 -0
- enapter/standalone/config.py +218 -0
- enapter/standalone/device.py +59 -0
- enapter/standalone/device_protocol.py +33 -0
- enapter/standalone/logger.py +21 -0
- enapter/standalone/mqtt_adapter.py +134 -0
- enapter/standalone/run.py +39 -0
- enapter/standalone/ucm.py +28 -0
- enapter-0.12.1.dist-info/METADATA +74 -0
- enapter-0.12.1.dist-info/RECORD +52 -0
- {enapter-0.6.3.dist-info → enapter-0.12.1.dist-info}/WHEEL +1 -1
- {enapter-0.6.3.dist-info → enapter-0.12.1.dist-info}/top_level.txt +0 -1
- enapter/mqtt/command.py +0 -45
- enapter/mqtt/config.py +0 -48
- enapter/mqtt/device_channel.py +0 -83
- enapter/types.py +0 -3
- enapter/vucm/__init__.py +0 -12
- enapter/vucm/app.py +0 -48
- enapter/vucm/config.py +0 -59
- enapter/vucm/device.py +0 -92
- enapter/vucm/logger.py +0 -39
- enapter/vucm/ucm.py +0 -30
- enapter-0.6.3.dist-info/METADATA +0 -111
- enapter-0.6.3.dist-info/RECORD +0 -27
- tests/test_async.py +0 -152
- tests/test_log.py +0 -69
- /tests/__init__.py → /enapter/py.typed +0 -0
enapter/mqtt/command.py
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import enum
|
|
2
|
-
import json
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class CommandState(enum.Enum):
|
|
6
|
-
COMPLETED = "completed"
|
|
7
|
-
ERROR = "error"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class CommandRequest:
|
|
11
|
-
@classmethod
|
|
12
|
-
def unmarshal_json(cls, data):
|
|
13
|
-
req = json.loads(data)
|
|
14
|
-
return cls(id_=req["id"], name=req["name"], args=req.get("arguments"))
|
|
15
|
-
|
|
16
|
-
def __init__(self, id_, name, args=None):
|
|
17
|
-
self.id = id_
|
|
18
|
-
self.name = name
|
|
19
|
-
|
|
20
|
-
if args is None:
|
|
21
|
-
args = {}
|
|
22
|
-
self.args = args
|
|
23
|
-
|
|
24
|
-
def new_response(self, *args, **kwargs):
|
|
25
|
-
return CommandResponse(self.id, *args, **kwargs)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class CommandResponse:
|
|
29
|
-
def __init__(self, id_, state, payload=None):
|
|
30
|
-
self.id = id_
|
|
31
|
-
|
|
32
|
-
if not isinstance(state, CommandState):
|
|
33
|
-
state = CommandState(state)
|
|
34
|
-
self.state = state
|
|
35
|
-
|
|
36
|
-
if not isinstance(payload, dict):
|
|
37
|
-
payload = {"message": payload}
|
|
38
|
-
self.payload = payload
|
|
39
|
-
|
|
40
|
-
def json(self):
|
|
41
|
-
json_object = {"id": self.id, "state": self.state.value}
|
|
42
|
-
if self.payload is not None:
|
|
43
|
-
json_object["payload"] = self.payload
|
|
44
|
-
|
|
45
|
-
return json_object
|
enapter/mqtt/config.py
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class Config:
|
|
5
|
-
@classmethod
|
|
6
|
-
def from_env(cls, prefix="ENAPTER_", env=os.environ):
|
|
7
|
-
def pem(value):
|
|
8
|
-
if value is None:
|
|
9
|
-
return value
|
|
10
|
-
return value.replace("\\n", "\n")
|
|
11
|
-
|
|
12
|
-
return cls(
|
|
13
|
-
host=env[prefix + "MQTT_HOST"],
|
|
14
|
-
port=int(env[prefix + "MQTT_PORT"]),
|
|
15
|
-
user=env.get(prefix + "MQTT_USER", default=None),
|
|
16
|
-
password=env.get(prefix + "MQTT_PASSWORD", default=None),
|
|
17
|
-
tls_secret_key=pem(env.get(prefix + "MQTT_TLS_SECRET_KEY", default=None)),
|
|
18
|
-
tls_cert=pem(env.get(prefix + "MQTT_TLS_CERT", default=None)),
|
|
19
|
-
tls_ca_cert=pem(env.get(prefix + "MQTT_TLS_CA_CERT", default=None)),
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
def __init__(
|
|
23
|
-
self,
|
|
24
|
-
host,
|
|
25
|
-
port,
|
|
26
|
-
user=None,
|
|
27
|
-
password=None,
|
|
28
|
-
tls_secret_key=None,
|
|
29
|
-
tls_cert=None,
|
|
30
|
-
tls_ca_cert=None,
|
|
31
|
-
):
|
|
32
|
-
self.host = host
|
|
33
|
-
self.port = port
|
|
34
|
-
self.user = user
|
|
35
|
-
self.password = password
|
|
36
|
-
|
|
37
|
-
self.tls_secret_key = tls_secret_key
|
|
38
|
-
self.tls_cert = tls_cert
|
|
39
|
-
self.tls_ca_cert = tls_ca_cert
|
|
40
|
-
|
|
41
|
-
self.tls_enabled = {tls_secret_key, tls_cert, tls_ca_cert} != {None}
|
|
42
|
-
|
|
43
|
-
def __repr__(self):
|
|
44
|
-
return "mqtt.Config(host=%r, port=%r, tls_enabled=%r)" % (
|
|
45
|
-
self.host,
|
|
46
|
-
self.port,
|
|
47
|
-
self.tls_enabled,
|
|
48
|
-
)
|
enapter/mqtt/device_channel.py
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import enum
|
|
2
|
-
import json
|
|
3
|
-
import logging
|
|
4
|
-
import time
|
|
5
|
-
|
|
6
|
-
from .. import async_
|
|
7
|
-
from .command import CommandRequest
|
|
8
|
-
|
|
9
|
-
LOGGER = logging.getLogger(__name__)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class DeviceChannel:
|
|
13
|
-
def __init__(self, client, hardware_id, channel_id):
|
|
14
|
-
self._client = client
|
|
15
|
-
self._logger = self._new_logger(hardware_id, channel_id)
|
|
16
|
-
self._hardware_id = hardware_id
|
|
17
|
-
self._channel_id = channel_id
|
|
18
|
-
|
|
19
|
-
@property
|
|
20
|
-
def hardware_id(self):
|
|
21
|
-
return self._hardware_id
|
|
22
|
-
|
|
23
|
-
@property
|
|
24
|
-
def channel_id(self):
|
|
25
|
-
return self._channel_id
|
|
26
|
-
|
|
27
|
-
@staticmethod
|
|
28
|
-
def _new_logger(hardware_id, channel_id):
|
|
29
|
-
extra = {"hardware_id": hardware_id, "channel_id": channel_id}
|
|
30
|
-
return logging.LoggerAdapter(LOGGER, extra=extra)
|
|
31
|
-
|
|
32
|
-
@async_.generator
|
|
33
|
-
async def subscribe_to_command_requests(self):
|
|
34
|
-
async with self._subscribe("v1/command/requests") as messages:
|
|
35
|
-
async for msg in messages:
|
|
36
|
-
yield CommandRequest.unmarshal_json(msg.payload)
|
|
37
|
-
|
|
38
|
-
async def publish_command_response(self, resp):
|
|
39
|
-
await self._publish_json("v1/command/responses", resp.json())
|
|
40
|
-
|
|
41
|
-
async def publish_telemetry(self, telemetry, **kwargs):
|
|
42
|
-
await self._publish_json("v1/telemetry", telemetry, **kwargs)
|
|
43
|
-
|
|
44
|
-
async def publish_properties(self, properties, **kwargs):
|
|
45
|
-
await self._publish_json("v1/register", properties, **kwargs)
|
|
46
|
-
|
|
47
|
-
async def publish_logs(self, msg, severity, persist=False, **kwargs):
|
|
48
|
-
logs = {
|
|
49
|
-
"message": msg,
|
|
50
|
-
"severity": severity.value,
|
|
51
|
-
}
|
|
52
|
-
if persist:
|
|
53
|
-
logs["persist"] = True
|
|
54
|
-
|
|
55
|
-
await self._publish_json("v3/logs", logs, **kwargs)
|
|
56
|
-
|
|
57
|
-
def _subscribe(self, path):
|
|
58
|
-
topic = f"v1/to/{self._hardware_id}/{self._channel_id}/{path}"
|
|
59
|
-
return self._client.subscribe(topic)
|
|
60
|
-
|
|
61
|
-
async def _publish_json(self, path, json_object, **kwargs):
|
|
62
|
-
if "timestamp" in json_object:
|
|
63
|
-
raise ValueError("`timestamp` field is reserved")
|
|
64
|
-
|
|
65
|
-
json_object["timestamp"] = int(time.time())
|
|
66
|
-
payload = json.dumps(json_object)
|
|
67
|
-
|
|
68
|
-
await self._publish(path, payload, **kwargs)
|
|
69
|
-
|
|
70
|
-
async def _publish(self, path, payload, **kwargs):
|
|
71
|
-
topic = f"v1/from/{self._hardware_id}/{self._channel_id}/{path}"
|
|
72
|
-
try:
|
|
73
|
-
await self._client.publish(topic, payload, **kwargs)
|
|
74
|
-
except Exception as e:
|
|
75
|
-
self._logger.error("failed to publish %s: %r", path, e)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
class DeviceLogSeverity(enum.Enum):
|
|
79
|
-
|
|
80
|
-
DEBUG = "debug"
|
|
81
|
-
INFO = "info"
|
|
82
|
-
WARNING = "warning"
|
|
83
|
-
ERROR = "error"
|
enapter/types.py
DELETED
enapter/vucm/__init__.py
DELETED
enapter/vucm/app.py
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
|
|
3
|
-
from .. import async_, log, mqtt
|
|
4
|
-
from .config import Config
|
|
5
|
-
from .ucm import UCM
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
async def run(device_factory):
|
|
9
|
-
log.configure(level=log.LEVEL or "info")
|
|
10
|
-
|
|
11
|
-
config = Config.from_env()
|
|
12
|
-
|
|
13
|
-
async with App(config=config, device_factory=device_factory) as app:
|
|
14
|
-
await app.join()
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class App(async_.Routine):
|
|
18
|
-
def __init__(self, config, device_factory):
|
|
19
|
-
self._config = config
|
|
20
|
-
self._device_factory = device_factory
|
|
21
|
-
|
|
22
|
-
async def _run(self):
|
|
23
|
-
tasks = set()
|
|
24
|
-
|
|
25
|
-
mqtt_client = await self._stack.enter_async_context(
|
|
26
|
-
mqtt.Client(config=self._config.mqtt)
|
|
27
|
-
)
|
|
28
|
-
tasks.add(mqtt_client.task())
|
|
29
|
-
|
|
30
|
-
if self._config.start_ucm:
|
|
31
|
-
ucm = await self._stack.enter_async_context(
|
|
32
|
-
UCM(mqtt_client=mqtt_client, hardware_id=self._config.hardware_id)
|
|
33
|
-
)
|
|
34
|
-
tasks.add(ucm.task())
|
|
35
|
-
|
|
36
|
-
device = await self._stack.enter_async_context(
|
|
37
|
-
self._device_factory(
|
|
38
|
-
channel=mqtt_client.device_channel(
|
|
39
|
-
hardware_id=self._config.hardware_id,
|
|
40
|
-
channel_id=self._config.channel_id,
|
|
41
|
-
)
|
|
42
|
-
)
|
|
43
|
-
)
|
|
44
|
-
tasks.add(device.task())
|
|
45
|
-
|
|
46
|
-
self._started.set()
|
|
47
|
-
|
|
48
|
-
await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
|
enapter/vucm/config.py
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import base64
|
|
2
|
-
import json
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
|
-
from .. import mqtt
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class Config:
|
|
9
|
-
@classmethod
|
|
10
|
-
def from_env(cls, prefix="ENAPTER_VUCM_", env=os.environ):
|
|
11
|
-
try:
|
|
12
|
-
blob = os.environ[prefix + "BLOB"]
|
|
13
|
-
except KeyError:
|
|
14
|
-
pass
|
|
15
|
-
else:
|
|
16
|
-
config = cls.from_blob(blob)
|
|
17
|
-
try:
|
|
18
|
-
config.channel_id = os.environ[prefix + "CHANNEL_ID"]
|
|
19
|
-
except KeyError:
|
|
20
|
-
pass
|
|
21
|
-
return config
|
|
22
|
-
|
|
23
|
-
hardware_id = os.environ[prefix + "HARDWARE_ID"]
|
|
24
|
-
channel_id = os.environ[prefix + "CHANNEL_ID"]
|
|
25
|
-
|
|
26
|
-
mqtt_config = mqtt.Config.from_env(prefix=prefix, env=env)
|
|
27
|
-
|
|
28
|
-
start_ucm = os.environ.get(prefix + "START_UCM", "1") != "0"
|
|
29
|
-
|
|
30
|
-
return cls(
|
|
31
|
-
hardware_id=hardware_id,
|
|
32
|
-
channel_id=channel_id,
|
|
33
|
-
mqtt_config=mqtt_config,
|
|
34
|
-
start_ucm=start_ucm,
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
@classmethod
|
|
38
|
-
def from_blob(cls, blob):
|
|
39
|
-
payload = json.loads(base64.b64decode(blob))
|
|
40
|
-
|
|
41
|
-
mqtt_config = mqtt.Config(
|
|
42
|
-
host=payload["mqtt_host"],
|
|
43
|
-
port=int(payload["mqtt_port"]),
|
|
44
|
-
tls_ca_cert=payload["mqtt_ca"],
|
|
45
|
-
tls_cert=payload["mqtt_cert"],
|
|
46
|
-
tls_secret_key=payload["mqtt_private_key"],
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
return cls(
|
|
50
|
-
hardware_id=payload["ucm_id"],
|
|
51
|
-
channel_id=payload["channel_id"],
|
|
52
|
-
mqtt_config=mqtt_config,
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
def __init__(self, hardware_id, channel_id, mqtt_config, start_ucm=True):
|
|
56
|
-
self.hardware_id = hardware_id
|
|
57
|
-
self.channel_id = channel_id
|
|
58
|
-
self.mqtt = mqtt_config
|
|
59
|
-
self.start_ucm = start_ucm
|
enapter/vucm/device.py
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import traceback
|
|
3
|
-
from typing import Optional, Set
|
|
4
|
-
|
|
5
|
-
from .. import async_, mqtt, types
|
|
6
|
-
from .logger import Logger
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Device(async_.Routine):
|
|
10
|
-
def __init__(self, channel, cmd_prefix="cmd_", task_prefix="task_") -> None:
|
|
11
|
-
self.__channel = channel
|
|
12
|
-
|
|
13
|
-
self.__cmd_prefix = cmd_prefix
|
|
14
|
-
self.__task_prefix = task_prefix
|
|
15
|
-
|
|
16
|
-
self.log = Logger(channel=channel)
|
|
17
|
-
self.alerts: Set[str] = set()
|
|
18
|
-
|
|
19
|
-
async def send_telemetry(self, telemetry: Optional[types.JSON] = None) -> None:
|
|
20
|
-
if telemetry is None:
|
|
21
|
-
telemetry = {}
|
|
22
|
-
else:
|
|
23
|
-
telemetry = telemetry.copy()
|
|
24
|
-
|
|
25
|
-
telemetry.setdefault("alerts", list(self.alerts))
|
|
26
|
-
|
|
27
|
-
await self.__channel.publish_telemetry(telemetry)
|
|
28
|
-
|
|
29
|
-
async def send_properties(self, properties: Optional[types.JSON] = None) -> None:
|
|
30
|
-
if properties is None:
|
|
31
|
-
properties = {}
|
|
32
|
-
else:
|
|
33
|
-
properties = properties.copy()
|
|
34
|
-
|
|
35
|
-
await self.__channel.publish_properties(properties)
|
|
36
|
-
|
|
37
|
-
async def _run(self):
|
|
38
|
-
tasks = set()
|
|
39
|
-
|
|
40
|
-
for name in dir(self):
|
|
41
|
-
if name.startswith(self.__task_prefix):
|
|
42
|
-
task_func = getattr(self, name)
|
|
43
|
-
name_without_prefix = name[len(self.__task_prefix) :]
|
|
44
|
-
tasks.add(asyncio.create_task(task_func(), name=name_without_prefix))
|
|
45
|
-
|
|
46
|
-
tasks.add(
|
|
47
|
-
asyncio.create_task(
|
|
48
|
-
self.__process_command_requests(), name="command_requests_processor"
|
|
49
|
-
)
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
self._started.set()
|
|
53
|
-
|
|
54
|
-
try:
|
|
55
|
-
await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
|
|
56
|
-
except asyncio.CancelledError:
|
|
57
|
-
pass
|
|
58
|
-
|
|
59
|
-
finally:
|
|
60
|
-
for task in tasks:
|
|
61
|
-
task.cancel()
|
|
62
|
-
self._stack.push_async_callback(self.__wait_task, task)
|
|
63
|
-
|
|
64
|
-
async def __wait_task(self, task):
|
|
65
|
-
try:
|
|
66
|
-
await task
|
|
67
|
-
except asyncio.CancelledError:
|
|
68
|
-
pass
|
|
69
|
-
except Exception as e:
|
|
70
|
-
try:
|
|
71
|
-
await self.log.error(f"device task {task.get_name()!r} failed: {e!r}")
|
|
72
|
-
except:
|
|
73
|
-
pass
|
|
74
|
-
raise
|
|
75
|
-
|
|
76
|
-
async def __process_command_requests(self):
|
|
77
|
-
async with self.__channel.subscribe_to_command_requests() as reqs:
|
|
78
|
-
async for req in reqs:
|
|
79
|
-
state, payload = await self.__execute_command(req)
|
|
80
|
-
resp = req.new_response(state, payload)
|
|
81
|
-
await self.__channel.publish_command_response(resp)
|
|
82
|
-
|
|
83
|
-
async def __execute_command(self, req):
|
|
84
|
-
try:
|
|
85
|
-
cmd = getattr(self, self.__cmd_prefix + req.name)
|
|
86
|
-
except AttributeError:
|
|
87
|
-
return mqtt.CommandState.ERROR, {"reason": "unknown command"}
|
|
88
|
-
|
|
89
|
-
try:
|
|
90
|
-
return mqtt.CommandState.COMPLETED, await cmd(**req.args)
|
|
91
|
-
except:
|
|
92
|
-
return mqtt.CommandState.ERROR, {"traceback": traceback.format_exc()}
|
enapter/vucm/logger.py
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
|
-
from .. import mqtt
|
|
4
|
-
|
|
5
|
-
LOGGER = logging.getLogger(__name__)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class Logger:
|
|
9
|
-
def __init__(self, channel):
|
|
10
|
-
self._channel = channel
|
|
11
|
-
self._logger = self._new_logger(channel.hardware_id, channel.channel_id)
|
|
12
|
-
|
|
13
|
-
@staticmethod
|
|
14
|
-
def _new_logger(hardware_id, channel_id):
|
|
15
|
-
extra = {"hardware_id": hardware_id, "channel_id": channel_id}
|
|
16
|
-
return logging.LoggerAdapter(LOGGER, extra=extra)
|
|
17
|
-
|
|
18
|
-
async def debug(self, msg: str, persist: bool = False):
|
|
19
|
-
self._logger.debug(msg)
|
|
20
|
-
await self.log(msg, severity=mqtt.DeviceLogSeverity.DEBUG, persist=persist)
|
|
21
|
-
|
|
22
|
-
async def info(self, msg: str, persist: bool = False):
|
|
23
|
-
self._logger.info(msg)
|
|
24
|
-
await self.log(msg, severity=mqtt.DeviceLogSeverity.INFO, persist=persist)
|
|
25
|
-
|
|
26
|
-
async def warning(self, msg: str, persist: bool = False):
|
|
27
|
-
self._logger.warning(msg)
|
|
28
|
-
await self.log(msg, severity=mqtt.DeviceLogSeverity.WARNING, persist=persist)
|
|
29
|
-
|
|
30
|
-
async def error(self, msg: str, persist: bool = False):
|
|
31
|
-
self._logger.error(msg)
|
|
32
|
-
await self.log(msg, severity=mqtt.DeviceLogSeverity.ERROR, persist=persist)
|
|
33
|
-
|
|
34
|
-
async def log(
|
|
35
|
-
self, msg: str, severity: mqtt.DeviceLogSeverity, persist: bool = False
|
|
36
|
-
):
|
|
37
|
-
await self._channel.publish_logs(msg=msg, severity=severity, persist=persist)
|
|
38
|
-
|
|
39
|
-
__call__ = log
|
enapter/vucm/ucm.py
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
|
|
3
|
-
from .device import Device
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class UCM(Device):
|
|
7
|
-
def __init__(self, mqtt_client, hardware_id):
|
|
8
|
-
super().__init__(
|
|
9
|
-
channel=mqtt_client.device_channel(
|
|
10
|
-
hardware_id=hardware_id, channel_id="ucm"
|
|
11
|
-
)
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
async def cmd_reboot(self):
|
|
15
|
-
await asyncio.sleep(0)
|
|
16
|
-
raise NotImplementedError
|
|
17
|
-
|
|
18
|
-
async def cmd_upload_lua_script(self, url, sha1, payload=None):
|
|
19
|
-
await asyncio.sleep(0)
|
|
20
|
-
raise NotImplementedError
|
|
21
|
-
|
|
22
|
-
async def task_telemetry_publisher(self):
|
|
23
|
-
while True:
|
|
24
|
-
await self.send_telemetry()
|
|
25
|
-
await asyncio.sleep(1)
|
|
26
|
-
|
|
27
|
-
async def task_properties_publisher(self):
|
|
28
|
-
while True:
|
|
29
|
-
await self.send_properties({"virtual": True, "lua_api_ver": 1})
|
|
30
|
-
await asyncio.sleep(10)
|
enapter-0.6.3.dist-info/METADATA
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: enapter
|
|
3
|
-
Version: 0.6.3
|
|
4
|
-
Summary: Enapter Python SDK
|
|
5
|
-
Home-page: https://github.com/Enapter/python-sdk
|
|
6
|
-
Author: Roman Novatorov
|
|
7
|
-
Author-email: rnovatorov@enapter.com
|
|
8
|
-
Description-Content-Type: text/markdown
|
|
9
|
-
Requires-Dist: aiohttp (==3.8.*)
|
|
10
|
-
Requires-Dist: asyncio-mqtt (==0.12.*)
|
|
11
|
-
Requires-Dist: dnspython (==2.2.*)
|
|
12
|
-
Requires-Dist: json-log-formatter (==0.5.*)
|
|
13
|
-
|
|
14
|
-
# Enapter Python SDK
|
|
15
|
-
|
|
16
|
-
[](https://github.com/Enapter/python-sdk/actions/workflows/ci.yml)
|
|
17
|
-
[](https://pypi.org/project/enapter)
|
|
18
|
-
[](https://github.com/python/black)
|
|
19
|
-
|
|
20
|
-
Enapter software development kit for Python.
|
|
21
|
-
|
|
22
|
-
:warning: **This project is work in progress. The API is not stable and may change at any time.** :warning:
|
|
23
|
-
|
|
24
|
-
## Installation
|
|
25
|
-
|
|
26
|
-
Stable from PyPI:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
pip install enapter
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Latest for GitHub:
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
pip install git+https://github.com/Enapter/python-sdk#egg=enapter
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Usage
|
|
39
|
-
|
|
40
|
-
Checkout [examples](examples).
|
|
41
|
-
|
|
42
|
-
## Implementing your own VUCM
|
|
43
|
-
|
|
44
|
-
### Device Telemetry and Properties
|
|
45
|
-
|
|
46
|
-
Every method of `enapter.vucm.Device` subclass with a name that starts with
|
|
47
|
-
`task_` prefix is considered a _device task_. When such a device is started,
|
|
48
|
-
all of its tasks are started as well. Device tasks are started in random order
|
|
49
|
-
and are being executed concurrently in the background. If a device task returns
|
|
50
|
-
or raises an exception, device routine is terminated. A typical use of the task
|
|
51
|
-
is to run a periodic job to send device telemetry and properties.
|
|
52
|
-
|
|
53
|
-
In order to send telemetry and properties define two corresponding device
|
|
54
|
-
tasks. It is advised (but is not obligatory) to send telemetry every **1
|
|
55
|
-
second** and to send properties every **10 seconds**.
|
|
56
|
-
|
|
57
|
-
Examples:
|
|
58
|
-
|
|
59
|
-
- [wttr-in](examples/vucm/wttr-in)
|
|
60
|
-
|
|
61
|
-
### Device Commands
|
|
62
|
-
|
|
63
|
-
Every method of `enapter.vucm.Device` subclass with a name that starts with
|
|
64
|
-
`cmd_` prefix is considered a _device command handler_. Device command handlers
|
|
65
|
-
receive the same arguments as described in device Blueprint manifest and can
|
|
66
|
-
optionally return a payload as `dict`.
|
|
67
|
-
|
|
68
|
-
In order to handle device commands define corresponding device command
|
|
69
|
-
handlers.
|
|
70
|
-
|
|
71
|
-
Examples:
|
|
72
|
-
|
|
73
|
-
- [zhimi-fan-za5](examples/vucm/zhimi-fan-za5)
|
|
74
|
-
|
|
75
|
-
### Device Alerts
|
|
76
|
-
|
|
77
|
-
Device alerts are stored in `self.alerts`. It is a usual Python `set`, so you
|
|
78
|
-
can add an alert using `alerts.add`, remove an alert `alerts.remove` and clear
|
|
79
|
-
alerts using `alerts.clear`.
|
|
80
|
-
|
|
81
|
-
Alerts are sent only as part of telemetry, so in order to report device alert,
|
|
82
|
-
use `send_telemetry` with any payload.
|
|
83
|
-
|
|
84
|
-
## Running your own VUCM via Docker
|
|
85
|
-
|
|
86
|
-
A simple Dockerfile can be:
|
|
87
|
-
|
|
88
|
-
```
|
|
89
|
-
FROM python:3.10-alpine3.16
|
|
90
|
-
|
|
91
|
-
WORKDIR /app
|
|
92
|
-
|
|
93
|
-
RUN python -m venv .venv
|
|
94
|
-
COPY requirements.txt requirements.txt
|
|
95
|
-
RUN .venv/bin/pip install -r requirements.txt
|
|
96
|
-
|
|
97
|
-
COPY script.py script.py
|
|
98
|
-
|
|
99
|
-
CMD [".venv/bin/python", "script.py"]
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
:information_source: If you are using [Enapter
|
|
103
|
-
Gateway](https://handbook.enapter.com/software/gateway_software/), you should
|
|
104
|
-
connect your containers to `host` network :information_source::
|
|
105
|
-
|
|
106
|
-
```bash
|
|
107
|
-
docker run --network host ...
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
:warning: Host networking is not supported on Mac and Windows, so Docker VUCMs
|
|
111
|
-
are at the moment only available on Linux.
|
enapter-0.6.3.dist-info/RECORD
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
enapter/__init__.py,sha256=N95xA1SYof-WTNf_q5WfZIHXHHW4Tjx7Ezd_bNzlmk8,182
|
|
2
|
-
enapter/types.py,sha256=2366bx_VI7bIBNnIaG5sol7G4k0PqaQ6oS74dzM4l1M,52
|
|
3
|
-
enapter/async_/__init__.py,sha256=JuiRI2bN2AgB-HLfAUoSsZpEziwFRftNNEp59Evnd0M,109
|
|
4
|
-
enapter/async_/generator.py,sha256=qBhnt36Gl2166sJFnZHsREbZu8l43M4DfxybUMIv6W4,300
|
|
5
|
-
enapter/async_/routine.py,sha256=A5fG4XnCEQT0Qa_JNh1N43Fv5lnLaCoGF4xt6pOAdNs,1770
|
|
6
|
-
enapter/log/__init__.py,sha256=n1sWMDKJSs_ebZzjbTrVdfg-oi0V1tvliTxgIV-msJ0,600
|
|
7
|
-
enapter/log/json_formatter.py,sha256=P46zEdU5-dr3mDMkUjBy7z_FSMPi61RUgEYyBEfqVNQ,705
|
|
8
|
-
enapter/mdns/__init__.py,sha256=uwsg8wJ0Lcsr9oOEW1PkEV3jVgWzgA7RG2ur_MRLtM0,55
|
|
9
|
-
enapter/mdns/resolver.py,sha256=31zQz8LsUXKorh39QGqzknZBWpu2krBySm2xcCNYVPo,1159
|
|
10
|
-
enapter/mqtt/__init__.py,sha256=D_RK-cScavukvDvwahEwvlvGLHSPsVLjG_JpdvpC2ok,336
|
|
11
|
-
enapter/mqtt/client.py,sha256=qqAm8G5Lo8HUHOJPkyMmndijXWKIP8gN1hY_rMPrbzY,4975
|
|
12
|
-
enapter/mqtt/command.py,sha256=ozhDTjRrdCWv_bzPTjVFpL8tx7nhirm3JtQaD45wTdo,1092
|
|
13
|
-
enapter/mqtt/config.py,sha256=Bng9A271vXMg1cNZ1A0lWXVKQUzun8fQHLqqt8AMXNw,1399
|
|
14
|
-
enapter/mqtt/device_channel.py,sha256=BrAuzrNITd3t6XRchOcO7gLY05m9C1Wv8Obrn6uCtsc,2556
|
|
15
|
-
enapter/vucm/__init__.py,sha256=OhUYlVNO1RereHiZEyeJPdpEcy2QEqDBg4xBk_-A7-c,177
|
|
16
|
-
enapter/vucm/app.py,sha256=UmwFrercm2MWs7ZUBRAAjNQ-74gQfklA5puEROe2ba8,1325
|
|
17
|
-
enapter/vucm/config.py,sha256=ir5EQwniN2vNuMbLS1YpXh9mSyHXALk3j8kOwxdoGHo,1649
|
|
18
|
-
enapter/vucm/device.py,sha256=JMer-q19nuBCyXbvvYAoQuRGrETUA7p7Hwk3mWIzOCk,2923
|
|
19
|
-
enapter/vucm/logger.py,sha256=nzcb8y5jEsykGxrLLgwkkx_cU4-hOhsyVxA5Mqv3zqo,1354
|
|
20
|
-
enapter/vucm/ucm.py,sha256=tcHy-Lkv5XhwwcgOiG6hsNNT1CzEKISNqWOJiZxjeIk,824
|
|
21
|
-
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
-
tests/test_async.py,sha256=KwhwKBSeSb7Qyaf50Ca2LGB7gm3m5j5wgGgWnvYY98k,4208
|
|
23
|
-
tests/test_log.py,sha256=DQD1OCVkYYzy-Kq-jH_xsaRDtiwIqULuTgMitjSeiuo,1876
|
|
24
|
-
enapter-0.6.3.dist-info/METADATA,sha256=nKgQF-xwZfiHTBHpR8SVZkDsWvsBVVxXfzuD6tiom1w,3303
|
|
25
|
-
enapter-0.6.3.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
|
26
|
-
enapter-0.6.3.dist-info/top_level.txt,sha256=DsMzVradd7z3A0fm7zmn9oh08ijO41RtzglrnPlx54w,14
|
|
27
|
-
enapter-0.6.3.dist-info/RECORD,,
|