1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! This crate provides type definitions and utilities for working with the [S2 energy flexibility standard](https://s2standard.org). S2 is a communication standard for energy flexibility and energy management in homes and buildings, designed to simplify the use of energy flexibility of smart devices. To learn more about the S2 standard:
//! - [Read the documentation](https://docs.s2standard.org/) for a detailed explanation of S2
//! - [Visit the website](https://s2standard.org) for a high-level explanation of S2
//! - [Read the whitepaper](https://ecostandard.org/wp-content/uploads/2024/05/20240521_DSF_PositionPaper.pdf) to learn why it's important to expose and utilise energy flexibility
//!
//! # Crate contents
//! This crate provides Rust types for all types specified by S2. It also includes provides utilities in the [`connection`] and [`transport`] modules
//! that help you set up S2 connections.
//!
//! JSON over WebSockets is a common and recommended way to implement S2, but you're free to choose a different
//! format and communication protocol. In that case, the types in this crate should still be useful but you may wish to disable the `websockets-json` feature.
//!
//! # Using this crate
//! When implementing S2, you need to have a clear picture of which control types you want to implement. If you're not sure which control type
//! you're implementing, see [the documentation website](https://docs.s2standard.org/docs/concepts/control-types/) for an overview of the
//! available control types.
//!
//! S2 types common to all control types (such as [`PowerValue`](common::PowerValue) and [`Commodity`](common::Commodity)) are in the [`common`] module.
//! The types specific to control types (such as [`FRBC.ActuatorStatus`](frbc::ActuatorStatus) or [`PEBC.PowerConstraints`](pebc::PowerConstraints)) are
//! divided into modules based on the control type they belong to.
//!
//! ### Creating S2 types
//! S2 types have all their fields exposed, so you can construct them using regular Rust constructor syntax:
//! ```
//! # use s2energy::common::Id;
//! # let actuator_id = Id::generate();
//! # let operation_mode_id = Id::generate();
//! use s2energy::{common::NumberRange, frbc::ActuatorStatus};
//!
//! let range = NumberRange {
//! start_of_range: 1.0,
//! end_of_range: 30.5,
//! };
//!
//! let actuator_status = ActuatorStatus::builder()
//! .active_operation_mode_id(operation_mode_id)
//! .actuator_id(actuator_id)
//! .operation_mode_factor(0.7)
//! .build();
//! ```
//!
//! Most S2 types have an automatically generated builder (such as [`ActuatorStatus::builder`](frbc::ActuatorStatus::builder) in the above example), so
//! you can use those to create an actuator status as a more convenient alternative to the regular object initialization syntax.
//! S2 types with only one field have a `new` function to easily create them.
//!
//! ### Working with [`Message`](common::Message)
//! When sending or receiving S2 messages, you'll be working with [`common::Message`]. This type represents all possible S2 messages in a big enum. When
//! receiving messages, you'll want to match on `Message` to determine how to handle it:
//! ```
//! # use s2energy::common::Message;
//! # use s2energy::frbc::StorageStatus;
//! # let incoming_message = Message::FrbcStorageStatus(StorageStatus::new(2.1));
//! match incoming_message {
//! Message::FrbcSystemDescription(system_description) => { /* Handle it */ },
//! Message::FrbcStorageStatus(storage_status) => { /* Handle it */ },
//! _ => { /* Ignore other messages */ }
//! }
//! ```
//!
//! All types that serve as the content of a message (such as [`frbc::SystemDescription`] and [`frbc::StorageStatus`] in the above example) implement
//! `Into<Message>` for convenience. This means you can do:
//! ```
//! # use s2energy::common::Message;
//! # use s2energy::frbc::StorageStatus;
//! let storage_status = StorageStatus::new(2.1);
//! let message: Message = storage_status.into();
//! ```
//! [`Connection::send_message`](connection::S2Connection::send_message) accepts an `impl Into<Message>`, so you can just give it any compatible
//! type and it will work.
//!
//! ### Sending/receiving S2 messages
//! S2 does not specify a particular transport protocol for S2 messages. As a result, many transport protocols can be used: WebSockets, MQTT, [even D-Bus](https://github.com/victronenergy/venus/wiki/Venus-OS-D%E2%80%90Bus-S2-Interface).
//! To facilitate the use of different transport protocols, this crates provides a central abstraction in [`connection::S2Connection`] and [`transport::S2Transport`].
//! An `S2Connection` can use any transport protocol implementing the `S2Transport` trait.
//!
//! This crate provides some transport implementations for end-users. Currently, only a WebSockets implementation is provided (in [`transport::websockets_json`]).
//! D-Bus support is also planned for the near future.
//!
//! ### Crate features
//! The crate currently has these features:
//! - `websockets-json` (enabled by default): enables WebSocket support with the [`transport::websockets_json`] module.
//!
//! # Reading this documentation
//! Part of this documentation is automatically generated by extracting descriptions from the S2 specification.
//! Specifically, the S2 type definitions in [`common`] and the control type specific modules are all generated, and their documentation
//! can be spotty. The [language-agnostic documentation for the S2 standard](https://docs.s2standard.org/)
//! is often more helpful and complete.
//!
//! Module documentation (for all modules) and documentation for other types (like those in [`connection`] or [`transport`]) is hand-written and generally of a higher standard.
//! It assumes that you are familiar with the S2 standard; if this is not the case, it may be useful to refer to [the S2 documentation website](https://docs.s2standard.org/docs/welcome/).
include!;