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.
@@ -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,6 @@
1
+ This is a home heating controller.
2
+
3
+ Currently it's a personal project that controls a Solvis controller, buffer,
4
+ and heat pump, and a KWB pellet burner, in dual/alternate mode, though the
5
+ result is intended to be somewhat more generic.
6
+
@@ -0,0 +1,4 @@
1
+ # noqa:D104 pylint: disable=missing-module-docstring
2
+ from __future__ import annotations
3
+
4
+ __path__ = __import__("pkgutil").extend_path(__path__, __name__)
@@ -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")