I only skimmed this thread, so if I repeat something, oops.
First off, I applaud the intention to learn from the HASS data. We tried to promote that with Jupyter notebooks but it did not get any traction.
So now about the data in Home Assistant.
First, there are 5 different types of devices and how they tell Home Assistant about their state: assume, cloud poll, cloud push, local poll, local push (read more about what they are here).
When we call a service, let’s say from the frontend, the code looks as follows (slightly simplified):
yield from device.turn_on()
if device.should_poll:
yield from device.update_ha_state()
That means that if we are polling a device, and we refresh a device right after calling a method on it, we can assume that the state update was because of the method we called. However, it doesn’t have to be: if we update device at T, call a method at T+15 and update the state right away, any change happened in the last 15 seconds could be attributed to our service call.
It’s different for devices that push their state. In that case we will not ask for a state update after calling a method because the device will let us know when something has changed. In this case we could add some time based logic: if a new state gets pushed and we called a method within a second, attribute the state change to the method we called. However, now we are hard coding this and the time might be different per device or your algorithm sees it differently.
All of this gets even more complex when you start turning on groups or scenes, as it can impact a lot of different things.
So there is no consistent approach to attribute a state change to a device. It also would require a significant change to the core and every component.
An easier and cleaner solution would be to add a source attribute to service calls. That way, it’s up to the parser of the event stream to decide which state changes should be attributed to what. The nice thing here is also that the source parameter could be inherited. So when you call a service to activate a script and the script then turns on a light, we could still attribute that 2nd call to the original source.
For source names, we should use the entity_id of the source if possible and fall back to just the domain. Examples of sources:
-
rest
(should be api
because that’s how the component is called but that’s confusing…)
websocket
-
automation.all_gone
(we can differentiate between automations using entity_id)
-
device_sun_light_trigger
(this is an automation component without entities)