-
-
Notifications
You must be signed in to change notification settings - Fork 428
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable binding to store historic states #3000
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your contribution. I have done an initial review, but I also have a general question: What happens if the state has a date this is in the future? Also please check for the SAT errors.
...rg.openhab.core.thing/src/main/java/org/openhab/core/thing/binding/ThingHandlerCallback.java
Outdated
Show resolved
Hide resolved
bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/ItemUpdater.java
Show resolved
Hide resolved
bundles/org.openhab.core/src/main/java/org/openhab/core/items/events/ItemEventFactory.java
Show resolved
Hide resolved
bundles/org.openhab.core/src/main/java/org/openhab/core/items/events/ItemEventFactory.java
Show resolved
Hide resolved
...les/org.openhab.core/src/main/java/org/openhab/core/items/events/ItemHistoricStateEvent.java
Show resolved
Hide resolved
....persistence/src/main/java/org/openhab/core/persistence/internal/PersistenceManagerImpl.java
Outdated
Show resolved
Hide resolved
...es/org.openhab.core.thing/src/main/java/org/openhab/core/thing/binding/BaseThingHandler.java
Show resolved
Hide resolved
...es/org.openhab.core.thing/src/main/java/org/openhab/core/thing/binding/BaseThingHandler.java
Outdated
Show resolved
Hide resolved
...es/org.openhab.core.thing/src/main/java/org/openhab/core/thing/binding/BaseThingHandler.java
Show resolved
Hide resolved
bundles/org.openhab.core.thing/src/main/java/org/openhab/core/thing/internal/HistoricState.java
Show resolved
Hide resolved
wow, that was fast. I must apologize. I won’t be able to react that quickly. Please give me a few days. |
@J-N-K i agree with your proposals and have made changes accordingly.
that date is treated as any other. In other words, the binding is responsible for providing a correct date. There may be instances where a future date is needed. Maybe some kind of forecast? I cannot think of anything useful now. |
While I like a lot that this topic come to concrete form I suppose that a bit better way would be proper tagging of states instead of multiplication of events. As outlined - bindings can provide estimations which are ahead of time, hence it would require another kind of event. I think that making a type
Thing is, if core is ready for such step? |
@splatch I do not fully understand your comment. What do you mean by 'can still be a valid state'? |
@altaroca roger, I missed type itself and got lost by changes in other areas. If you rely on the state then main question I could raise is why distinguish situation by introducing I see a point in making such state a first level citizen because some of bindings could provide a timestamp of received update. To give more concrete example - a CAN interface controller can attach timestamp to the message which delivers state update. Binding then knows exact timestamp of an update and it is not a historic state per say as it is instant value with receive time information. |
@splatch I had thought about that and decided against it. There were two reasons:
|
interface WrapperState extends State /*, ComplexType? */ {
State unwrap();
// 24.08.2022 - navigate to specific type of state under wrapper
<T extends State> T unwrap(Class<T> type);
<T extends State> boolean has(Class<T> type);
}
class TimeBoundState implements WrapperState {
private DateTime dateTime;
private State state;
State unwrap() {
State myState = state;
while (myState instanceof WrapperState) {
// it can be also moved to State/Type itself so DecimalType will always return this, but wrapper states will unpack themselves
myState = myState instanceof WrapperState ? ((WrapperState) myState).unpack() : myState;
}
return myState;
}
// ..
}
// for callers doing persistence and other parties which need to process concrete state instance
State actualState = state instanceof WrapperState ? ((WrapperState) state).unpack() : state;
if (actualState instanceof DecimalType) {
...
}
// 24.08.2022 - unpacking to a specific type
DecimalType actualState = state instanceof WrapperState ? ((WrapperState) state).unpack(DecimalType.class) : state.as(DecimalType.class);
if (state instanceof DecimalType || (state instanceof WrapperState && ((WrapperState) state).has(DecimalType.class)) {
DecimalType dec = state instanceof DecimalType ? (DecimalType) state : ((WrapperState) state).unpack(DecimalType.class);
}
|
Err, no. That's not my point. I meant to say that no existing code will understand the meaning of types like
Of course. I completely agree. And I do not care about the name. It's up for discussion what's the best name: What I mean is that people, seeing |
IMO if timestamps in the future are allowed, something like I wonder if it would not be better to extend This could also replace the need for the |
@wborn Since you created the original issue: I would like your opinion here. WDYT about adding a timestamp to |
This pull request has been mentioned on openHAB Community. There might be relevant details there: https://community.openhab.org/t/panasonic-comfort-cloud-binding/133848/26 |
@openhab/core-maintainers I would really like to hear your opinion here. |
Thanks for working on this @altaroca! 👍
I don't have a strong opinion but it seems that in previous related PRs the timestamp always has been kept separate from the See for instance this code: |
Yes, but I don't understand the reasoning behind that. From what @kaikreuzer said in #3033 a possible argument is that this timestamp might not be the actual timestamp of the value change. While this might be true, if the timestamp is @nullable, a binding can chose not to set it, if it is not known. But having a timestamp together with a state will remove the need for extra-code to handle future values (e.g. forecasts) and historic values (retrieved from persistence service) and will also allow bindings that CAN determine the exact point in time of a value change to communicate that. And it is fully backward compatible because all add-ons agnostic of the timestamp will not be affected (they produce states without timestamps, just like before and they consume states ignoring the timestamp, just like before), but persistence services can properly store the timestamp together with the value. If no timestamp is present, they use the system time, like it is now. |
i cannot foresee all downstream implications of this change. Let's approach it semantically:
This definition does not refer to a timestamp. So if you wanted to always combine a State object with a time you should at least call it differently, such as StateWithTime or the above TimeboundState. Now the question is: can openhab do without a State object and can you replace it in all places with a TimeboundState object? |
Adding forecasts on the same channels probably will also have its own set of issues. You probably don't want to persist forcasted state and actual state using the same item. I can imagine there are also devices that store their forecasts made at a certain time in a buffer. So if you then read these forecasts at a later time you might want to persist "historic forecasts". 😉 |
It's questionable if someone wants to persist forecasts at all (maybe to use My concern is that we now have Keeping aside that this leads to a very bulky API (and therefore a high burden for developers), I doubt that users will benefit from that. We now separate I doubt that this |
I do see usecases for persisting forecasts - electricity prices and weather are two. If my network goes down and openHAB is restarted, I would loose the ability to make decisions on these data without persistence. IMHO a The Could we explore the feasability to create new |
The thing handler callback have already bunch of methods, adding one more does not impact it much (its already over bloated and does everything from configuration validation through thing status ending with channel updates). Basic fact that REST resource does not use communication manager does not mean that it can't be used elsewhere. It is an internal component which is not used directly by any REST endpoint (even SSE which is closest by nature). Reason why I was mentioning profiles is because they could be used i.e to calculate mean/average over a moving window, effectively delivering re-computed state from forecast and time |
I do not see much value here as forecast values will very often come in bulk. If you process 240 events for updated values for the next 24 hours sequentially, it will not only cause huge unnecessary CPU utilization, but also can result in inadequate intermediate results, because the full picture simply wasn't there yet. |
@kaikreuzer I didn't mean ingestion of future/past states. What I meant is necessity to continuously poll database in order to re-calculate items which are derivative of these states. While rules have a cron trigger, using that mechanism to average i.e. future temperature predictions will cost extra. Doing that in profile which can keep last couple of states will be lighter than above. |
I am also interested in this for various use cases. I drafted this flowchart: Some notes:
Therefore, I am thinking that adding timestamps both to the State and Events may seem more work but it may help with ingestion and persistence of historical/future states. With the added benefit that it will make it easier (and more performant) for users to have access to lastUpdate in rules and the UI (with some extra work there) without reaching to persistence extensions or creating DateTime items. (The mermaid diagram is here for anyone wanting to make changes) |
@splatch Can't fix stupid. You can already create rules that run endlessly and also create two rules that always call each other. IMO applying a profile that does such things just makes no sense (while a profile that converts values or data types makes sense and is not critical). @henfiber Conceptually it would make more sense to couple timestamp and state than timestamp and event, especially since we then would not need any additional methods. States without timestamps are considered to be "now" (like it is at the moment) and everyone not interested in future or past events can simply discard every event that has a timestamp. Items would only set states that have no timestamp. That aside: We don't need new events. Since #3141 we differentiate between
so using @kaikreuzer Bulk updates are another issue, we currently have no means to do so (although I see a benefit for that). This should really be a new event type but there is another issue: If we don't couple timestamps and states but timestamps and events we can't transport states for different timestamps in one event. |
I'm not sure if I understand this correctly, but shouldn't this work pretty much like now, i.e. the persistence service configured for item My doubt is if we are considering getting anything back from persistence, i.e. like restoreOnStartup? I would assume restoreOnStartup to be able to push back time-bound states as well, similar to what bindings would do. But besides that, shouldn't we be able to handle time-bound states completely independent of persistence services? If a binding would update an item not persisted with a state one hour into the future, this should work. But what will happen in an hour, will there be an event that the (current) item value changed? If not, all bindings would have to implement this by themselves. And if yes, then how will it work? Will it be feasible to keep queued future states in memory, or do we need persistence? The persistence we might need here I see as something different from the persistence the user has configured for items. This is persistence needed by the event bus to keep track of future state updates, and only to preserve memory. Also it should work for all items whether persisted or not by persistence services. When openHAB is restarted, we can flush all this. It may be repopulated by bindings or by persistence services when using restoreOnStartup, just like the current states are repopulated or restored today. Sorry if I misunderstood something, just want to understand the thinking here and be sure we are not coupling existing persistence services too much with the need for the event bus to be updated when future states become present. |
A possible way would be that the item keeps track of future states and schedules its own update for the time that the state is for. When However, for calculations like "when is the best time to start the dish washer" we would still probably need persistence. |
This would be an ideal solution in my opinion. This would allow for example to use the same spot price Item to for "price right now" UI element. At the moment, I have an hourly Rule that reads the price for the current hour from my database and updates the "spot price" Item.
Yes, I think so. At least I am very curious to do analysis of the "control points" vs. "actuals". |
Exactly. This is similar to what I currently (have to) do manually in the EDS binding:
Yes, so they will then also keep track of future states and schedule updates after being restored.
Definitely. Comments above were meant for the event bus. Here we need access to persistence from the Energy Management Service. |
This PR already contains parts of what we discussed, but there is also a lot missing (e.g. the scheduling and restoring part). And of course it should not be |
@J-N-K it seems that many people have interest from a slightly different angle in this initiative, so I think you're right to be asking for some consensus before spending time on this. I will be happy to contribute (testing, updating docs, code if possible etc.) regardless of the decisions. That said I have some comments. Let me summarize what I have understood as your current proposal:
Please correct me if I have misunderstood something. My questions/comments on that: 1. Will a binding be able to send a timestamp with every state and make the items (indirectly) updated with this timestamp-state pair? From your previous comment, I understand that this won't be the case:
In my view, Items should also set states that have a timestamp. With the condition that the timestamp is newer than the previously received timestamp (stored in If Items only set states without a timestamp, then:
So, it will not be possible to both send timestamped measurements and set the current state at once without introducing inconsistency or duplicates. 2. Will the timestamp(s) be available to SSE consumers? If the state will only be coupled with the State and not with events, will SSE consumers have a way to receive the timestamp with the stream of state updates? For a Java binding it may not be an issue to get the timestamp from the registry, but it may be an issue for SSE consumers like Habapp or the UI if they need to make an extra HTTP request to get the timestamp on every change. I have read your concerns regarding event timestamps breaking the strict order in the SSE stream, but I don't see why OpenHAB should change the order. It should continue emitting the events in the incoming order. The timestamps would be just reported as an extra attribute. It is up to the SSE consumer to re-order the events (by introducing latency/buffering) if an application has such a requirement. 3. Should we save the timestamp only when set by a binding? I would argue that it would be useful to store the timestamp even if a binding did not include one with the state. In this case, it would be the ingestion time, i.e. the system clock time ("now") when the new state was received. With the suggested (and current) approach the ingestion time is not stored at all, so we have to use the consumer's time. Persistence services are one of the consumers and some of them use their own An even greater issue is when someone opens the OpenHAB dashboard after some time or makes a REST API call to Finally, the absence of a stored ingestion timestamp breaks "idempotence". i.e. the state cannot be uniquely identified by its value and we cannot enforce at-most-once or exactly-once semantics if requested or forwarded multiple times. The need for idempotence arises in distributed scenarios such as syncing with remote OH instances or MQTT brokers, or if we added for instance a streaming/queue persistence service such as Kafka. In contrast, if the timestamp was always saved (in the existing Now, all that said (and apologies for the long comment), even if this is implemented as suggested it will be an improvement to the existing situation. Just wanted to share some concerns in case others have similar use cases. |
For me it feels that this discussion goes into a fairly wrong direction and I do not agree with the summary of @henfiber, sorry. We already had different options discussed above and for various reasons, I still prefer option 3. For the requirement that this PR wants to address, I do not see the need to have timestamps added to existing state update events. This would introduce many issues that were already mentioned above (like ordering, measurement time vs. processing time, the fact that persistence services persist states, but not events, bulk update handling, ...) and I believe this is a fully independent discussion that should not be in scope here. Please also note that imho the requirement in this PR is also primarily about bulk updates of history. Be it missing history that is recovered and added as bulk from a cloud service, be it the prices for electricity for every hour of the next day, be it the weather forecast for the next 7 days. In all these use cases, single events do not make sense on their own, but what would be needed is an event that a certain historic (past or future) time range has just been updated - that's why I suggested a new event type that is issued by the persistence manager. |
I also don't think we need timestamps on events, I think we need to find a way to couple timestamps and states, and probably also (as @kaikreuzer correctly points out) also a way to group several of these "combined states" for bulk updates. Nothing of that requires timestamps for events and if I understood correct what @kaikreuzer is saying, I also agree that we should probably not re-use the existing events for that and I'm fine with using "option 3" if the item and persistence manager are not bypassed by these methods. But I think we cannot separate the discussion here from the "full picture" (maybe it would have been better to start a discussion independent from the code here). Historic data and forecasts are quite similar in that they don't represent the current state of an entity but the state of that entity at a different point in time. Inserting them in persistence is only the half way for using them (it's fine for historic events, but not sufficient for forecasts). I'm not saying that the "second half" needs to be addressed in this PR (or even in the very near future), but we should have a picture of what the design should look like, so we don't introduce something now that we have to revert or break because we find out that we forgot something very important that we need to use the forecasts for our energy management system. We already have a coupling of states and timestamps, the This is what should be covered here. In a follow-up PR we can add filters to persistence services so that different persistence series attached to the same item can be used for current states and the timed states and (if we agreed that it is desirable) an update/restore logic for future values for the item. |
I think that's a good idea. It's trivial to have a binding generate a Or will this still behave like a normal item and there will be a function to query the current value from persistence which can only be used in rules. |
Where do you see a difference here? (Btw, ChatGPT suggested to use the expression "time-series data" as it covers historic data and forecasted data equally well.)
To avoid the "weird" name, we might also think about a In general, I like your proposal to pass it this way through the |
WDYT? Regarding the difference: for historic data we have a good coverage of everything we need in the If we allow future state in the persistence, how we determine the state that we restore, if we want to automatically update the item when we reach the respective time. None of this would affect the persistence itself, but I would like to discuss as much of the design as possible before we add code, since there might be something that you or me don't think of at the moment which needs fundamental changes - even though I don't really expect that. So my summary would be:
Open questions:
The actual implementation of 4 can be discussed in the future. If the answer to 3b is "the persistence service" then it needs more changes in the services. |
I would see this as a responsibility of the
Sounds good to me.
|
Looks like we have the same picture now, nice. @altaroca Do you also agree and would you be willing to adapt the PR accordingly? |
@J-N-K, @kaikreuzer I understand that the discussion regarding the implementation of this feature is nearing its final decision, and I respect the expertise and knowledge that you and the other core maintainers bring to the table. However, I would like to kindly request a reconsideration of the concerns I raised in my last comment, as I believe they could lead to a more robust and effective solution. I will rephrase and shorten them a bit here:
If I should rank their relative importance I would go with 1 > 3 > 2. So, we may ignore the 2nd one for now, i.e. attaching timestamps to events. The 3rd one can also be addressed separately although I think it is closely related to what we are discussing here. Regarding the 1st corcern though, I have listed a common case which I believe cannot be handled effectively if the I apologize if I come across as overly persistent. I am genuinely interested in this feature, so I am trying to provide feedback in the spirit of what @J-N-K mentioned:
|
|
If
I think you have covered the future states situation nicely. What I am more concerned about is the "gray" area between what is considered "past" and what "current" state. For me the "current" (virtual) state is the latest state we have received before "now" (with or without timestamps attached). EDIT: Just noticed that in your previous comment you mentioned something similar for
This is my exact whole point, the last value before "now" should be the state, but not only for restoring state from persistence, but also for updating state from I will explain below my perspective about the issues arising when Items only accept states without timestamps. How historical state is usually retrievedDevices or services that keep a local history of measurements, do not usually separate between "past" and "current" data. They forward, export or return the last measurements in one of the following ways:
I don't know of any device that sends only "past" data (i.e. excluding the current, latest state), unless it has an API where you can explicitly add a filter argument to do so. Specific example: OwnTracksLet's look at a specific example, OwnTracks, and see how it operates and what happens depending on how we handle historical states. The OwnTracks json payload includes a
So,
Let's see the sequence of events: We have an OpenHAB item OwnTracks detects a location update (at 2023-04-18 13:38:25) and queues the message due to a lack of connectivity:
OwnTracks detects a second location update (at 2023-04-18 13:39:25):
OwnTracks now has connectivity, so it first sends the previously queued message and then the latest one (FIFO). What should be the in-memory and persisted states of I would expect that:
What will be the resulting OH state with currently accepted implementation plan? From my understanding:
i.e. TimeSeries updates are only concerned with Past or Future states but not the current state. So we end up with this situation: The Since it does not make sense to leave the If we want to avoid the duplicates, then we somehow need to separate the last ("current") value from the "past" (queued) values. So we end up with this situation: Unfortunately, this is not possible for FIFO (non-batch) updates since the binding cannot know which update is the latest. And is not ideal from a consistency perspective since we end up with mixed clock sources. Finally, we can completely ignore the timestamps and only send Therefore, what I advocate for, is the situation in the first diagram which is accomplished by updating the state of the Which seems a reasonable expectation to me since an OpenHAB item's state represents the most recent received state. Appendix: Devices that may operate in a similar manner to OwnTracks
Regarding (3)
I will try to explain it better in a subsequent comment. Apologies again for my long comment, hopefully you will find it helpful. |
I don't think this is an issue. The |
Hi all, I thought I'd drop a note to others in this thread that I'm most likely not able to contribute too much on making the energy optimizations after #3597 lands even though I was earlier showing strong interest to contribute and generalize these things to be as easy for non-developer background users as possible. We got fantastic news a couple of weeks ago that the Finnish Olympic Committee will support our curling team next winter season on our run to Milano Cortina 2026. This will mean that I will work less to be able to curl more, which means that when I'm home I want to spend that time with the family. Cheers, |
Hey @masipila, That's an awesome opportunity for you! All the best for your preparations and whenever you feel you need a break from curling and dive into some code instead, please think of us! |
This pull request has been mentioned on openHAB Community. There might be relevant details there: |
This pull request has been mentioned on openHAB Community. There might be relevant details there: https://community.openhab.org/t/entso-e-binding-for-nordpool-spot-prices/143833/19 |
Superseded by #3597 |
please refer to #844
this adds a method
BaseThingHandler.updateHistoricState (ChannelUID channelUID, State state, ZonedDateTime dateTime)
and all logic to route the correspondingItemHistoricStateEvent
through the core to the persistence layer and anyModifiablePersistenceService
.I have tested it using my fork of the E3DC binding and influxdb modified to implement
ModifiablePersistenceService
.I made some choices for simplicity sake: