wirely 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.
Files changed (122) hide show
  1. wirely-0.1.0/LICENSE +21 -0
  2. wirely-0.1.0/MANIFEST.in +11 -0
  3. wirely-0.1.0/PKG-INFO +94 -0
  4. wirely-0.1.0/README.md +62 -0
  5. wirely-0.1.0/doc/README.md +8 -0
  6. wirely-0.1.0/doc/language.md +166 -0
  7. wirely-0.1.0/doc/libraries.md +177 -0
  8. wirely-0.1.0/doc/python-style.md +12 -0
  9. wirely-0.1.0/examples/delay.wrl +7 -0
  10. wirely-0.1.0/examples/flipflop.wrl +10 -0
  11. wirely-0.1.0/examples/gpio.wrl +4 -0
  12. wirely-0.1.0/examples/knx.wrl +23 -0
  13. wirely-0.1.0/examples/test.wrl +86 -0
  14. wirely-0.1.0/pyproject.toml +40 -0
  15. wirely-0.1.0/setup.cfg +4 -0
  16. wirely-0.1.0/test/abb.py +37 -0
  17. wirely-0.1.0/test/abb.wrl +8 -0
  18. wirely-0.1.0/test/and.py +29 -0
  19. wirely-0.1.0/test/and.wrl +14 -0
  20. wirely-0.1.0/test/compiler.wrl +8 -0
  21. wirely-0.1.0/test/csv.py +270 -0
  22. wirely-0.1.0/test/csv.wrl +9 -0
  23. wirely-0.1.0/test/delay.py +75 -0
  24. wirely-0.1.0/test/delay.wrl +13 -0
  25. wirely-0.1.0/test/ext_prog.py +70 -0
  26. wirely-0.1.0/test/ext_prog.wrl +3 -0
  27. wirely-0.1.0/test/flipflop.py +90 -0
  28. wirely-0.1.0/test/flipflop.wrl +9 -0
  29. wirely-0.1.0/test/gpio.py +143 -0
  30. wirely-0.1.0/test/gpio.wrl +9 -0
  31. wirely-0.1.0/test/knx.py +450 -0
  32. wirely-0.1.0/test/knx.wrl +81 -0
  33. wirely-0.1.0/test/knx_time.py +74 -0
  34. wirely-0.1.0/test/knx_time.wrl +7 -0
  35. wirely-0.1.0/test/mqtt.py +345 -0
  36. wirely-0.1.0/test/mqtt.wrl +73 -0
  37. wirely-0.1.0/test/negative/duplicate_block_type_in_reopened_library.wrl +9 -0
  38. wirely-0.1.0/test/negative/library_inside_node_type.wrl +4 -0
  39. wirely-0.1.0/test/negative/singleton_input.wrl +3 -0
  40. wirely-0.1.0/test/negative/singleton_parameter.wrl +3 -0
  41. wirely-0.1.0/test/negative/unknown_abb_jra.wrl +2 -0
  42. wirely-0.1.0/test/negative/unknown_node_type.wrl +1 -0
  43. wirely-0.1.0/test/negative/unknown_parameter.wrl +1 -0
  44. wirely-0.1.0/test/nested_library.wrl +42 -0
  45. wirely-0.1.0/test/option.py +26 -0
  46. wirely-0.1.0/test/option.wrl +25 -0
  47. wirely-0.1.0/test/parser.wrl +4 -0
  48. wirely-0.1.0/test/simple.wrl +3 -0
  49. wirely-0.1.0/test/std.py +49 -0
  50. wirely-0.1.0/test/std.wrl +10 -0
  51. wirely-0.1.0/test/time.py +291 -0
  52. wirely-0.1.0/test/time.wrl +15 -0
  53. wirely-0.1.0/test/two_n.py +349 -0
  54. wirely-0.1.0/test/two_n.wrl +42 -0
  55. wirely-0.1.0/test/types.py +169 -0
  56. wirely-0.1.0/test/types.wrl +0 -0
  57. wirely-0.1.0/test/username_password.txt +2 -0
  58. wirely-0.1.0/tools/build-pex +36 -0
  59. wirely-0.1.0/tools/git_write_wd_tree.py +80 -0
  60. wirely-0.1.0/tools/run-tests +67 -0
  61. wirely-0.1.0/tools/setup-venv +31 -0
  62. wirely-0.1.0/tools/test-gitlab-ci +395 -0
  63. wirely-0.1.0/tools/wirely +27 -0
  64. wirely-0.1.0/wirely/__init__.py +406 -0
  65. wirely-0.1.0/wirely/__main__.py +45 -0
  66. wirely-0.1.0/wirely/ast.py +1135 -0
  67. wirely-0.1.0/wirely/block.py +542 -0
  68. wirely-0.1.0/wirely/block_type.py +342 -0
  69. wirely-0.1.0/wirely/blocks.lark +126 -0
  70. wirely-0.1.0/wirely/compiler.py +125 -0
  71. wirely-0.1.0/wirely/engine.py +418 -0
  72. wirely-0.1.0/wirely/enum.py +15 -0
  73. wirely-0.1.0/wirely/example.py +36 -0
  74. wirely-0.1.0/wirely/graphviz.py +107 -0
  75. wirely-0.1.0/wirely/knx/__init__.py +0 -0
  76. wirely-0.1.0/wirely/knx/connection_adapter.py +90 -0
  77. wirely-0.1.0/wirely/knx/connection_config.py +152 -0
  78. wirely-0.1.0/wirely/lib/abb_jra.wrl +64 -0
  79. wirely-0.1.0/wirely/lib/csv.wrl +32 -0
  80. wirely-0.1.0/wirely/lib/ext_prog.wrl +12 -0
  81. wirely-0.1.0/wirely/lib/gpio.wrl +23 -0
  82. wirely-0.1.0/wirely/lib/knx.wrl +80 -0
  83. wirely-0.1.0/wirely/lib/memory.wrl +45 -0
  84. wirely-0.1.0/wirely/lib/mqtt.wrl +66 -0
  85. wirely-0.1.0/wirely/lib/std.wrl +134 -0
  86. wirely-0.1.0/wirely/lib/time.wrl +93 -0
  87. wirely-0.1.0/wirely/lib/two_n.wrl +111 -0
  88. wirely-0.1.0/wirely/misc/__init__.py +0 -0
  89. wirely-0.1.0/wirely/misc/service.py +48 -0
  90. wirely-0.1.0/wirely/native/__init__.py +74 -0
  91. wirely-0.1.0/wirely/native/abb_jra.py +66 -0
  92. wirely-0.1.0/wirely/native/csv.py +194 -0
  93. wirely-0.1.0/wirely/native/ext_prog.py +77 -0
  94. wirely-0.1.0/wirely/native/gpio.py +125 -0
  95. wirely-0.1.0/wirely/native/knx.py +455 -0
  96. wirely-0.1.0/wirely/native/memory.py +113 -0
  97. wirely-0.1.0/wirely/native/mqtt.py +605 -0
  98. wirely-0.1.0/wirely/native/std.py +334 -0
  99. wirely-0.1.0/wirely/native/test_library.py +14 -0
  100. wirely-0.1.0/wirely/native/time.py +359 -0
  101. wirely-0.1.0/wirely/native/two_n.py +632 -0
  102. wirely-0.1.0/wirely/node.py +125 -0
  103. wirely-0.1.0/wirely/parser.py +175 -0
  104. wirely-0.1.0/wirely/program.py +155 -0
  105. wirely-0.1.0/wirely/run.py +143 -0
  106. wirely-0.1.0/wirely/service.py +65 -0
  107. wirely-0.1.0/wirely/test.py +569 -0
  108. wirely-0.1.0/wirely/time.py +172 -0
  109. wirely-0.1.0/wirely/two_n/__init__.py +0 -0
  110. wirely-0.1.0/wirely/two_n/cert.py +126 -0
  111. wirely-0.1.0/wirely/two_n/client.py +402 -0
  112. wirely-0.1.0/wirely/two_n/fake_server.py +415 -0
  113. wirely-0.1.0/wirely/two_n/grab_images.py +146 -0
  114. wirely-0.1.0/wirely/types.py +522 -0
  115. wirely-0.1.0/wirely/util.py +111 -0
  116. wirely-0.1.0/wirely/value.py +163 -0
  117. wirely-0.1.0/wirely.egg-info/PKG-INFO +94 -0
  118. wirely-0.1.0/wirely.egg-info/SOURCES.txt +120 -0
  119. wirely-0.1.0/wirely.egg-info/dependency_links.txt +1 -0
  120. wirely-0.1.0/wirely.egg-info/entry_points.txt +2 -0
  121. wirely-0.1.0/wirely.egg-info/requires.txt +30 -0
  122. wirely-0.1.0/wirely.egg-info/top_level.txt +1 -0
wirely-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Steffen Kieß
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.
@@ -0,0 +1,11 @@
1
+ recursive-include doc *.md
2
+ recursive-include examples *.wrl
3
+ recursive-include test *.py *.wrl
4
+ include test/username_password.txt
5
+ include tools/build-pex
6
+ include tools/git_write_wd_tree.py
7
+ include tools/run-tests
8
+ include tools/setup-venv
9
+ include tools/test-gitlab-ci
10
+ include tools/wirely
11
+ global-exclude *~ *.py[cod]
wirely-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: wirely
3
+ Version: 0.1.0
4
+ Summary: Node-based language and runtime for low-level home automation
5
+ Author: Steffen Kieß
6
+ License: MIT
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: lark
11
+ Requires-Dist: sdnotify
12
+ Provides-Extra: knx
13
+ Requires-Dist: xknx<4,>=3.0; extra == "knx"
14
+ Provides-Extra: mqtt
15
+ Requires-Dist: aiomqtt<3,>=2.3; extra == "mqtt"
16
+ Provides-Extra: two-n
17
+ Requires-Dist: aiohttp<4,>=3.12; extra == "two-n"
18
+ Requires-Dist: cryptography; extra == "two-n"
19
+ Provides-Extra: graphviz
20
+ Requires-Dist: graphviz; extra == "graphviz"
21
+ Provides-Extra: gpio
22
+ Requires-Dist: gpiod<3,>=2.1; extra == "gpio"
23
+ Provides-Extra: all
24
+ Requires-Dist: wirely[gpio,graphviz,knx,mqtt,two-n]; extra == "all"
25
+ Provides-Extra: test
26
+ Requires-Dist: pyOpenSSL; extra == "test"
27
+ Provides-Extra: pex
28
+ Requires-Dist: pex; extra == "pex"
29
+ Provides-Extra: dev
30
+ Requires-Dist: wirely[pex,test]; extra == "dev"
31
+ Dynamic: license-file
32
+
33
+ Wirely
34
+ ======
35
+
36
+ Wirely is a node-based language and runtime for low-level home automation.
37
+ It connects signals from KNX group addresses, MQTT topics, and other device
38
+ interfaces through nodes that process values and apply logic. For example,
39
+ it can listen for boolean values on two KNX group addresses and publish
40
+ `first_value AND NOT second_value` to a third. Wirely works directly with
41
+ these signals; it does not model higher-level concepts such as rooms,
42
+ as Home Assistant does.
43
+
44
+ See the [language documentation](doc/language.md) for how to write Wirely
45
+ programs.
46
+
47
+ Installation
48
+ ------------
49
+
50
+ Wirely requires Python 3.11 or newer. Install the core package with:
51
+
52
+ ```
53
+ python3 -m pip install wirely
54
+ ```
55
+
56
+ The core install includes the compiler and command-line interface. The
57
+ installed `wirely` command and `python3 -m wirely` provide the same
58
+ subcommands. For example:
59
+
60
+ ```
61
+ wirely compile program.wrl
62
+ python3 -m wirely compile program.wrl
63
+ ```
64
+
65
+ Install optional integrations as needed:
66
+
67
+ ```
68
+ python3 -m pip install 'wirely[knx]' # KNX
69
+ python3 -m pip install 'wirely[mqtt]' # MQTT
70
+ python3 -m pip install 'wirely[two-n]' # 2N devices
71
+ python3 -m pip install 'wirely[graphviz]' # show-graph
72
+ python3 -m pip install 'wirely[gpio]' # GPIO
73
+ python3 -m pip install 'wirely[all]' # all optional integrations
74
+ ```
75
+
76
+ The `test` extra adds dependencies used only by the test suite. Install
77
+ `wirely[all,test]` to include both integrations and test dependencies.
78
+
79
+ Development checkout
80
+ --------------------
81
+
82
+ Create a virtual environment with all dependencies, then use the
83
+ checkout launchers:
84
+
85
+ ```
86
+ tools/setup-venv
87
+ tools/wirely compile test/simple.wrl
88
+ tools/run-tests
89
+ ```
90
+
91
+ The launchers use `venv/bin/python` when available. Otherwise, they warn and
92
+ use the current Python interpreter. `tools/wirely` accepts the same arguments
93
+ as `wirely` and runs the source in this checkout from any working directory.
94
+ The test suite also requires `knxd` and `mosquitto`.
wirely-0.1.0/README.md ADDED
@@ -0,0 +1,62 @@
1
+ Wirely
2
+ ======
3
+
4
+ Wirely is a node-based language and runtime for low-level home automation.
5
+ It connects signals from KNX group addresses, MQTT topics, and other device
6
+ interfaces through nodes that process values and apply logic. For example,
7
+ it can listen for boolean values on two KNX group addresses and publish
8
+ `first_value AND NOT second_value` to a third. Wirely works directly with
9
+ these signals; it does not model higher-level concepts such as rooms,
10
+ as Home Assistant does.
11
+
12
+ See the [language documentation](doc/language.md) for how to write Wirely
13
+ programs.
14
+
15
+ Installation
16
+ ------------
17
+
18
+ Wirely requires Python 3.11 or newer. Install the core package with:
19
+
20
+ ```
21
+ python3 -m pip install wirely
22
+ ```
23
+
24
+ The core install includes the compiler and command-line interface. The
25
+ installed `wirely` command and `python3 -m wirely` provide the same
26
+ subcommands. For example:
27
+
28
+ ```
29
+ wirely compile program.wrl
30
+ python3 -m wirely compile program.wrl
31
+ ```
32
+
33
+ Install optional integrations as needed:
34
+
35
+ ```
36
+ python3 -m pip install 'wirely[knx]' # KNX
37
+ python3 -m pip install 'wirely[mqtt]' # MQTT
38
+ python3 -m pip install 'wirely[two-n]' # 2N devices
39
+ python3 -m pip install 'wirely[graphviz]' # show-graph
40
+ python3 -m pip install 'wirely[gpio]' # GPIO
41
+ python3 -m pip install 'wirely[all]' # all optional integrations
42
+ ```
43
+
44
+ The `test` extra adds dependencies used only by the test suite. Install
45
+ `wirely[all,test]` to include both integrations and test dependencies.
46
+
47
+ Development checkout
48
+ --------------------
49
+
50
+ Create a virtual environment with all dependencies, then use the
51
+ checkout launchers:
52
+
53
+ ```
54
+ tools/setup-venv
55
+ tools/wirely compile test/simple.wrl
56
+ tools/run-tests
57
+ ```
58
+
59
+ The launchers use `venv/bin/python` when available. Otherwise, they warn and
60
+ use the current Python interpreter. `tools/wirely` accepts the same arguments
61
+ as `wirely` and runs the source in this checkout from any working directory.
62
+ The test suite also requires `knxd` and `mosquitto`.
@@ -0,0 +1,8 @@
1
+ # Wirely documentation
2
+
3
+ Wirely programs are graphs of typed nodes written in `.wrl` files. Start with
4
+ the [language guide](language.md) to learn how to declare nodes and connect
5
+ them. Then use the [library reference](libraries.md) to find the nodes supplied
6
+ with Wirely.
7
+
8
+ For Python contributions, follow the [Python style guide](python-style.md).
@@ -0,0 +1,166 @@
1
+ # The Wirely language
2
+
3
+ Wirely is a declarative language for connecting typed nodes. A `.wrl` file
4
+ instantiates node types and connects their outputs to inputs. A node can
5
+ compute a value, emit an event, keep state, or interact with an external
6
+ service. Several `.wrl` files passed to the compiler form one program.
7
+
8
+ ## A first program
9
+
10
+ ```wrl
11
+ node source : Std.Constant(Type = boolean, Value = true);
12
+ node printer : Std.DebugPrinter(type = boolean);
13
+ connect source -> printer;
14
+ ```
15
+
16
+ `source` has an implicit `value boolean` output, and `printer` has an implicit
17
+ `value boolean` input. The connection feeds the output to the input. Save this
18
+ as `hello.wrl` and check it from the repository root with:
19
+
20
+ ```sh
21
+ python3 -m wirely compile hello.wrl
22
+ ```
23
+
24
+ `compile` builds the graph and checks its connections. To start the service,
25
+ use `python3 -m wirely run-service hello.wrl`. Runtime service dependencies
26
+ are described in [the library reference](libraries.md).
27
+
28
+ ## Declarations and names
29
+
30
+ Declarations may appear at the top level or inside a node type. Node
31
+ instantiation uses `node name : Type;` or named parameters in parentheses:
32
+
33
+ ```wrl
34
+ node source : Std.Constant(Type = boolean, Value = true);
35
+ node sink : Std.DebugPrinter(type = boolean);
36
+ connect source -> sink;
37
+ ```
38
+
39
+ The type name may be a local name or a namespace-qualified name such as
40
+ `Std.Constant`. A program may also declare its own types:
41
+
42
+ ```wrl
43
+ node type Forward {
44
+ implicit input : value boolean;
45
+ implicit output : value boolean;
46
+ native procedure run depends on implicit input produces implicit output;
47
+ }
48
+ ```
49
+
50
+ This illustrates a type declaration, but its `native procedure` needs a
51
+ matching Python implementation before the type can be instantiated. To make
52
+ a composite type entirely in `.wrl`, connect its external input to child
53
+ nodes:
54
+
55
+ ```wrl
56
+ node type PrintBoolean {
57
+ implicit input : value boolean;
58
+ node printer : Std.DebugPrinter(type = boolean);
59
+ connect implicit input -> printer;
60
+ }
61
+
62
+ node source : Std.Constant(Type = boolean, Value = true);
63
+ node sink : PrintBoolean;
64
+ connect source -> sink;
65
+ ```
66
+
67
+ A `namespace Name { ... }` groups node types under `Name.Type`. Namespaces can
68
+ contain other namespaces, and a dotted declaration names the same path:
69
+
70
+ ```wrl
71
+ namespace ABB.JRA { node type Status { } }
72
+ // Equivalent to: namespace ABB { namespace JRA { node type Status { } } }
73
+ ```
74
+
75
+ Both forms define `ABB.JRA.Status`. Repeated declarations of the same namespace
76
+ path add their contents to one namespace; node type names within it must remain
77
+ unique. Namespaces contain node types or other namespaces, not instances.
78
+ A `singleton node type` shares one instance across its uses and cannot declare
79
+ parameters. `native node type` indicates that the implementation also owns
80
+ Python state or resources. Both forms require native support when instantiated.
81
+
82
+ Identifiers are case-sensitive. A keyword can be used as an identifier with
83
+ the `r#` prefix, for example `node r#node : Std.And;`. Declarations end with
84
+ `;`, except type and namespace bodies, which use braces. `//` starts a line
85
+ comment, and `/* ... */` block comments may be nested.
86
+
87
+ ## Parameters, types, and literals
88
+
89
+ A node type declares parameters with `parameter Name : Type;`. A parameter
90
+ may have a default (`= value`) or be marked `optional`. Instantiation passes
91
+ named arguments, for example `Time.TriggerAt(Time = "08:00")`. Type parameters
92
+ use `ValueType`; a later parameter or connector can refer to one:
93
+
94
+ ```wrl
95
+ node type Example {
96
+ parameter Type : ValueType;
97
+ parameter Initial : Type;
98
+ implicit output : value Type;
99
+ node constant : Std.Constant(Type = Type, Value = Initial);
100
+ connect constant -> implicit output;
101
+ }
102
+ ```
103
+
104
+ Built-in types are `unit`, `boolean`, signed `int8`/`int16`/`int32`/`int64`,
105
+ unsigned `uint8`/`uint16`/`uint32`/`uint64`, `String`, `Duration`,
106
+ `TimeOfDay`, `MonotonicTime`, and `ThermostatSystemMode`. `ValueType` is used
107
+ for type parameters. `Option[T]` and `List[T]` construct types from `T`.
108
+
109
+ Use `true` and `false` for booleans, non-negative decimal or `0x`/`0o`
110
+ integers for integer parameters, quoted strings for `String`, and `[a, b]`
111
+ for list values. Raw strings (`r"..."` and `r#"..."#`) are also accepted.
112
+ Durations and times of day are passed as strings, for example `"PT1S"` and
113
+ `"08:00"`. `None` is the available `Option[T]` literal; use library nodes
114
+ such as `Std.OptionSome` to create an optional value at runtime.
115
+ `MonotonicTime.Invalid` is the available monotonic-time constant. The
116
+ compile-time function `TimeOfDay_AddWrap(time, offset)` adds an `"HH:MM"`
117
+ offset and wraps at midnight.
118
+
119
+ Expressions have limited support: literals, parameter references, lists,
120
+ generic types, and the named time function above. In particular, unary
121
+ minus and general arithmetic expressions are not currently implemented.
122
+
123
+ ## Connectors and connections
124
+
125
+ An `input` or `output` declares a named connector. `implicit input` and
126
+ `implicit output` declare the connectors used when a connection names just
127
+ the node. For example:
128
+
129
+ ```wrl
130
+ node bus : KNX.Connection(UnixPath = "/run/knxnet");
131
+ node receiver : KNX.ValueReceiver(Type = boolean,
132
+ GroupAddress = "0/4/5");
133
+ connect bus -> receiver.Bus;
134
+ ```
135
+
136
+ The first connection uses `bus`'s implicit output and the named `Bus` input.
137
+ For a named output, write `node_name.OutputName`. Inside a type body,
138
+ `implicit input` and `implicit output` may also be connection endpoints.
139
+
140
+ Data connectors have a kind and a type: `value boolean` carries the current
141
+ value, while `event boolean` carries an occurrence with a boolean payload.
142
+ An event may be absent in a step. Source and target must have the same kind
143
+ and type; Wirely does not automatically convert them. Ordinary inputs accept
144
+ one source. A `list value T` or `list event T` input accepts several sources;
145
+ `connect a, b -> target;` connects both. `Std.And` is an example of a list
146
+ input. Libraries also define resource connectors `knx_bus`, `mqtt_server`,
147
+ and `two_n_device`, which connect a service node to its clients.
148
+
149
+ ## State and execution
150
+
151
+ Inside a node type, `state name : Type = value;` declares a persistent value
152
+ with an initial value. `temporary name : Type;` holds a value calculated
153
+ within a step. Native procedures declare which inputs or temporaries they
154
+ `depend on` and which connectors or variables they `produce`. The engine
155
+ orders procedures from these dependencies and rejects dependency cycles.
156
+
157
+ Procedures run in three stages: `stage input` reads external sources, the
158
+ default stage calculates values and updates state, and `stage output` sends
159
+ results to external systems. State updates become visible on the next step.
160
+ `Std.Delay` demonstrates this: it outputs the previous value and stores the
161
+ current input for the following step.
162
+
163
+ The `.wrl` declaration specifies a native procedure's interface; its code
164
+ lives in Python under `wirely/native/`. Node types can combine child nodes
165
+ and connections in `.wrl`, as `KNX.ValueProvider` does. See the
166
+ [library reference](libraries.md) for available nodes and their connectors.
@@ -0,0 +1,177 @@
1
+ # Library reference
2
+
3
+ The compiler loads all ten libraries below by default. Instantiate a node
4
+ as `node name : Library.Type(Parameter = value);`. Names and parameter
5
+ capitalization matter. In the signatures below, `in` and `out` mean implicit
6
+ connectors; named connectors are written explicitly. `value T` carries a
7
+ current value, and `event T` carries an occurrence with a payload. A dash
8
+ means no connector of that kind.
9
+
10
+ See the [language guide](language.md) for syntax and connection rules. The
11
+ declarations in [`wirely/lib/`](../wirely/lib/) are the source for exact
12
+ signatures; behavior comes from [`wirely/native/`](../wirely/native/).
13
+
14
+ ## Std
15
+
16
+ General data flow, optional values, edge detection, and debug nodes.
17
+
18
+ | Node type | Parameters | Connectors and behavior |
19
+ | --- | --- | --- |
20
+ | `Constant` | `Type: ValueType`, `Value: Type` | Out: `value Type`. Produces the supplied value. |
21
+ | `And` | None | In: `list value boolean`; out: `value boolean`. True when every connected input is true, including an empty list. |
22
+ | `Delay` | `type: ValueType`, `initialValue: type` | In/out: `value type`. Outputs the previous step's input, initially `initialValue`. |
23
+ | `TwoStageValueForward` | `type: ValueType` | In/out: `value type`. Forwards through two native procedures. |
24
+ | `DebugPrinter` | `type: ValueType` | In: `value type`. Prints changes in the output stage. |
25
+ | `DebugEventPrinter` | `type: ValueType` | In: `event type`. Prints events in the output stage. |
26
+ | `DebugInput` | `type: ValueType` | Out: `value type`. Debug-controlled value source. |
27
+ | `DebugEventSource` | `type: ValueType` | Out: `event type`. Debug-controlled event source. |
28
+ | `OptionNone` | `InnerType: ValueType` | Out: `value Option[InnerType]`. Produces `None`. |
29
+ | `OptionSome` | `InnerType: ValueType` | In: `value InnerType`; out: `value Option[InnerType]`. Wraps the input. |
30
+ | `OptionUnwrapOrDefault` | `InnerType: ValueType`, `Default: InnerType` | In: `value Option[InnerType]`; out: `value InnerType`. Uses `Default` for `None`. |
31
+ | `OptionIsSome` | `InnerType: ValueType` | In: `value Option[InnerType]`; out: `value boolean`. True when a value is present. |
32
+ | `OptionCreate` | `InnerType: ValueType` | Named in: `HasValue: value boolean`, `Value: value InnerType`; out: `value Option[InnerType]`. Wraps `Value` when `HasValue` is true; otherwise produces `None`. |
33
+ | `DetectEdge` | `EdgeType: String` | In: `value boolean`; out: `event boolean`. Only `"Falling"` works; `"Rising"` and `"Both"` raise an error when run. |
34
+
35
+ The debug sources are intended for testing and interactive control. Their
36
+ input is supplied through the engine's debug interface, not a `.wrl`
37
+ connection. `TwoStageValueForward` is primarily a demonstration of procedure
38
+ dependencies.
39
+
40
+ ## Time
41
+
42
+ Time-based values and triggers. `TimeOfDay` parameters use strings such as
43
+ `"08:00"`; `Duration` parameters use strings such as `"PT1S"`.
44
+
45
+ | Node type | Parameters | Connectors and behavior |
46
+ | --- | --- | --- |
47
+ | `MonotonicTimeReached` | None | In: `value MonotonicTime`; out: `value Option[boolean]`. True once the time is reached, false before it, or `None` if the times cannot be compared. |
48
+ | `DelayChange` | `Type: ValueType`, `Time: Duration` | In/out: `value Type`. Changes the output only after the input has remained stable for `Time`. |
49
+ | `TimeOfDayMinute` | None | Out: `value TimeOfDay`. Shared singleton that updates at minute boundaries; normally used through other Time nodes. |
50
+ | `IsBetween` | `start: TimeOfDay`, `end: TimeOfDay` | Out: `value boolean`. Tests the interval from `start` (inclusive) to `end` (exclusive), including intervals that cross midnight. |
51
+ | `TriggerAt` | `Time: TimeOfDay`, `Type: ValueType = unit`, optional `Value: Type` | Out: `event Type`. Emits around the requested time, with logic to avoid duplicate daily events during clock changes. Supply `Value` when `Type` is not `unit`. |
52
+
53
+ ## KNX
54
+
55
+ KNX nodes use a `KNX.Connection` node and a named `Bus: knx_bus` input.
56
+ `GroupAddress` is a KNX group address string such as `"0/4/5"`.
57
+
58
+ | Node type | Parameters | Connectors and behavior |
59
+ | --- | --- | --- |
60
+ | `Connection` | Optional `Host: String`, `Protocol: String`, `Port: uint16`, `UnixPath: String`, `SSHTarget: String`, `IndividualAddress: String` | Out: `knx_bus`. Opens a KNX connection. |
61
+ | `ValueReceiver` | `Type: ValueType`, `GroupAddress: String` | Named in: `Bus`; out: `value Option[Type]`. Reads the latest value, with `None` before one is available. |
62
+ | `ValueOptionProvider` | `Type: ValueType`, `GroupAddress: String` | Named in: `Bus`; in: `value Option[Type]`. Exposes an optional value on the bus. |
63
+ | `ValueProvider` | `Type: ValueType`, `GroupAddress: String` | Named in: `Bus`; in: `value Type`. Wraps the value with `Std.OptionSome` and uses `ValueOptionProvider`. |
64
+ | `EventReceiver` | `Type: ValueType`, `GroupAddress: String` | Named in: `Bus`; out: `event Type`. Emits received KNX events. |
65
+ | `EventEmitter` | `Type: ValueType`, `GroupAddress: String` | Named in: `Bus`; in: `event Type`. Sends KNX events. |
66
+
67
+ The current native value receiver and provider support `boolean` and
68
+ `uint16`. Other declared `Type` values may fail when those nodes are
69
+ instantiated. Event support depends on the KNX value type handled by the
70
+ native implementation. KNX use requires the `xknx` package and a reachable
71
+ KNX service.
72
+
73
+ Choose one connection mode:
74
+
75
+ - `Host` with `Protocol = "TCP"` or `"UDP"` opens a direct tunnel. `Port`
76
+ defaults to `3671`.
77
+ - `Protocol = "Unix"` opens a local Unix socket. `UnixPath` defaults to
78
+ `/run/knxnet`; supplying `UnixPath` implies `Protocol = "Unix"`.
79
+ - `SSHTarget` opens a Unix socket on a remote host over SSH. It requires
80
+ `Protocol = "Unix"` unless `UnixPath` is also supplied.
81
+ - `Protocol = "Auto"` uses automatic discovery.
82
+
83
+ `IndividualAddress` is supported for TCP and Unix socket connections. An
84
+ empty connection, an SSH target without a protocol or Unix path, and
85
+ parameters from conflicting modes are errors. For example,
86
+ `KNX.Connection(UnixPath = "/run/knxnet")` uses a local socket.
87
+
88
+ ## ABB.JRA
89
+
90
+ These nodes decode a 16-bit ABB JRA status word into named boolean outputs.
91
+ `InterpretStatusInformation` takes `value uint16` and produces `value
92
+ boolean` outputs. `InterpretStatusInformationOption` takes `value
93
+ Option[uint16]` and produces `value Option[boolean]` outputs; a missing input
94
+ remains missing in every output. Neither node has parameters.
95
+
96
+ Both expose the same named outputs: `ManualOperation`, `BlockActive`,
97
+ `ForcedOperation`, `FrostAlarm`, `RainAlarm`, `WindAlarm`,
98
+ `AutomaticSunProtection`, `AutomaticHeatingCooling`, `DriveError`,
99
+ `DriveInMotion`, `DriveDown`, `DriveUp`, and `SendReceiveDelayActive`.
100
+
101
+ ## MQTT
102
+
103
+ MQTT nodes use a `MQTT.Server` and a named `Server: mqtt_server` input.
104
+ `Topic` is a topic string. `Format` and `Template`, where offered, are
105
+ optional strings used by the native JSON encoder. A template embeds the
106
+ encoded value at one `%s` placeholder; `%%` represents a literal percent
107
+ sign. The `URI` accepts `mqtt://host`, `mqtts://host`, or `mqtt+unix:///path`.
108
+
109
+ | Node type | Parameters | Connectors and behavior |
110
+ | --- | --- | --- |
111
+ | `Server` | `URI: String` | Out: `mqtt_server`. Connects to a broker. |
112
+ | `ValueOptionProvider` | `Type: ValueType`, `Topic: String`, optional `Format: String`, `Template: String` | Named in: `Server`; in: `value Option[Type]`. Publishes JSON; `None` publishes an empty payload. |
113
+ | `ValueProvider` | Same as `ValueOptionProvider` | Named in: `Server`; in: `value Type`. Wraps the value and uses `ValueOptionProvider`. |
114
+ | `EventEmitter` | `Type: ValueType`, `Topic: String`, optional `Format: String`, `Template: String` | Named in: `Server`; in: `event Type`. Publishes each event as JSON. |
115
+ | `EventReceiver` | `Type: ValueType`, `Topic: String`, optional `Format: String` | Named in: `Server`; out: `event Type`. Emits decoded messages received on the topic. |
116
+
117
+ Only types supported by the native JSON encoder/decoder can be used. MQTT
118
+ requires the `aiomqtt` package and a reachable broker.
119
+
120
+ ## CSV
121
+
122
+ `CSV.ValueWriter` takes `Type: ValueType` and `Filename: String`. It appends
123
+ rows from an implicit `value Type` input, recording the timestamp, old value,
124
+ and new value. An empty field marks an unavailable value at startup or
125
+ shutdown. It writes on changes and repeats a current value hourly. `Type`
126
+ must support CSV conversion, such as `boolean`, an integer type, or an
127
+ `Option` of one. The file is opened for append when the service starts.
128
+
129
+ ## TwoN
130
+
131
+ TwoN nodes communicate with a 2N device. Create `TwoN.Device`, then connect
132
+ its implicit `two_n_device` output to each client's named `Device` input.
133
+
134
+ | Node type | Parameters | Connectors and behavior |
135
+ | --- | --- | --- |
136
+ | `Device` | `Hostname: String`, `Port: uint16 = 443`, `TLSA: List[String]`, `UsernamePasswordFile: String` | Out: `two_n_device`. Connects to the device. The credential file contains a username and password on separate lines; a relative path is resolved beside the `.wrl` file. |
137
+ | `KeyEvent` | `Key: String` | Named in: `Device`; out: `event boolean`. True on press, false on release. |
138
+ | `KeyState` | `Key: String`, `Timeout: Duration = "30s"` | Named in: `Device`; out: `value boolean`. Tracks a key, assuming release after the timeout. Startup state can be wrong if the key was already pressed. |
139
+ | `IOState` | `Port: String` | Named in: `Device`; out: `value boolean`. Tracks an input or output port. |
140
+ | `SwitchState` | `SwitchNumber: int32` | Named in: `Device`; out: `value boolean`. Tracks whether a switch is active. |
141
+ | `CodeEnteredEvent` | `Code: String` | Named in: `Device`; out: `event unit`. Emits when the specified code is entered. |
142
+ | `OutputControl` | `Port: String` | Named in: `Device`; in: `event boolean`. Sets an output on or off. |
143
+
144
+ `TLSA` provides certificate association records. TwoN use needs the bundled
145
+ TwoN dependencies, device credentials, and network access to the device.
146
+
147
+ ## GPIO
148
+
149
+ `GPIO.Output` sets a GPIO line from an implicit `value boolean` input.
150
+ Parameters are `ChipDeviceFile: String`, `Name: String`,
151
+ `ActiveState: String = "High"`, `Drive: String = "PushPull"`, and
152
+ `Bias: String = "AsIs"`. `ActiveState` accepts `"High"` or `"Low"`;
153
+ `Drive` accepts `"PushPull"`, `"OpenDrain"`, or `"OpenSource"`; and
154
+ `Bias` accepts `"PullDown"`, `"PullUp"`, `"Disable"`, or `"AsIs"`.
155
+ GPIO use requires the `gpio` extra (or the official `gpiod` 2.x Python
156
+ bindings) and access to the chip device.
157
+
158
+ ## Memory
159
+
160
+ `Memory.ToggleFlipFlopTrigger` can switch an external impulse relay
161
+ (latching relay) on or off. Each pulse toggles the relay, so the node compares
162
+ the requested state with feedback and emits a pulse only when the state needs
163
+ to change. It takes an implicit `event boolean` command input (`true` for on,
164
+ `false` for off) and a named `Feedback: value Option[boolean]` input. Its
165
+ implicit `event OutputType` output provides the trigger pulse. If feedback is
166
+ `None`, commands are ignored. Its parameters are `OutputType: ValueType = unit`,
167
+ optional `OutputValue: OutputType`, and `MinimumDelay: Duration = "PT1S"`.
168
+ Set `OutputValue` when `OutputType` is not `unit`. The minimum delay limits
169
+ successive output events.
170
+
171
+ ## ExtProg
172
+
173
+ `ExtProg.LaunchOnEvent` launches a shell command when an input event arrives.
174
+ Events received while a launch is pending may be combined into one launch.
175
+ It takes `ShellCommand: String` and `Type: ValueType = unit`, with an implicit
176
+ `event Type` input. The event payload is ignored. The command is passed to
177
+ `sh -c` in the output stage and runs with the service process's permissions.
@@ -0,0 +1,12 @@
1
+ # Python style guide
2
+
3
+ ## Text format
4
+
5
+ - Save Python files as UTF-8 with LF line endings and a final newline.
6
+ - Use spaces, not tabs. Indent code with four spaces per level.
7
+
8
+ ## Code style
9
+
10
+ - Generally, follow PEP 8.
11
+ - Longer lines are acceptable when they improve readability.
12
+ - Use `snake_case` for functions and variables and `PascalCase` for classes.
@@ -0,0 +1,7 @@
1
+ node in : Std.DebugInput(type = boolean);
2
+
3
+ node delay : Time.DelayChange(Type = boolean, Time = "PT1S");
4
+ connect in -> delay;
5
+
6
+ node prnt : Std.DebugPrinter(type = boolean);
7
+ connect delay -> prnt;
@@ -0,0 +1,10 @@
1
+ node in : Std.DebugEventSource(type = boolean);
2
+
3
+ node fb : Std.DebugInput(type = Option[boolean]);
4
+
5
+ node trigger : Memory.ToggleFlipFlopTrigger(OutputType = boolean, OutputValue = true, MinimumDelay = "PT1S");
6
+ connect in -> trigger;
7
+ connect fb -> trigger.Feedback;
8
+
9
+ node prnt : Std.DebugEventPrinter(type = boolean);
10
+ connect trigger -> prnt;
@@ -0,0 +1,4 @@
1
+ node in : Std.DebugInput(type = boolean);
2
+
3
+ node gpio : GPIO.Output(ChipDeviceFile = "/dev/gpiochip0", Name = "GPIO5", ActiveState = "Low", Bias = "PullUp");
4
+ connect in -> gpio;
@@ -0,0 +1,23 @@
1
+ // Meeting-room lighting example: turn the lights on only when the room is
2
+ // occupied and lighting is enabled.
3
+ node Bus : KNX.Connection(UnixPath = "/run/knxnet");
4
+
5
+ node Occupancy : KNX.ValueReceiver(GroupAddress = "0/0/1", Type = boolean);
6
+ connect Bus -> Occupancy.Bus;
7
+
8
+ node LightingEnabled : KNX.ValueReceiver(GroupAddress = "0/0/2", Type = boolean);
9
+ connect Bus -> LightingEnabled.Bus;
10
+
11
+ // Keep the lights off until both input values have been received.
12
+ node OccupiedValue : Std.OptionUnwrapOrDefault(InnerType = boolean, Default = false);
13
+ connect Occupancy -> OccupiedValue;
14
+
15
+ node EnabledValue : Std.OptionUnwrapOrDefault(InnerType = boolean, Default = false);
16
+ connect LightingEnabled -> EnabledValue;
17
+
18
+ node LightsOn : Std.And;
19
+ connect OccupiedValue, EnabledValue -> LightsOn;
20
+
21
+ node Lights : KNX.ValueProvider(GroupAddress = "0/0/3", Type = boolean);
22
+ connect Bus -> Lights.Bus;
23
+ connect LightsOn -> Lights;