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

[solax] Initial contribution #14880

Merged
merged 34 commits into from
Aug 24, 2023
Merged

[solax] Initial contribution #14880

merged 34 commits into from
Aug 24, 2023

Conversation

theater
Copy link
Contributor

@theater theater commented Apr 25, 2023

This is a OpenHAB binding for a Solax Wi-fi modules that have the option to be connected directly via HTTP (firmware version 3.x+).

Currently the Solax cloud services provide an update on every 5 minutes (sometimes even more) and they also happen to be down sometimes, which makes it hard to automate the decision making by OpenHAB if we have more complex rules.
For example: I want to charge my car if the battery of my inverter is 90%+ and my overall consumption is only from the solar power.
The binding retrieves structured data from the wifi module, parses it and pushes it in to a inverter thing where each channel represents a specific information (inverter output power, voltage, PV1 power, etc)

More about the technical review of the PR:

The HTTP connectivity part is in the connectivity package.
There we have a "connector" which uses the common jetty client from the openhab distribution, connects to the wi-fi module and retrieves the raw data from the module. It is used directly from the bridge handler to connect and retrieve the necessary data.
Then we create the raw data bean which implements the InverterData interface and the methods of the interface directly retrieve data from the raw data bean and provide it as an easy to use Java code. (this is arguable from architecture perspective if it's better bean to have only the raw data and to use a separate parser that returns a DTO but from simplicity point of view it looks better to me this way currently)

The methods of the InverterData interface are used as getters to make the data transformation and set the channel values of the inverter thing in openHAB thing handler by using the InverterDataUpdateListener interface which is called from the bridge.
The InverterData interface is needed because at later point in time I will implement a second bridge which can retrieve a different raw data from the cloud service which could be useful for people who do not own/did not upgraded to firmware version 3.x of the Solax wi-fi module and only support cloud connection. The idea is not to care what bridge do we have but to have a common way to retrieve practically the same data which is structured in a separate way and set the Inverter thing channels.

A marketplace topic: https://community.openhab.org/t/solax-binding/146324

Cheers,
Konstantin

@theater theater added new binding If someone has started to work on a binding. For a new binding PR. work in progress A PR that is not yet ready to be merged labels Apr 25, 2023
@theater theater added this to the 4.0 milestone Apr 25, 2023
@theater theater self-assigned this Apr 25, 2023
@theater theater requested a review from a team as a code owner April 25, 2023 15:29
@theater theater removed their assignment Apr 25, 2023
@openhab-bot
Copy link
Collaborator

This pull request has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/solax-binding/146324/1

@lolodomo lolodomo removed this from the 4.0 milestone May 4, 2023
@theater theater force-pushed the Solax_binding branch 5 times, most recently from 6bf7069 to 7589809 Compare June 21, 2023 14:56
@openhab-bot
Copy link
Collaborator

This pull request has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/warning-during-build-and-error-in-the-pr-build/147406/1

Copy link
Member

@wborn wborn left a comment

Choose a reason for hiding this comment

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

Thanks for the PR!

You can probably fix the builds by adding a dependency for your add-on to this POM file:

bom/openhab-addons/pom.xml

@theater
Copy link
Contributor Author

theater commented Jun 27, 2023

Thanks for the PR!

You can probably fix the builds by adding a dependency for your add-on to this POM file:

bom/openhab-addons/pom.xml

Thanks for the suggestion. That indeed fixed the build issue. I wonder if you could also do a review of the binding. At the current stage I think we can merge it as initial version of the binding with local connection only.
Next improvement could be to create additional bridge for the cloud integration option using the same model...

@theater theater removed the work in progress A PR that is not yet ready to be merged label Jun 27, 2023
@theater theater changed the title [solax][WIP] Initial contribution [solax] Initial contribution Jun 27, 2023
Signed-off-by: Konstantin Polihronov <polychronov@gmail.com>
Signed-off-by: Konstantin Polihronov <polychronov@gmail.com>
Copy link
Contributor

@jlaur jlaur left a comment

Choose a reason for hiding this comment

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

Thank you for the contribution! It looks good - I have added some feedback from my first review round. 🙂

bundles/org.openhab.binding.solax/README.md Outdated Show resolved Hide resolved
bundles/org.openhab.binding.solax/README.md Outdated Show resolved Hide resolved
bundles/org.openhab.binding.solax/README.md Outdated Show resolved Hide resolved
bundles/org.openhab.binding.solax/README.md Show resolved Hide resolved
bundles/org.openhab.binding.solax/README.md Outdated Show resolved Hide resolved
bundles/org.openhab.binding.solax/README.md Outdated Show resolved Hide resolved
bundles/org.openhab.binding.solax/README.md Outdated Show resolved Hide resolved
bundles/org.openhab.binding.solax/README.md Outdated Show resolved Hide resolved
bundles/org.openhab.binding.solax/README.md Show resolved Hide resolved
bundles/org.openhab.binding.solax/README.md Outdated Show resolved Hide resolved
Co-authored-by: Jacob Laursen <jacob-github@vindvejr.dk>
Signed-off-by: Konstantin Polihronov <polychronov@gmail.com>
Signed-off-by: Konstantin Polihronov <polychronov@gmail.com>
Signed-off-by: Konstantin Polihronov <polychronov@gmail.com>
@theater
Copy link
Contributor Author

theater commented Aug 18, 2023

Hi,
The warning is handled externally as we never pass null to the parser, therefore null cannot be returned.
Fixed the prio3 warnings where possible. In the raw data bean the response from the API is having a capital letters for some fields and that's the reason for them not to be based on the naming convention.
Probably I can create a custom mapper for this, but I don't think investment worths it...
Please let me know of your thoughts.

Cheers,
K.

@theater
Copy link
Contributor Author

theater commented Aug 18, 2023

In your case I would probably keep the local API inverter thing, and then dynamically remove the channels that shouldn't exist for a particular inverter type/model.

How can I create / remove channels dynamically? I think that could be very well the solution (instead of creating separate handlers)...
P.S. Maybe I should move this conversation to the communities if you participate there as well...

@jlaur
Copy link
Contributor

jlaur commented Aug 18, 2023

How can I create / remove channels dynamically?

You would need to do either one or the other. For this scenario it seems simplest to remove a few statically defined channels:

List<Channel> channelsToRemove = new ArrayList<>();
updateThing(editThing().withoutChannels(channelsToRemove).build());

See for example:

/**
* If the given channel exists in the thing, but is NOT required in the thing, then add it to a list of channels to
* be removed. Or if the channel does NOT exist in the thing, but is required in the thing, then log a warning.
*
* @param removeList the list of channels to be removed from the thing.
* @param channelId the id of the channel to be (eventually) removed.
* @param channelRequired true if the thing requires this channel.
*/
private void removeListProcessChannel(List<Channel> removeList, String channelId, boolean channelRequired) {
Channel channel = thing.getChannel(channelId);
if (!channelRequired && channel != null) {
removeList.add(channel);
} else if (channelRequired && channel == null) {
logger.warn("Shade {} does not have a '{}' channel => please reinitialize the thing", shadeId, channelId);
}
}
/**
* Remove previously statically created channels if the shade does not support them or they are not relevant.
*
* @param capabilities the capabilities of the shade.
* @param shade the shade data.
*/
private void updateDynamicChannels(Capabilities capabilities, ShadeData shade) {
List<Channel> removeList = new ArrayList<>();
removeListProcessChannel(removeList, CHANNEL_SHADE_POSITION, capabilities.supportsPrimary());
removeListProcessChannel(removeList, CHANNEL_SHADE_SECONDARY_POSITION,
capabilities.supportsSecondary() || capabilities.supportsSecondaryOverlapped());
removeListProcessChannel(removeList, CHANNEL_SHADE_VANE,
capabilities.supportsTiltAnywhere() || capabilities.supportsTiltOnClosed());
boolean batteryChannelsRequired = shade.getBatteryKind() != BatteryKind.HARDWIRED_POWER_SUPPLY;
removeListProcessChannel(removeList, CHANNEL_SHADE_BATTERY_LEVEL, batteryChannelsRequired);
removeListProcessChannel(removeList, CHANNEL_SHADE_LOW_BATTERY, batteryChannelsRequired);
if (!removeList.isEmpty()) {
if (logger.isDebugEnabled()) {
StringJoiner joiner = new StringJoiner(", ");
removeList.forEach(c -> joiner.add(c.getUID().getId()));
logger.debug("Removing unsupported channels for {}: {}", shadeId, joiner.toString());
}
updateThing(editThing().withoutChannels(removeList).build());
}
}

In other more dynamic scenarios the best (or only) option is to create all relevant channels dynamically.

Copy link
Contributor

@jlaur jlaur left a comment

Choose a reason for hiding this comment

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

See latest posted comments.

@theater
Copy link
Contributor Author

theater commented Aug 21, 2023

Hi,
I'm not sure what has changed recently... checked the commits and couldn't find any meaningful reason for it but the binding did not activate on the Karaf for the following error:
`2023-08-21 11:54:48.355 [WARN ] [org.apache.felix.fileinstall ] - Error while starting bundle: file:/openhab/addons/org.openhab.binding.solax-4.1.0-SNAPSHOT.jar
org.osgi.framework.BundleException: Could not resolve module: org.openhab.binding.solax [243]
Unresolved requirement: Import-Package: javax.measure; version="[2.2.0,3.0.0)"

    at org.eclipse.osgi.container.Module.start(Module.java:463) ~[org.eclipse.osgi-3.18.0.jar:?]
    at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:445) ~[org.eclipse.osgi-3.18.0.jar:?]

`

I had to tackle it by adding the following dependency to the pom.xml:
<dependencies> <dependency> <groupId>javax.measure</groupId> <artifactId>unit-api</artifactId> <version>1.0</version> </dependency> </dependencies>

Do you know what could be the reason for this? I couldn't find any breaking changes in the recent commits... also tried to update to 4.0.2 my docker infrastructure but it's the same behaviour...

@theater
Copy link
Contributor Author

theater commented Aug 21, 2023

Could it be related to this issue?
[WARNING] Some problems were encountered while building the effective model for org.openhab.addons.bundles:org.openhab.binding.solax:jar:4.1.0-SNAPSHOT
[WARNING] 'parent.relativePath' of POM org.openhab.addons:org.openhab.addons.reactor:4.1.0-SNAPSHOT (E:\git\openhab-addons\pom.xml) points at org.openhab.addons.bundles:org.openhab.addons.reactor.bundles instead of org.openhab:openhab-super-pom, please verify your project structure @ org.openhab.addons:org.openhab.addons.reactor:4.1.0-SNAPSHOT, E:\git\openhab-addons\pom.xml, line 6, column 11
[WARNING]
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING]
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING]

The other warnings during build that I have are these:
[WARNING] The POM for org.eclipse.orbit.bundles:net.i2p.crypto.eddsa:jar:0.3.0.v20220506-1020 is missing, no dependency information available
[WARNING] The POM for org.jupnp:org.jupnp:jar:2.7.1.OH1 is missing, no dependency information available
[WARNING] The POM for org.openhab:base-fixes:jar:1.0.0 is missing, no dependency information available

@theater
Copy link
Contributor Author

theater commented Aug 21, 2023

OK. Seems that the units of measurement library has changed in 4.1 (or at least it's very similar to what I see written here: #10926)
Currently works with the added dependency, so if I provide the jar to users with 4.0 probably I have to keep it inside but it shouldn't be required for the 4.1 milestone builds.

* Add empty json string check
* Add serialization annotations for the fields with capital letters
* Refactor the fields with capital letters

Signed-off-by: Konstantin Polihronov <polychronov@gmail.com>
@theater
Copy link
Contributor Author

theater commented Aug 23, 2023

Hi,
the only leftover warning is the one for the NPE which I don't know how to resolve at the moment... Could you please check my comments in the thread about it?
Cheers,
K.

@jlaur
Copy link
Contributor

jlaur commented Aug 23, 2023

the only leftover warning is the one for the NPE which I don't know how to resolve at the moment... Could you please check my comments in the thread about it?

Yes, I wanted to clone your branch and try to propose a fix yesterday, but unfortunately ended up not finding the time. I'll try to prioritize it in the evening today.

@theater
Copy link
Contributor Author

theater commented Aug 23, 2023 via email

Copy link
Contributor

@jlaur jlaur left a comment

Choose a reason for hiding this comment

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

With these suggestions it compiles for me without any warnings or checkstyle issues.

theater and others added 2 commits August 24, 2023 09:22
…nding/solax/internal/connectivity/rawdata/LocalConnectRawDataBean.java

Co-authored-by: Jacob Laursen <jacob-github@vindvejr.dk>
Signed-off-by: Konstantin Polihronov <polychronov@gmail.com>
…nding/solax/internal/connectivity/rawdata/LocalConnectRawDataBean.java

Co-authored-by: Jacob Laursen <jacob-github@vindvejr.dk>
Signed-off-by: Konstantin Polihronov <polychronov@gmail.com>
@theater
Copy link
Contributor Author

theater commented Aug 24, 2023

OK I confirmed your changes and tested them locally. All looks good except my Eclipse warning but that's fine...

@theater
Copy link
Contributor Author

theater commented Aug 24, 2023

How can I create / remove channels dynamically?

You would need to do either one or the other. For this scenario it seems simplest to remove a few statically defined channels:

Before we complete the PR.... just to be sure that I understand the approach - your proposal is to add all the possible channels in one thingtype (inverter) and based on the inverter type I just dynamically remove the unnecessary channels in the handler, is my understanding right?

@jlaur
Copy link
Contributor

jlaur commented Aug 24, 2023

Before we complete the PR.... just to be sure that I understand the approach - your proposal is to add all the possible channels in one thingtype (inverter) and based on the inverter type I just dynamically remove the unnecessary channels in the handler, is my understanding right?

Exactly. It might be a good idea to create an issue for the next development iteration. This can also be used for discussing such things. Just give me a go-ahead for merging.

@theater
Copy link
Contributor Author

theater commented Aug 24, 2023

Yep. Please merge. I'll create a new PR once I'm ready with the option to support multiple inverters..
Thank you for all the support!

@jlaur jlaur merged commit 17978b8 into openhab:main Aug 24, 2023
2 checks passed
@jlaur jlaur added this to the 4.1 milestone Aug 24, 2023
@jlaur
Copy link
Contributor

jlaur commented Aug 24, 2023

Thank you for all the support!

You're welcome, and thanks again for your contribution. To not forget, please see https://www.openhab.org/docs/developer/addons/#add-your-add-on-s-logo-to-the-openhab-website

@theater theater deleted the Solax_binding branch August 24, 2023 14:13
Pshatsillo pushed a commit to Pshatsillo/openhab-addons that referenced this pull request Sep 29, 2023
Signed-off-by: Konstantin Polihronov <polychronov@gmail.com>
@jlaur jlaur linked an issue Oct 6, 2023 that may be closed by this pull request
austvik pushed a commit to austvik/openhab-addons that referenced this pull request Mar 27, 2024
Signed-off-by: Konstantin Polihronov <polychronov@gmail.com>
Signed-off-by: Jørgen Austvik <jaustvik@acm.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new binding If someone has started to work on a binding. For a new binding PR.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[solax] New binding with local and cloud data retrieval
5 participants