Skip to content
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

Implement virtual device support #139

Closed
wants to merge 1 commit into from

Conversation

derjoerg
Copy link
Collaborator

This is the first try to implement virtual device support. No tests are written yet as I first want to have consensus about my approach.

I re-implemented everything what I removed in #103 and created dedicated ...Virtual classes.

For now I only implemented a "WindowDoorSensor" and a "SwitchActuator".

As the return of the F@H-API regarding virtual devices interface=vdev:... seems to be unreliable I moved to a check of the device_id: All my tests showed that a virtual device device_id always starts with 6000

@derjoerg
Copy link
Collaborator Author

I think especially the modifications I've done in freeathome.py need to be checked. As I'm not an expert in python there might be some potential for improvements 😃

In general the handling of a virtual device in F@H is exactly 100% reversed to the handling of "normal" devices. This means to change the state from the outside we need to write to output-datapoints and changes from F@H are written to input-datapoints where we need to react on (callbacks).

@derjoerg
Copy link
Collaborator Author

The communication flows are as follow:

  1. If an external program wants to modify the state of a virtual device it needs to write to the appropriate output-datapoint
  2. If F@H modifies the state of a virtual device it updates an input-datapoint (which an external program needs to consume) and the external program needs to "confirm" this by writing to the appropriate output-datapoint (see point 1.)

A virtual device in F@H is fully controlled by an external program, so modifications by an external program are treated as fact in F@H. If F@H wants to modify a virtual device it sends a "wish" to the external program and the external program needs to aknowledge this wish (if not, the state change in F@H is reversed)

Copy link
Owner

@kingsleyadam kingsleyadam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we try to keep the virtual device logic completely out of the existing devices classes. Maybe having another directory virtual_devices including a new base class with the existing base class as the parent. Then only adding in the logic required (or changing existing logic e.g. refresh_state() for virtual devices.

I think once we start mixing logic in existing classes it'll start to blur the lines between a normal device and a virtual device.

My review of this is a little bit all over the place, I realize that.

@@ -16,4 +16,3 @@ class Interface(enum.Enum):
WIRELESS_RF = "RF"
HUE = "hue"
SONOS = "sonos"
VIRTUAL_DEVICE = "vdev:[email protected]"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this is technically supposed to be there I'd keep it. Maybe ABB will implement a fix if we bring it to them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After first tests within the HA-integration I also found at that it is needed as - at least in my setup - I have some virtual devices with the interface-setting and some without (UNDEFINED).
So at the moment e.g. for HA I need to include Interface.VIRTUAL_DEVICE and Interface.UNDEFINED and additionally check for the serial-number. 😢

Comment on lines +152 to +168
for _pairing in self._state_refresh_input_pairings:
_input_id, _input_value = self.get_input_by_pairing(pairing=_pairing)

_datapoint = (
await self._api.get_datapoint(
device_id=self.device_id,
channel_id=self.channel_id,
datapoint=_input_id,
)
)[0]

self._refresh_state_from_input(
input={
"pairingID": _pairing.value,
"value": _datapoint,
}
)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want to apply this logic to all devices as it's only required for virtual devices. I think a self._is_virtual_device attribute makes sense. Then only running the refresh state from the api for either inputs, or outputs, depending on if it's a virtual device.

Completely different, I'd probably rename this to refresh_state_from_api.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will think about it. Also if we will introduce an additional base class, perhaps some of the logic can be moved to it and makes it more clearer

Comment on lines +32 to +35
FUNCTION_DEVICE_MAPPING_VIRTUAL: dict[Function, Base] = {
Function.FID_WINDOW_DOOR_SENSOR: WindowDoorSensorVirtual,
Function.FID_SWITCH_ACTUATOR: SwitchActuatorVirtual,
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need another variable for this? I'd probably keep this within the FUNCTION_DEVICE_MAPPING variable. I think adding another variable adds some additional complexities.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how else you want to make the distinction between the "Function Device Mapping" of normal devices and the "Function Device Mapping" of virtual devices (they share the same functionIDs but need different classes). As you see in my implemented examples with this setup we can easily control which virtual devices we support.

Comment on lines +65 to +67
# Filter out virtual devices
if _device_key[0:4] == "6000" and not self._include_virtual_devices:
continue
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any documentation that supports all virtual devices will start with 6000?

@derjoerg
Copy link
Collaborator Author

Ideally we try to keep the virtual device logic completely out of the existing devices classes. Maybe having another directory virtual_devices including a new base class with the existing base class as the parent. Then only adding in the logic required (or changing existing logic e.g. refresh_state() for virtual devices.

I think once we start mixing logic in existing classes it'll start to blur the lines between a normal device and a virtual device.

My review of this is a little bit all over the place, I realize that.

I'm absolutey happy to split this up. I'm not sure if also a dedicated directory is needed. If we just create a new base-class for the virtual devices e.g. BaseVirtual and have - as convention - the naming standard that everything regarding virtual devices end with Virtual

@derjoerg
Copy link
Collaborator Author

derjoerg commented Jan 9, 2025

I will close this pull-request and start from scratch.

@derjoerg derjoerg closed this Jan 9, 2025
@derjoerg derjoerg deleted the virtual_devices branch January 22, 2025 16:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants