openhab / openhab-addons

Add-ons for openHAB
https://www.openhab.org/
Eclipse Public License 2.0
1.85k stars 3.56k forks source link

[boschshc] Add support for Relay - 'MICROMODULE_RELAY' #16590

Open mike-bike opened 3 months ago

mike-bike commented 3 months ago

This request is to add support for another Bosch Smart Home device, namely Relay:

From openhab.log: Unknown deviceModel 'MICROMODULE_RELAY'! Please create a support request issue for this unknown device model.

2024-03-28 [DEBUG] [rnal.discovery.ThingDiscoveryService] - Found device: name=Relais Test id=hdm:ZigBee:30XXXXXXXXXXXXXX
2024-03-28 [DEBUG] [rnal.discovery.ThingDiscoveryService] - .... service: CommunicationQuality
2024-03-28 [DEBUG] [rnal.discovery.ThingDiscoveryService] - .... service: PowerSwitch
2024-03-28 [DEBUG] [rnal.discovery.ThingDiscoveryService] - .... service: ChildProtection
2024-03-28 [DEBUG] [rnal.discovery.ThingDiscoveryService] - .... service: PowerSwitchProgram
2024-03-28 [DEBUG] [rnal.discovery.ThingDiscoveryService] - .... service: ElectricalFaults
2024-03-28 [DEBUG] [rnal.discovery.ThingDiscoveryService] - .... service: SwitchConfiguration
2024-03-28 [DEBUG] [rnal.discovery.ThingDiscoveryService] - .... service: Linking
{"@type":"device","rootDeviceId":"64-da-a0-10-83-a7","id":"hdm:ZigBee:30fb10fffe46d732","deviceServiceIds":["CommunicationQuality","PowerSwitch","ChildProtection","PowerSwitchProgram","ElectricalFaults","SwitchConfiguration","Linking"],"manufacturer":"BOSCH","roomId":"hz_16","deviceModel":"MICROMODULE_RELAY","serial":"30FB10FFFE46D732","profile":"GENERIC","iconId":"ip_heat_pump","name":"Relais Test","status":"AVAILABLE","childDeviceIds":[],"supportedProfiles":["LIGHT","GENERIC","HEATING_RCC"]}

Please let me know if I'd need to provide further details or can support testing.

Happy Easter!

Regards, Michael

david-pace commented 3 months ago

Thanks for the enhancement request. Assuming you can help with testing again we can start coding as soon as someone finds some spare time :+1:

mike-bike commented 3 months ago

Absolutely! Just let me know when you have something ready to test. My PI2 test bed is always yours :-)

Happy Easter!

Kind regards Michael

Am 31.03.2024 um 15:12 schrieb David Pace @.***>:

Thanks for the enhancement request. Assuming you can help with testing again we can start coding as soon as someone finds some spare time 👍

— Reply to this email directly, view it on GitHub https://github.com/openhab/openhab-addons/issues/16590#issuecomment-2028719695, or unsubscribe https://github.com/notifications/unsubscribe-auth/AHVH7GBTL7ATBMQU3WAVTP3Y3ADTNAVCNFSM6AAAAABFM3A4SKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAMRYG4YTSNRZGU. You are receiving this because you authored the thread.

david-pace commented 3 months ago

Hi @mike-bike, apparently the relay can have different configurations. I see the following supportedProfiles in the JSON example above:

From what I've learned in https://github.com/tschamm/boschshc-hass/issues/101, one of these modes (the "light" mode) features a "toggle" button and the impulse switch mode only has a "push" button.

Can you please check/answer the following:

mike-bike commented 3 months ago

I'd need to check the logs to confirm the services/responses when the configuration changes. Maybe I find some time later today or tomorrow.

I am trying to answer your other questions:

  1. The BOSCH Relais is fully configurable via the app.
  2. There are 4 different "modes" as you could configure the device. These modes define further options (I'd assume these to be PowerSwitchProgram)
    • Generic mode: if nothing else fits better
    • Light mode: connected devices will be treated as a "light" in the app, e.g. switched on with "room light" or used in alarm system and motion sensors
    • Elektro heating: require assigned room thermostat to turn off relay when temp matches
    • Boiler: similar to Electro but slightly different timing to turn off relay
  3. Available switch configuration (SwitchConfiguration) are the same for all 4 modes above. I'd assume these to be the same as for the other BOSCH controller devices: It tells the device if a impulse (Taster) or on/off (Wippschalter) switch is connected to change mode - i.e. toggle state with each electric impulse or by on/off change
  4. I do not have any clue about ElectricalFaults and could not find anything in the app. Potentially a counter or flag which indicates that the power has been interrupted and the relays is in Off-state again...?

I will amend my findings after reviewing the logs

mike-bike commented 3 months ago

Hi @david-pace, after reviewing my previous post, I do not think you need to worry much about the relay's device configuration. I think these parameters only drives the BOSCH bridge/app internal functionality:

From thing's perspective I'd see it as just an on/off switch. The SwitchConfiguration defines the type of physically wired switch, so that the device would switch on/off on electrical impulse (Taster) or current (Wippschalter). Neither is relevant for openHAB other than information only - though an item connected to the SwitchConfiguration or PowerSwitchPorgram could be used for presentation purposes - e.g. display different icons for the switch state; but at the end of the day it is ON and OFF only.

Though it would be nice to expose the timer to automatically switch off. This is available in multiple BOSCH devices. With that, openHAB could change the timer based on other conditions (e.g. time of day / year, etc).

Still wondering about ElectricalFaults and Linking.

Unfortunately I cannot complete the testing because I do not have a Room Thermostat available to configure Electrical / Boiler modes. Again, I don't think it will differ based on my findings above.

mike-bike commented 3 months ago

I have tried different configuration, but the result seem to be always the same. We probably need to have a thing configured to get more details if needed:


{"@type":"device","rootDeviceId":"64-da-a0-10-83a7", 
"id":"hdm:ZigBee:30fb10fffe46d732",
"deviceServiceIds["CommunicationQuality","PowerSwitch","ChildProtection","PowerSwitchProgram","ElectricalFaults","SwitchConfiguration","Linking"],
"manufacturer":"BOSCH",
"roomId":"hz_16",
"deviceModel":"MICROMODULE_RELAY",
"serial":"30FB10FFFE46D732",
"profile":"GENERIC","iconId":"ip_heat_pump",
"name":"Relais Test",
"status":"AVAILABLE",
"childDeviceIds":[],
"supportedProfiles":["HEATING_RCC","LIGHT","GENERIC"]}
david-pace commented 2 months ago

Thank you for the information, @mike-bike :+1: In this case I would propose to implement a minimal set of services and then provide a test JAR. Then you can test with the hardware how the individual services react in which mode.

mike-bike commented 2 months ago

Sounds like a plan 😀 Regards Michael

david-pace commented 2 months ago

Hi @mike-bike, good news, I have a first implementation ready that you can test 😎

Here is the link to the Test JAR.

I think you already know the testing procedure, but if you have any doubts or questions please let me know :+1: I'm excited to hear what already works and what does not work 😉

mike-bike commented 2 months ago

Hi @david-pace, refreshed the test-bed and loaded the new JAR.

I think, I experienced similar issues as with the first test of the Light Switch II. I recall, that you quickly have identified the root cause and provided a fix. So I am expecting nothing serious.

Good progress! Regards, Mike

Quick summary:

  1. Binding loads and seem to continue supporting the previous devices
  2. Relay has been discovered into in-box
  3. Implemented Thing and created/linked items
  4. Test results below
  5. no obvious issues reported in the log (even with TRACE)

PowerSwitch: Switching On/Off is working in openHAB Changes in Bosch App are not reflected in openHAB

Childprotection: Setting ChildProtection in openHAB changes state in Bosch App Changes to ChildProtection in Bosch App is not reflected in openHAB

PowerSwitch Program: Not sure about the purpose. Should it be a status indicator from the Bosch device whether it is running timer controlled or manually? Changing state in openHAB does not change anything in the App or vice versa (Toggle between Manual/Automatic?) Anyway, the same service should be available for many other Bosch devices as well. So I'd suggest a consistent approach.

ImpulsSwitch is not working Honestly I do not know, how this should be reflected in openHAB. I am not aware of an Impuls Switch as a device in openHAB. Technically this should be a push-button which one can preset and it will then be automatically release to toggle a state. I’d consider a more deterministic approach more useful: opening or closing the relays, rather than toggle into an unknown state. This could be mimicked by a run if needed.

After unlinking and removing ImpulseSwitch Item, Thing turned immediately into GREEN

Error in Thing:

COMMUNICATION_ERROR 
Error when trying to refresh state from service ImpulseSwitch: State request with URL https://192.168.2.61:8444/smarthome/devices/hdm:ZigBee:30fb10fffe46d732/services/ImpulseSwitch/state failed with status code 404 and error code ENTITY_NOT_FOUND
deviceID: hdm:ZigBee:30fb10fffe46d732                                                                                                       type: MICROMODULE_RELAY -> relay
service: CommunicationQuality -> communicationquality
service: PowerSwitch -> powerswitch 
service: ChildProtection -> childprotection
service: PowerSwitchProgram -> powerswitchprogram
service: ElectricalFaults -> !UNSUPPORTED! 
service: SwitchConfiguration -> !UNSUPPORTED! 
service: Linking -> !UNSUPPORTED!
david-pace commented 2 months ago

Thanks for the initial test 👍 Could you please look in your logs for example requests/responses for the relevant services? You can identify them by the following @type attributes (or at least similar types, spelling might be a bit different):

I need to know the exact spelling and a few examples of complete requests and/or responses if possible.

mike-bike commented 2 months ago

Not sure if that is what you are looking for. I found the binding to be reverted to the supplied one. I have stopped openHAB, clean-cache, and restarted. Then upgraded the binding again.

openhab> bundle:update 275 file:///home/openhabian/addons/org.openhab.binding.boschshc-4.2.0-SNAPSHOT-relay.jar
openhab> list | grep -i bosch
275 │ Active │  80 │ 4.2.0.202404121436    │ openHAB Add-ons :: Bundles :: Bosch Smart Home Binding

Interestingly, during a short period Switch, Child Protection, and PowerSwitchProgram were working in both directions. Then suddenly, it stopped... Very strange - changes in Bosch App get not reflected in openHAB. There are also no traces in the log on changes in the App. Please see snippets below and let me know if I'd need to change something or investigate further.

Switching off/on in Bosch App

2024-04-12 23:03:14.279 [DEBUG] [nternal.devices.bridge.BridgeHandler] - Got update for service PowerSwitch of type DeviceServiceData: {"@type":"powerSwitchState","automaticPowerOffTime":0,"switchState":"OFF"}
2024-04-12 23:03:19.886 [DEBUG] [.internal.devices.bridge.LongPolling] - Long poll response: {"result":[{"path":"/devices/hdm:ZigBee:30fb10fffe46d732/services/PowerSwitch","@type":"DeviceServiceData","id":"PowerSwitch","state":{"@type":"powerSwitchState","automaticPowerOffTime":0,"switchState":"ON"},"deviceId":"hdm:ZigBee:30fb10fffe46d732"}],"jsonrpc":"2.0"}
2024-04-12 23:03:19.887 [DEBUG] [nternal.devices.bridge.BridgeHandler] - Got update for service PowerSwitch of type DeviceServiceData: {"@type":"powerSwitchState","automaticPowerOffTime":0,"switchState":"ON"}

Switching Manual/Automatic in Bosch App

2024-04-12 23:05:09.295 [DEBUG] [nternal.devices.bridge.BridgeHandler] - Got update for service PowerSwitchProgram of type DeviceServiceData: {"schedule":{"profiles":[{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"MONDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"TUESDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"WEDNESDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"THURSDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"FRIDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"SATURDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"SUNDAY"}]},"operationMode":"SCHEDULE","@type":"powerSwitchProgramState"}
2024-04-12 23:05:13.046 [DEBUG] [.internal.devices.bridge.LongPolling] - Long poll response: {"result":[{"path":"/devices/hdm:ZigBee:30fb10fffe46d732/services/PowerSwitchProgram","@type":"DeviceServiceData","id":"PowerSwitchProgram","state":{"schedule":{"profiles":[{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"MONDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"TUESDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"WEDNESDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"THURSDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"FRIDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"SATURDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"SUNDAY"}]},"operationMode":"MANUAL","@type":"powerSwitchProgramState"},"deviceId":"hdm:ZigBee:30fb10fffe46d732"}],"jsonrpc":"2.0"}
2024-04-12 23:05:13.047 [DEBUG] [nternal.devices.bridge.BridgeHandler] - Got update for service PowerSwitchProgram of type DeviceServiceData: {"schedule":{"profiles":[{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"MONDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"TUESDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"WEDNESDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"THURSDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"FRIDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"SATURDAY"},{"switchPoints":[{"startTimeMinutes":0,"value":{"@type":"switchStateSwitchPointValue","switchState":"OFF"}}],"day":"SUNDAY"}]},"operationMode":"MANUAL","@type":"powerSwitchProgramState"}

Childprotection in Bosch App

2024-04-12 23:10:18.450 [DEBUG] [.internal.devices.bridge.LongPolling] - Long poll response: {"result":[{"path":"/devices/hdm:ZigBee:30fb10fffe46d732/services/ChildProtection","@type":"DeviceServiceData","id":"ChildProtection","state":{"@type":"ChildProtectionState","childLockActive":false},"deviceId":"hdm:ZigBee:30fb10fffe46d732"}],"jsonrpc":"2.0"}
2024-04-12 23:10:18.452 [DEBUG] [nternal.devices.bridge.BridgeHandler] - Got update for service ChildProtection of type DeviceServiceData: {"@type":"ChildProtectionState","childLockActive":false}
2024-04-12 23:10:18.484 [DEBUG] [nternal.devices.bridge.BridgeHandler] - Found handler org.openhab.binding.boschshc.internal.devices.relay.RelayHandler@795eb0, calling processUpdate() for service ChildProtection with state {"@type":"ChildProtectionState","childLockActive":false}
david-pace commented 2 months ago

Thanks for looking into the log files :+1:

First, a few general explanations:

So far I have identified 2 issues:

I will provide a new JAR with fixes for these issues as soon as I find some time.

And I have two questions:

mike-bike commented 2 months ago

You are right. All Updates from Bosch are broken. Will check this afternoon Gruß Michael

mike-bike commented 2 months ago

I do not think PowerSwitchProgram is really needed. Though, it would be cool changing the schedules from openHAB. But I agree it is probably not worth the effort. Any UI would be complex, but it might be an interesting option pushing sets of predefined schedules to thermostats, switches, etc...

I'd assume ImpulseSwitch is not required. In my opinion it is showing the type of the wired switch with 3 options: No switch, Switch (Schalter), ImpulseSwitch (Taste). It just drives the behavior of the relay. Does it change state with each Impuls, or does it stay on as long there is current on the switch input. Similar configuration is available for Light/Rollershutter II module.

Did I understand correctly that when the Bosch -> openHAB communication fails, the other direction (openHAB -> Bosch) still works?

Yes, this is correct understanding. On/Off Switch item is working, but does not show changes from Bosch app.

If this happens, do you still get updates for all your other devices? If not, I suppose that the long poll fails for some reason. You could check your log for any long poll failure, long poll cancelled etc. entries.

Last Long Poll response was PowerSwitchProgram response and then no more. There is no poll error message even with TRACE on. Thing switches into error when linking item to ImpulsSwitch channel:

COMMUNICATION_ERROR
Error when trying to refresh state from service ImpulseSwitch: State request with URL https://192.168.2.61:8444/smarthome/devices/hdm:ZigBee:30fb10fffe46d732/services/ImpulseSwitch/state failed with status code 404 and error code ENTITY_NOT_FOUND

I am going to stay away from these dangerous items... A bundle:restart on the console brought long poll back to life and Power-Switch and Child-Protection are working again in both ways.

david-pace commented 2 months ago

I just uploaded a new JAR with a few enhancements and fixes (under the same link). Please delete your channels and the relay thing before you download the new JAR.

Changes:

I have a theory about the impulse switch service, maybe you can help me verify whether it's correct: In my opinion we need the impulse switch channel and service, because I don't think Bosch would advertise a service that is completely useless. This is also indicated in https://github.com/tschamm/boschshc-hass/issues/101.

So my theory is that the relay can be operated with the ImpulseSwitch service when configured in impulse switch mode, and otherwise with the PowerSwitch service. Could you please change the configuration of your relay and check how it behaves when you send commands to both of the services, respectively?

I'm excited to learn whether if it works better with the new version and if I was able to mitigate the long poll issues.

mike-bike commented 2 months ago

Ok, now I slowly understand. I was confused because the relay need to be configured differently during the initial training in the Bosch app. My bad - I did not realized that. During first initialization in the Bosch App, you need to define the "mode" of the relay:

  1. PowerSwitch - keeps open or closed state until changed, or
  2. ImpulseSwitch - closes the relay for a pre-configured time and then automatically opens it again

You cannot switch between both modes in the Bosch app. I had to remove the device and reconfigure. Now the app does no longer provide On/Off toggles but only a pushbutton. When I press it, the relay close for the 1 sec (configured time between 0.5 and 20secs) and then opens again.

I tested the binding with the relay in PowerSwitch configuration. Signal-Strength recognized the correct value. On/Off and Child-Protection switches were correctly working in both ways.

Then I deleted the device from the app and re-connected it selecting "Impulse Mode" during setup. I have unlinked the items and deleted the thing. Triggered rediscovery, added thing and relinked items: that messed up the long-poll and I could not get anything working with the items (Child-Protection was working at the beginning). Need to re-do in a more structured approach.

boschshc deviceInfo | less might give a bit more insights:

deviceID: hdm:ZigBee:30fb10fffe46d732                                                                                                   
    type: MICROMODULE_RELAY -> relay
 service: CommunicationQuality -> communicationquality
 service: ImpulseSwitch -> impulseswitch
 service: ChildProtection -> childprotection
 service: ElectricalFaults -> !UNSUPPORTED!

In that configuration there is no PowerSwitch service. Which makes sense: PowerSwitch and ImpulseSwitch are mutually exclusive. That needs to be reflected in the thing. Pending on the configuration, I think you would either need to expose one or the other channel. PowerSwitch could become a read/only status item, during the period the relay is closed. In current configuration, the ImpulsSwitch triggers on ON

2024-04-14 17:47:34.477 [TRACE] [ernal.devices.bridge.BoschHttpClient] - create request for https://192.168.2.61:8444/smarthome/devices/hdm:ZigBee:30fb10fffe46d732/services/ImpulseSwitch/state and content {"impulseState":true,"stateType":"impulseSwitchState","@type":"impulseSwitchState"}

and on OFF

2024-04-14 17:47:38.342 [TRACE] [ernal.devices.bridge.BoschHttpClient] - create request for https://192.168.2.61:8444/smarthome/devices/hdm:ZigBee:30fb10fffe46d732/services/ImpulseSwitch/state and content {"impulseState":false,"stateType":"impulseSwitchState","@type":"impulseSwitchState"}

but the relay does not switch at all. When I press the push button in the app the following is visible in the log:

2024-04-14 17:52:23.633 [DEBUG] [.internal.devices.bridge.LongPolling] - Long poll response: {"result":[{"path":"/devices/hdm:ZigBee:30fb10fffe46d732/services/ImpulseSwitch","operations":["sendImpulse"],"@type":"DeviceServiceData","id":"ImpulseSwitch","state":{"impulseLength":100,"instantOfLastImpulse":"2024-04-14T15:52:31.677366Z","@type":"ImpulseSwitchState","impulseState":true},"deviceId":"hdm:ZigBee:30fb10fffe46d732"}],"jsonrpc":"2.0"}
2024-04-14 17:52:23.635 [DEBUG] [nternal.devices.bridge.BridgeHandler] - Got update for service ImpulseSwitch of type DeviceServiceData: {"impulseLength":100,"instantOfLastImpulse":"2024-04-14T15:52:31.677366Z","@type":"ImpulseSwitchState","impulseState":true}
2024-04-14 17:52:23.665 [DEBUG] [nternal.devices.bridge.BridgeHandler] - Found handler org.openhab.binding.boschshc.internal.devices.relay.RelayHandler@c6bfd2, calling processUpdate() for service ImpulseSwitch with state {"impulseLength":100,"instantOfLastImpulse":"2024-04-14T15:52:31.677366Z","@type":"ImpulseSwitchState","impulseState":true}
2024-04-14 17:52:23.761 [DEBUG] [.internal.devices.bridge.LongPolling] - Long poll response: {"result":[{"path":"/devices/hdm:ZigBee:30fb10fffe46d732/services/ImpulseSwitch","operations":["sendImpulse"],"@type":"DeviceServiceData","id":"ImpulseSwitch","state":{"impulseLength":100,"instantOfLastImpulse":"2024-04-14T15:52:49.882144Z","@type":"ImpulseSwitchState","impulseState":true},"deviceId":"hdm:ZigBee:30fb10fffe46d732"}],"jsonrpc":"2.0"}
2024-04-14 17:52:23.763 [DEBUG] [nternal.devices.bridge.BridgeHandler] - Got update for service ImpulseSwitch of type DeviceServiceData: {"impulseLength":100,"instantOfLastImpulse":"2024-04-14T15:52:49.882144Z","@type":"ImpulseSwitchState","impulseState":true}
2024-04-14 17:52:23.778 [DEBUG] [nternal.devices.bridge.BridgeHandler] - Found handler org.openhab.binding.boschshc.internal.devices.relay.RelayHandler@c6bfd2, calling processUpdate() for service ImpulseSwitch with state {"impulseLength":100,"instantOfLastImpulse":"2024-04-14T15:52:49.882144Z","@type":"ImpulseSwitchState","impulseState":true}

and after the defined duration (here 10 seconds - the config seems to use 0.1th seconds i.e. 100) the relay turns off:

2024-04-14 17:52:33.502 [DEBUG] [.internal.devices.bridge.LongPolling] - Long poll response: {"result":[{"path":"/devices/hdm:ZigBee:30fb10fffe46d732/services/ImpulseSwitch","operations":["sendImpulse"],"@type":"DeviceServiceData","id":"ImpulseSwitch","state":{"impulseLength":100,"instantOfLastImpulse":"2024-04-14T15:52:49.882144Z","@type":"ImpulseSwitchState","impulseState":false},"deviceId":"hdm:ZigBee:30fb10fffe46d732"}],"jsonrpc":"2.0"}
2024-04-14 17:52:33.503 [DEBUG] [nternal.devices.bridge.BridgeHandler] - Got update for service ImpulseSwitch of type DeviceServiceData: {"impulseLength":100,"instantOfLastImpulse":"2024-04-14T15:52:49.882144Z","@type":"ImpulseSwitchState","impulseState":false}
2024-04-14 17:52:33.534 [DEBUG] [nternal.devices.bridge.BridgeHandler] - Found handler org.openhab.binding.boschshc.internal.devices.relay.RelayHandler@c6bfd2, calling processUpdate() for service ImpulseSwitch with state {"impulseLength":100,"instantOfLastImpulse":"2024-04-14T15:52:49.882144Z","@type":"ImpulseSwitchState","impulseState":false}

It seems that the push-button requires a more complex command to fire... Getting it to work will still leave the same questions as discussed in the HA article.

May I propose the following:

PowerSwitch configuration Only expose PowerSwitch, Child-Protection and SignalStrength as channels. The thing may have a property mode=PowerSwitch

ImpulseSwitch configuration: Thing property mode=Impulse. Expose ImpulseSwitchState (point), pulseLength (set-point), instantOfLastImpulse (point), Child-Protection (switch) and SignalStrength (point) as channels. There also needs to be a channel to fire the button with (a user defined) duration. ImpulseSwitchState changes to ON and waits for the update on the long poll, when the relay turns off. We could then configure a switch-item in the UI and sync it with ImpulseSwitchState in an event-rule.

Regards, Michael

david-pace commented 2 months ago

Thank you for this analysis :+1: So my theory that only one of the services is available/used was correct. But the impulse switch is more complex than I thought.

I will think about how we can configure the channels automatically based on the configured mode/profile. Could you please help me determine all the possible identifiers for the profile property? According to https://github.com/openhab/openhab-addons/issues/16590#issue-2213324792 there are 3 different profiles (LIGHT, GENERIC, HEATING_RCC). But are there also three modes that you can choose during the setup of the device, or only two? I need to know all possible profile identifiers and whether they work with the ImpulseSwitch or the PowerSwitch service.

Thanks to your log output I can now properly send and receive messages for the ImpulseService. I will implement the property and the channels as proposed. I have an idea how this could be implemented without the need for an additional event rule. We can experiment with the solution and you can tell me what you think as soon as I find some time to write the code and upload a new JAR.

mike-bike commented 2 months ago

Hi @david-pace, I am happy to help. I'd consider the two modes PowerSwitch and ImpulsSwitch as two different devices pending on the configuration (Similar to the Lightswitch/Rollershutter II device). As they serve two different purposes, we could assume the chosen configuration to be fairly static. May I suggest you to store the mode as Thing property and use that in the code to void / disable the inappropriate channels if not generating two separate versions as with the Light/Shutter II devices?

I think you have summarized the variations correctly in your post above

PowerSwitch Mode: The profiles LIGHT, GENERIC, and HEATING_RCC do only exist in PowerSwitch mode. Actually the App offers 4 choices: Standard, Leuchte, Elektroheizung, and Boiler. Unfortunately I cannot configure Elektroheizung or Boiler as it requires a room-thermostat which I do not have. According to the text in the app, the only difference between both is the delay to turn off the relay. Elektroheizung has a 5 minute interval and Boiler has 10 minute interval to automatically switch off the relay after reaching the temp limit (from the thermostat).

Please note, that the PlugCompact (Zwischenstecker Kompakt) also supports the same three profiles, but with only three options in the app: choices: Standard, Leuchte, Elektroheizung. The 4th option Boiler is missing. I am going to check later if the config would allow to set heating without a thermostat... Need to unplug the device.

From openHAB perspective all 4 profiles should work the same: an On/Off switch turns the relay on and off. Profile LIGHT would appear as room-light in the Bosch app and scenes and will switch with the virtual Raumlicht button. Profile GENERIC does not show up as a room light and does not change the state when Raumlicht is selected. In addition the app allow to set a time limit between 0 and 60 minutes to automatically turn off again: getState(): Request complete: [{"@type":"powerSwitchState","switchState":"ON","automaticPowerOffTime":0}]

I do not know how the heating modes will be displayed. It could be, that they are linked to a Climate Control. I will ask around if I could borrow a room-thermostat.

ImpulsSwitch Mode: In that mode, the app only offers a stateless push-button which turns the relay on. After a predefined interval, the relay automatically turns off again. There are no profiles to chose from - aka only GENERIC

2024-04-15 19:15:31.227 [TRACE] [rnal.discovery.ThingDiscoveryService] - - got thingUID 'boschshc:relay:bshc01:hdm_ZigBee_30fb10fffe46d732' for device: 'Type device; RootDeviceId: 64-da-a0-10-83-a7; 
Id: hdm:ZigBee:30fb10fffe46d732; 
Device Service Ids: CommunicationQuality, ImpulseSwitch, ChildProtection, ElectricalFaults; 
Manufacturer: BOSCH; Room Id: hz_16; Device Model: MICROMODULE_RELAY; 
Serial: 30FB10FFFE46D732; 
Profile: GENERIC; Name: Relais Impuls Test; Status: AVAILABLE; Child Device Ids:  '
david-pace commented 2 months ago

Thank you for the detailed update :heart:

I think we should go for the property-based approach. I was trying to figure out a criteria to detect whether the relay is configured as regular power switch or impulse switch.

My idea was to check the profile value, but now I understood that my idea does not work. I thought we always have the same list of available profiles, but these also change depending on the configuration.

My new approach would be to check the list of device services. If ImpulseSwitch is in the list, we know that the device was configured as impulse switch, otherwise as a regular PowerSwitch. Or do you have a better idea for detection criteria?

mike-bike commented 2 months ago

Hi David, looking for the device services shall be the right method. Pending on the configuration it should either offer service: PowerSwitch or service: ImpulseSwitch.

mike-bike commented 2 months ago

Hi David, not sure if it is really related, but I feel that calling inappropriate service kills the long-poll process. I just found the updates from Bosch binding missing since yesterday 23:13. I had the thing configured as ImpulseSwitch and in the late evening changed the configuration of the relay back to PowerSwitch. I have missed to unlink the items and re-creation f the thing...

2024-04-16 23:13:35.369 [DEBUG] [.internal.devices.bridge.LongPolling] - Long poll response: {"result":[{"deleted":true,"@type":"Dev
iceServiceData","id":"CommunicationQuality","deviceId":"hdm:ZigBee:30fb10fffe46d732"}],"jsonrpc":"2.0"}

2024-04-16 23:13:35.371 [WARN ] [mmon.WrappedScheduledExecutorService] - Scheduled runnable ended with an exception:
java.lang.NullPointerException: Cannot invoke "com.google.gson.JsonElement.getAsString()" because the return value of "com.google.gs
on.JsonObject.get(String)" is null
        at org.openhab.binding.boschshc.internal.serialization.BoschServiceDataDeserializer.deserialize(BoschServiceDataDeserializer
.java:51) ~[?:?]

It seems that the code is sensitive to the long poll responses and cannot recover from an error or unexpected result. Note: The device needed to be deleted in the Bosch app and then added again.

david-pace commented 2 months ago

Hi @mike-bike, I uploaded a new version of the JAR. Please unlink the channels and delete the thing before updating it.

Changes:

Excited to hear whether it works better now 😎

mike-bike commented 2 months ago

Hi @david-pace, I have downloaded new JAR, unlinked all items and deleted the thing. Then updated binding with new JAR.

PowerSwitch Mode:

  1. Thing discovered in inbox
  2. Added Thing with relay in PowerSwitch mode
  3. Thing came online and property mode=PowerSwitch
  4. Thing exposes signal-strength, child-protection and power-switch but also impulse-switch, impulse-lengthand instant-of-last-impulse which were not expected.
  5. Linked and checked channels: signal-strength shows correct value, child-protection and power-switch working in both directions. I have not touched the 3 impulse channels.

Then unlink items and deleted thing in openHAB and device in Bosch app. Reconfigured relais in

ImpulseSwitch Mode:

  1. Scan for new devices -> Relais came up, created thing
  2. Thing came online and property mode=ImpulseSwitch
  3. Thing exposes signal-strength, child-protection, power-switch, impulse-switch, impulse-length and instant-of-last-impulse. I would not have expected power-switch in impulse mode.
  4. Linked all channels except power-switch
  5. child-protection working as expected
  6. signal-strength seems to behave a bit odd. I was expecting good (3) but item sticked to excellent as previous value. Event log shows correct value. Translation?
    2024-04-20 13:18:43.052 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'BSHC_RS03_Signalstarke' changed from 0 to 3
  7. impulse-switch working in both directions. Switch in openHAB stays on until relay switches off again. GREAT!
  8. impulse-length was updated with 20 (2s) according to Bosch app. Value also gets updated with change in Bosch app.
  9. instant-of-last-impulse is updated with last event

Overall, great progress. I am going to test setting impulse-length from openHAB later today and provide a summary.

Regards, Michael

mike-bike commented 2 months ago

Hi @david-pace, I tried to test with impulse-length. Neither assignment (update) or command does change the value in Bosch app or triggers on/off cycle. Though updates from Bosch app gets reflected.

New channel impulse-length that configures the impulse length in deciseconds as specified. This will also directly trigger an impulse switch. Let me know if you would prefer that this only sets the length internally, so that the user has to trigger the impulse-switch manually.

The way impulse-switch is working, is absolutely fine for me. I'd prefer updating impulse-length and fire the cycle with the switch. It will also have the advantage, that you could change the duration without triggering the event; this is same experience in the App. However, I was not able to update the duration to become visible in the App. Tried both: update and send-command. Changes in the App gets reflected in the item immediately!

In summary:

  1. Please review channels to be presented pending on configuration: with mode=PowerSwitch none of the impulse- should be available, and with Impulse-Switch the PowerSwitch channel does not make sense.
  2. I'd suggest some minor updates to the translations / wording: currently there is a mixture of english and german names suggested in the channels
  3. Default channel Unit in impulse-length should be ds rather than s - if it is working as expected it should default to setpoint rather than point - actually I have not seen that working correctly before on any other channel. Might be an UI or code/framework issue with openHAB. Following my comment above I'd prefer not to trigger the event. On/Off works with impulse-switch perfectly fine.

Many thanks for the great work. In summary it would be working for me. However, invalid channels could confuse the user. Please advise, how I should update the impulse-length to become reflected into the app. I have quickly trie via a rule to update / send-command with both failed.

I will be on vacation starting on Monday and returning on May-1st. Please expect my delayed response on further testing.

Regards, Michael

david-pace commented 2 months ago

Thanks for the detailed tests ❤️

When you are back from your vacation, you can test the new JAR I uploaded.

Fixes:

TODOs not yet fixed / to be discussed:

david-pace commented 2 months ago

Hi @jlaur and @lsiepel, we have a technical question: we have a relay device that can be one of two modes: Power switch mode (ON and OFF is controlled manually in both directions), or impulse switch (only an ON command can be sent, and the relay automatically switches off after a predefined time).

Depending on the mode, the device exposes different services. In Particular, in power switch mode there is a PowerSwitch service linked to one openHAB channel, and in impulse switch mode there is a ImpulseSwitch service linked to 3 openHAB channels. The mode is automatically determined during discovery and stored in a thing property.

I managed to deactivate the corresponding channels based on the thing property internally. So if commands are sent to inactive services, the implementation simply ignores these commands (not ignoring them would lead to errors when sent to the controller). But @mike-bike, who was so kind to test my code with real hardware, mentioned that from the user's perspective it would be even better if the irrelevant channels were not even visible in the UI.

So the question is: do you know if there is a way to completely hide certain channels from the UI depending on a thing property? Thanks in advance for your support :+1:

jlaur commented 2 months ago

[...] The mode is automatically determined during discovery and stored in a thing property. [...] So the question is: do you know if there is a way to completely hide certain channels from the UI depending on a thing property?

First, you should probably not rely on a property set by discovery, since this would break manual configuration (UI or file-based).

There are multiple strategies for hiding/removing channels. Do you need to be able to dynamically add those channels again, or should they be forever removed once the mode is determined? In this case they could be declared statically by XML as usual, and simply removed by code:

Set<String> supportedChannelIdSet = new HashSet<>();
List<Channel> unusedChannels = thing.getChannels().stream().filter(channel -> !supportedChannelIdSet.contains(channel.getUID().getId())).toList();
updateThing(editThing().withoutChannels(unusedChannels).build());
mike-bike commented 2 months ago

When you are back from your vacation, you can test the new JAR I uploaded.

Had some spare time running a quick test...

  • default unit for impulse-length should now be ds

add points to model shows ds as unit. Ok.

  • when a numeric command is received in the impulse-length channel, the new value is logged on DEBUG level

I do not see any debug statements in the log (I am on TRACE). I have used the quick rule below and would expect setting the delay in the Bosch app to 90 ds or 9 seconds.

console.log("Impulse Length pre:  ", items.BSHC_SW03_Impulse_Length.state);
items.BSHC_SW03_Impulse_Length.sendCommand(90);
console.log("Impulse Length post: ", items.BSHC_SW03_Impulse_Length.state);
  • impulse-length commands should now be sent to the Bosch SHC and should be reflected in the app. But since there is no documentation from Bosch yet, we might have to experiment with the payload. For now I send the new impulseLength and impulseState: false to the controller (and omit the instantOfLastImpulse field), but we have to test whether the controller accepts this.

Sending above command does not trigger anything. Value in Bosch app remains the same. However, changes in Bosch gets reflected in the Item.

TODOs not yet fixed / to be discussed:

  • I didn't have time to add German translations so far and might do this later, or leave it up to the community to add the translations via CrowdIn.

No worries, just wanted to share my observations. Happy to help with the translations, but do not know how to do it.

david-pace commented 2 months ago

Thank you for your hints, @jlaur 👍 I will change the code in such a way that the mode is determined during startup and not via the property.

If we remove the channels this will work until the mode is changed again (which is quite unlinkely, but can still happen). It this case we could advise the users in the documentation that the thing has to be deleted and added again. This would be acceptable in my opinion.

We could also go for the most convenient approach, which would mean that we dynamically add and remove channels during startup. But I wonder what implications this has if the channels to be removed are still used/linked somewhere. Do you know if the system would handle this gracefully?

jlaur commented 2 months ago

We could also go for the most convenient approach, which would mean that we dynamically add and remove channels during startup. But I wonder what implications this has if the channels to be removed are still used/linked somewhere. Do you know if the system would handle this gracefully?

What changes the mode? If the mode can actually change, you might want to reconsider, since, as you mention, the user might need to get the channel back. In this case you could dynamically create the channel by code (and remove the channel accordingly). There are plenty of bindings creating channels dynamically, if you need a reference I can find one tomorrow.

If the mode is not really static, but rather can change back and forth, I'm not sure adding/removing channels gives the best user experience.

The system can handle the channel being removed, it will just leave a broken link.

mike-bike commented 2 months ago

Just wanted to share my thoughts: Mode change could only happen during initial device configuration in the Bosch app when the user need to decide on the usage of the device. Mode change would only happen if the device is repurposed - potentially moved to different place / wiring. I‘d assume this to be rare. In that case, I‘d find manual intervention in openHAB to reassign channels absolutely acceptable. Cheers Michael

mike-bike commented 2 months ago

PS: using wrong channels caused some issues in the binding. However. Unlinking items and relinking them after reconf worked fine. I have done that many times during testing. Michael

david-pace commented 2 months ago

Hi @mike-bike, I hope you had a great vacation 😎

I'm still thinking about the best solution regarding adding/removing channels dynamically. @jlaur, do you have any new thoughts on this after @mike-bike 's description two posts earlier? Implementation-wise, I think we could achieve any of these three alternatives:

BTW @mike-bike, I think I added some checks so that using irrelevant channels should not crash the device anymore, but correct me if I'm wrong.

In the mean time, I thought about a more systematic approach to get the impulse-length synchronization working. Can you check if you see anything in the log when you change the impulse length in the Bosch App (but not trigger an impulse)? The JSON messages should give us some hint about what we are supposed to send in order to get the other direction working.

Thank you both for your support 👍

mike-bike commented 2 months ago

Hi David, yes, it was a great time. Thanks for asking. I will check the behaviour tonight. Do I need to refresh the binding with a newer version? From the 3 options below, I‘d not recommend the first as it would not be visible to the user which channels actually are active or to be ignored. I‘d be fine with manual user interaction whenever the device has been reconfigured rather then doing the change automatically behind the scenes. What if the thing goes into error state when thing config no longer matches the device? That would avoid faulty behaviour and forces the user to react? Maybe a error message is shown in the thing as a reason. Similar to other bindings when they loose connection or require re-authentication? Cheers, Michael

david-pace commented 2 months ago

Hi @mike-bike, I did not update the JAR since I last announced it (April 21st), so you should still have the latest version 👍

And yes, if we go for alternative 2, we should communicate to the user via the thing state that the thing configuration does not match the hardware configuration anymore.

mike-bike commented 2 months ago

Hi @david-pace,

changing impulse-length in the Bosch app is immediately reflected in the thing. Please find below change (from 10s) to 7s

tail -f openhab.log | grep -i impuls
2024-05-02 18:00:07.619 [DEBUG] [.internal.devices.bridge.LongPolling] - Long poll response: {"result":[{"path":"/devices/hdm:ZigBee:30fb10fffe46d732/services/ImpulseSwitch","operations":["sendImpulse"],"@type":"DeviceServiceData","id":"ImpulseSwitch","state":{"impulseLength":70,"instantOfLastImpulse":"2024-05-02T16:00:19.389292Z","@type":"ImpulseSwitchState","impulseState":false},"deviceId":"hdm:ZigBee:30fb10fffe46d732"}],"jsonrpc":"2.0"}
2024-05-02 18:00:07.622 [DEBUG] [nternal.devices.bridge.BridgeHandler] - Got update for service ImpulseSwitch of type DeviceServiceData: {"impulseLength":70,"instantOfLastImpulse":"2024-05-02T16:00:19.389292Z","@type":"ImpulseSwitchState","impulseState":false}
2024-05-02 18:00:07.664 [DEBUG] [nternal.devices.bridge.BridgeHandler] - Found handler org.openhab.binding.boschshc.internal.devices.relay.RelayHandler@363cae, calling processUpdate() for service ImpulseSwitch with state {"impulseLength":70,"instantOfLastImpulse":"2024-05-02T16:00:19.389292Z","@type":"ImpulseSwitchState","impulseState":false}

Channing the impulse-length in the item does change the items state, but does not change the value in the Bosch app. I have tried below code in a rule:

console.log("Impulse Length pre:  ", items.BSHC_SW03_Impulse_Length.state);
items.BSHC_SW03_Impulse_Length.sendCommand(90);
console.log("Impulse Length post: ", items.BSHC_SW03_Impulse_Length.state);

That results into the update of the state:

2024-05-02 18:03:00.503 [INFO ] [nhab.automation.script.ui.9a905cf07a] - Impulse Length pre:   70 ds
2024-05-02 18:03:00.533 [INFO ] [nhab.automation.script.ui.9a905cf07a] - Impulse Length post:  90 ds

Same result with postUpdate rather than sendCommand

items.BSHC_SW03_Impulse_Length.postUpdate("90");

But the Bosch app still has the old value. When I trigger the impulse switch in openHAB, the item is updated with the correct Bosch value (here 70ds) immediately.

Hope that helps. Let me know if I should use a different method.

Regards, Michael

jlaur commented 2 months ago

@jlaur, do you have any new thoughts on this after @mike-bike 's description two posts earlier? Implementation-wise, I think we could achieve any of these three alternatives:

  • all channels are always visible, but depending on the mode some channels simply do nothing
  • irrelevant channels are deleted upon the first initialization of the device. If the mode changes, the user has to re-add the thing
  • channels are configured fully dynamically upon each initialization of the device

First solution is the only one where recreating of Thing and relinking of items is not needed. But as you stated, the use case is rare, so this solution suffers from the UI issue of having too many irrelevant channels.

The other two solutions are similar, and they make the UI less complex for the user. In first of the two, the Thing has to be readded upon repurposing a device, and in the last this it not needed. In both cases relinking items will be needed. It is slightly more complex to dynamically create the channels as it has to be done in code rather than XML. Dynamically removing channels suffers from a very small UI glitch: If you are quickly jumping to the channel tab after creating the Thing, you might see all channels initially, before they are removed by the first initialization. This is a quite minor glitch though.

I think that covers more or less everything. I agree it's acceptable to have to recreate the Thing and relink channels when repurposing a device, so it's really up to you what solution you prefer.

david-pace commented 1 month ago

Hi @jlaur, I'm currently trying to implement the fully dynamic solution, which involves adding channels again (at least potentially). But I'm struggling to create channels by code. You mentioned that there is also a strategy to hide/show channels. Could you please give a hint how this can be accomplished? Thanks 🙂

david-pace commented 3 weeks ago

I found out how to create channels from scratch using ChannelBuilder. However, I would like to load the channels as they are defined in thing-types.xml to avoid duplicating metadata. Do you know how to do that @jlaur or do you maybe know even better mechanisms?