rpi2home-assistant 2.3.0__py2.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.
_raspy2mqtt_version.py ADDED
@@ -0,0 +1 @@
1
+ version = "2.3.0"
raspy2mqtt/__init__.py ADDED
File without changes
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env python3
2
+
3
+ #
4
+ # Author: fmontorsi
5
+ # Created: June 2024
6
+ # License: Apache license
7
+ #
8
+
9
+
10
+ # =======================================================================================================
11
+ # CircularBuffer
12
+ # =======================================================================================================
13
+
14
+
15
+ class CircularBuffer:
16
+ """
17
+ This is a specialized implementation of a circular buffer designed to:
18
+ * hold boolean/digital samples
19
+ * hold non-uniformly-distributed samples: each sample has its companion timestamp and
20
+ there is no fixed sampling frequency assumption
21
+ * hold in the buffer only CHANGES in value: pushing twice the same value into the buffer
22
+ (with different timestamps) means the second sample is merged with the first one
23
+ * allow simple & efficient filtering for "stable" values discarding transient fluctuations
24
+ if their duration is below a fixed threshold
25
+ """
26
+
27
+ def __init__(self, size: int):
28
+ assert size > 0
29
+ self.size = size
30
+ # buffer is empty at the start
31
+ # each entry is actually a tuple (TIMESTAMP;VALUE)
32
+ self.buffer = [(None, None)] * size
33
+ self.index = 0 # next writable location in the buffer
34
+ self.last_timestamp = 0
35
+
36
+ def push_sample(self, timestamp: int, value: bool) -> None:
37
+ # timestamp handling
38
+ if timestamp > self.last_timestamp:
39
+ assert timestamp > 0
40
+ self.last_timestamp = timestamp
41
+ else:
42
+ # fix invalid timestamp (NTP adjustment?)
43
+ self.last_timestamp += 1
44
+ timestamp = self.last_timestamp
45
+
46
+ # check if this is a value transition
47
+ if self.index > 0:
48
+ last_index = (self.index - 1) % self.size
49
+ if self.buffer[last_index][1] == value:
50
+ # not a transition... just discard the new sample -- it brings no information actually
51
+ # (to be fair: it provides the information that the value is STILL the same... but this
52
+ # is assumed implicitly to be the case when no new samples are present)
53
+ return
54
+
55
+ self.buffer[self.index % self.size] = (timestamp, value)
56
+ self.index += 1
57
+
58
+ def get_all_samples(self) -> list:
59
+ if self.index == 0:
60
+ return None # buffer is empty
61
+ if self.index <= self.size:
62
+ return self.buffer[: self.index] # return only valid/populated items
63
+ idx_mod = self.index % self.size
64
+ return self.buffer[idx_mod:] + self.buffer[:idx_mod] # linearize the circular buffer
65
+
66
+ def get_last_sample(self) -> tuple:
67
+ return self.get_past_sample(1) # look 1 sample in the past, which means the last pushed sample
68
+
69
+ def get_past_sample(self, sample_offset_in_the_past: int) -> tuple:
70
+ if self.index == 0:
71
+ return None # buffer is empty
72
+ if self.index < sample_offset_in_the_past:
73
+ # the caller is requesting a sample too much in the past -- beyond the buffer memory
74
+ return None
75
+ if sample_offset_in_the_past < 1:
76
+ # sample_offset_in_the_past==0 (or negative) is not a sample in the past
77
+ return None
78
+ if sample_offset_in_the_past > self.size:
79
+ # the caller is trying to explore too much in the past -- beyond the buffer memory
80
+ return None
81
+ last_index = (self.index - sample_offset_in_the_past) % self.size
82
+ return self.buffer[last_index]
83
+
84
+ def get_stable_sample(self, now_ts: int, min_stability_sec: float) -> tuple:
85
+ if self.index == 0:
86
+ return None # buffer is empty
87
+ if now_ts < self.last_timestamp:
88
+ # fix invalid timestamp (NTP adjustment?)
89
+ now_ts = self.last_timestamp
90
+ # starting from the last sample search going backwards the first "stable" sample:
91
+ last_ts = now_ts
92
+ sample_offset_in_the_past = 1
93
+ while sample_offset_in_the_past <= self.size:
94
+ s = self.get_past_sample(sample_offset_in_the_past)
95
+ if s is None:
96
+ # trying to dig too much into the past... perhaps the circular buffer is not completely full yet...
97
+ return None
98
+
99
+ sample_age_sec = last_ts - s[0]
100
+ if sample_age_sec >= min_stability_sec:
101
+ # debug only:
102
+ # print(
103
+ # f"sample_offset_in_the_past={sample_offset_in_the_past} -> sample_age_sec={sample_age_sec} -> STABLE for threshold {min_stability_sec}"
104
+ # )
105
+ # found a stable sample!
106
+ return s
107
+
108
+ # debug only:
109
+ # print(
110
+ # f"sample_offset_in_the_past={sample_offset_in_the_past} -> sample_age_sec={sample_age_sec} -> UNSTABLE for threshold {min_stability_sec}"
111
+ # )
112
+
113
+ # keep going backward
114
+ sample_offset_in_the_past += 1
115
+ last_ts = s[0]
116
+
117
+ # the whole buffer has been inspected but all value transitions were shorter than 'min_stability_sec'
118
+ return None
119
+
120
+ def clear(self) -> None:
121
+ self.buffer = [(None, None)] * self.size
122
+ self.index = 0