moat-dev-heat 0.1.0__tar.gz
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.
- moat_dev_heat-0.1.0/LICENSE.txt +14 -0
- moat_dev_heat-0.1.0/PKG-INFO +26 -0
- moat_dev_heat-0.1.0/README.md +6 -0
- moat_dev_heat-0.1.0/moat/dev/heat/__init__.py +4 -0
- moat_dev_heat-0.1.0/moat/dev/heat/_main.py +21 -0
- moat_dev_heat-0.1.0/moat/dev/heat/kwb.py +108 -0
- moat_dev_heat-0.1.0/moat/dev/heat/solvis.py +2282 -0
- moat_dev_heat-0.1.0/moat_dev_heat.egg-info/PKG-INFO +26 -0
- moat_dev_heat-0.1.0/moat_dev_heat.egg-info/SOURCES.txt +12 -0
- moat_dev_heat-0.1.0/moat_dev_heat.egg-info/dependency_links.txt +1 -0
- moat_dev_heat-0.1.0/moat_dev_heat.egg-info/requires.txt +1 -0
- moat_dev_heat-0.1.0/moat_dev_heat.egg-info/top_level.txt +1 -0
- moat_dev_heat-0.1.0/pyproject.toml +102 -0
- moat_dev_heat-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
The code in this repository, and all MoaT submodules it refers to,
|
|
2
|
+
is part of the MoaT project.
|
|
3
|
+
|
|
4
|
+
Unless a submodule's LICENSE.txt states otherwise, all included files are
|
|
5
|
+
licensed under the LGPL V3, as published by the FSF at
|
|
6
|
+
https://www.gnu.org/licenses/lgpl-3.0.html .
|
|
7
|
+
|
|
8
|
+
In addition to the LGPL's terms, the author(s) respectfully ask all users of
|
|
9
|
+
this code to contribute any bug fixes or enhancements. Also, please link back to
|
|
10
|
+
https://M-o-a-T.org.
|
|
11
|
+
|
|
12
|
+
Thank you.
|
|
13
|
+
|
|
14
|
+
Copyright © 2021 ff.: the MoaT contributor(s), as per the git changelog(s).
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: moat-dev-heat
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: REPLACE ME
|
|
5
|
+
Author-email: Matthias Urlichs <matthias@urlichs.de>
|
|
6
|
+
Project-URL: homepage, https://m-o-a-t.org
|
|
7
|
+
Project-URL: repository, https://github.com/M-o-a-T/moat
|
|
8
|
+
Keywords: MoaT
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Framework :: AnyIO
|
|
11
|
+
Classifier: Framework :: Trio
|
|
12
|
+
Classifier: Framework :: AsyncIO
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE.txt
|
|
18
|
+
Requires-Dist: anyio~=3.0
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
This is a home heating controller.
|
|
22
|
+
|
|
23
|
+
Currently it's a personal project that controls a Solvis controller, buffer,
|
|
24
|
+
and heat pump, and a KWB pellet burner, in dual/alternate mode, though the
|
|
25
|
+
result is intended to be somewhat more generic.
|
|
26
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Basic heater tool support
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import logging # pylint: disable=wrong-import-position
|
|
9
|
+
|
|
10
|
+
import asyncclick as click
|
|
11
|
+
|
|
12
|
+
from moat.util import load_subgroup
|
|
13
|
+
|
|
14
|
+
log = logging.getLogger()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@load_subgroup(prefix="moat.dev.heat")
|
|
18
|
+
@click.pass_obj
|
|
19
|
+
async def cli(obj):
|
|
20
|
+
"""Device Manager for heaters"""
|
|
21
|
+
obj # noqa:B018 pylint: disable=pointless-statement # TODO
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""
|
|
2
|
+
command line interface thing for KWB pellet burner
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import anyio
|
|
8
|
+
import logging
|
|
9
|
+
|
|
10
|
+
import asyncclick as click
|
|
11
|
+
|
|
12
|
+
from moat.util import yload
|
|
13
|
+
from moat.modbus.dev.poll import dev_poll
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def lifeticker(dest):
|
|
19
|
+
"Task to periodically update the lifeticker modbus var"
|
|
20
|
+
t_out = dest.regs.ksm.modbus.lifetick
|
|
21
|
+
t_in = dest.regs.ksm.modbus.commit_lifetick
|
|
22
|
+
|
|
23
|
+
t_out.value = 0
|
|
24
|
+
v_old = t_in.value
|
|
25
|
+
n_old = 0
|
|
26
|
+
|
|
27
|
+
while True:
|
|
28
|
+
for n in range(1, 65535):
|
|
29
|
+
t_out.value = n
|
|
30
|
+
await anyio.sleep(45)
|
|
31
|
+
|
|
32
|
+
if t_in.value != v_old:
|
|
33
|
+
v_old = t_in.value
|
|
34
|
+
n_old = 0
|
|
35
|
+
elif n_old > 2:
|
|
36
|
+
raise RuntimeError("No commit_lifetick change")
|
|
37
|
+
else:
|
|
38
|
+
n_old += 1
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@click.command(short_help="Access the KWB modbus.") # pylint: disable=undefined-variable
|
|
42
|
+
@click.option("-c", "--cfg", type=click.File("r"), help="Moat-Modbus configuration", required=True)
|
|
43
|
+
@click.option("-h", "--host", help="Host to access")
|
|
44
|
+
@click.option("-p", "--port", type=int, help="Port to access")
|
|
45
|
+
@click.option("-u", "--unit", type=int, help="Unit to access")
|
|
46
|
+
@click.pass_context
|
|
47
|
+
async def cli(ctx, cfg, host, port, unit):
|
|
48
|
+
"""
|
|
49
|
+
This command starts a Modbus client that connects to a KWB EasyFire pellet burner.
|
|
50
|
+
|
|
51
|
+
It will send Lifetick updates.
|
|
52
|
+
|
|
53
|
+
By default this command talks to all hosts with a `regs.modbus.lifetick` register.
|
|
54
|
+
(DO NOT set `regs.modbus.lifetick` to mirror from anywhere.)
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
cfg = yload(cfg, attr=True)
|
|
59
|
+
|
|
60
|
+
obj = ctx.obj
|
|
61
|
+
if "distkv" in obj.cfg:
|
|
62
|
+
# pylint: disable=import-outside-toplevel
|
|
63
|
+
from distkv.client import client_scope
|
|
64
|
+
|
|
65
|
+
dkv = await client_scope(**obj.cfg.distkv)
|
|
66
|
+
else:
|
|
67
|
+
dkv = None
|
|
68
|
+
|
|
69
|
+
n = 0
|
|
70
|
+
|
|
71
|
+
def get_one(d, h):
|
|
72
|
+
if h is None:
|
|
73
|
+
yield from d.values()
|
|
74
|
+
elif h in d:
|
|
75
|
+
yield d[h]
|
|
76
|
+
|
|
77
|
+
async with anyio.create_task_group() as tg:
|
|
78
|
+
cfg = await tg.start(dev_poll, cfg, dkv)
|
|
79
|
+
|
|
80
|
+
def proc(dest):
|
|
81
|
+
try:
|
|
82
|
+
dest.regs.ksm.modbus.lifetick # noqa:B018 pylint: disable=pointless-statement
|
|
83
|
+
except AttributeError:
|
|
84
|
+
return
|
|
85
|
+
tg.start_soon(lifeticker, dest)
|
|
86
|
+
|
|
87
|
+
nonlocal n
|
|
88
|
+
n += 1
|
|
89
|
+
|
|
90
|
+
for h in get_one(cfg.get("hostports", {}), host):
|
|
91
|
+
for p in get_one(h, port):
|
|
92
|
+
for dest in get_one(p, unit):
|
|
93
|
+
proc(dest)
|
|
94
|
+
|
|
95
|
+
if port is None or port == 502:
|
|
96
|
+
for h in get_one(cfg.get("hosts", {}), host):
|
|
97
|
+
for dest in get_one(h, unit):
|
|
98
|
+
proc(dest)
|
|
99
|
+
|
|
100
|
+
if n:
|
|
101
|
+
# found a KWB system. Possibly do some work here.
|
|
102
|
+
pass # will sleep here, because dev_poll and lifeticker continue to run
|
|
103
|
+
|
|
104
|
+
else:
|
|
105
|
+
# did not find a KWB system. Stop.
|
|
106
|
+
tg.cancel_scope.cancel()
|
|
107
|
+
|
|
108
|
+
raise RuntimeError("No matching servers found")
|