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

Adapt library to Homie 3.0.0 #550

Merged
merged 34 commits into from
Jan 22, 2019
Merged

Adapt library to Homie 3.0.0 #550

merged 34 commits into from
Jan 22, 2019

Conversation

bodiroga
Copy link
Collaborator

@bodiroga bodiroga commented Nov 5, 2018

Hi @marvinroger and @timpur!

Here you have my proposal to adapt the library to the newest Homie convention version 👍 I have tested the changes with a NodeMCU board and the provided examples compile successfully! Anyway, review the changes carefully, please, I'm not a C++ guy.

Some things that could be added in a future PR:

  • Add a new method to name each node array element individually (right now, the "node name" is the same as the "node id" -> strip_1, strip_2, strip_3 and strip_4 in the "LedStrip.ino").
  • Rework the "node" and "property" creation. For the "property" attributes, instead of providing all fields in the "advertise" function as parameters, add individual functions and chain them. For example: myNode.advertise("temperature").withName("Temperature").withDatatype("int").withUnit("ºC").withFormat("-5:50").settable(myFunction). Even if it is more verbose, IMO it is more readable and users don't have to remember the order of the parameters. Also, it makes easier to add more fields, because we don't have to overload the function signature.

Many thanks for all your amazing work and don't hesitate to criticize my code, feel free 😉

Best regards,

Aitor

PS: I have left all the individual commits in case you want to review them one by one, but I can squash them into a single one for the final merge.
PS2: related issues: #549, #467, #464

@bodiroga
Copy link
Collaborator Author

Hi @marvinroger and @timpur!

I'm going to add the "$retained" property attribute to conform to Homie 3.0.1 and I see this as the perfect opportunity to refactor the "advertise" function.

Do you agree that the best option is to define the advertise function with the only required attribute (id) and add the optional ones (name, unit, format, datatype, retained and settable) following a "builder" pattern? We actually do that with the settable attribute, so it's natural to extend that principle to the rest non-mandatory attributes. What's your favorite name? ".withName", ".name", ".setName",...?

Many thanks for you help and looking forward to hearing from you guys!

Aitor

@bodiroga
Copy link
Collaborator Author

Done, Homie 3.0.1 fully supported!

@timpur
Copy link
Contributor

timpur commented Nov 14, 2018

sorry need to still have a proper review ....

src/HomieNode.cpp Show resolved Hide resolved
examples/LedStrip/LedStrip.ino Outdated Show resolved Hide resolved
}

PropertyInterface& PropertyInterface::setProperty(Property* property) {
_property = property;
return *this;
}

HomieNode::HomieNode(const char* id, const char* type, const NodeInputHandler& inputHandler)
HomieNode::HomieNode(const char* id, const char* name, const char* type, bool range, uint16_t lower, uint16_t upper, const NodeInputHandler& inputHandler)
Copy link
Member

Choose a reason for hiding this comment

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

In all your examples the "id" is the same as "type".
"type" is actually optional, at least it doesn't say anything about the node in the controller perspective.
Could you maybe remove it from the constructor and make it a chainable setter instead?

Copy link
Member

Choose a reason for hiding this comment

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

Make all the array specific parameters a dedicated setter as well (setArray(uint16_t lower, uint16_t upper)). That way if arrays change at some point, we don't need to change the constructor.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

"type" is actually optional, at least it doesn't say anything about the node in the controller perspective.

Type is a required attribute for Nodes in Homie 3.0.1, see here: https://homieiot.github.io/spec-core-v3_0_1/#node-attributes. That's why I have added it to the class constructor. Perhaps we can remove it for Homie 4.0, but that discussion doesn't belong here 😉

Make all the array specific parameters a dedicated setter as well (setArray(uint16_t lower, uint16_t upper)). That way if arrays change at some point, we don't need to change the constructor.

I didn't want to change that part of the code too much, because it was already implemented, just the minimum necessary changes. What do you think @marvinroger?

@@ -50,16 +50,16 @@ uint16_t SendingPromise::send(const String& value) {
strcat(topic, Interface::get().getConfig().get().deviceId);
strcat_P(topic, PSTR("/"));
strcat(topic, _node->getId());
strcat_P(topic, PSTR("/"));
strcat(topic, _property->c_str());

if (_range.isRange) {
char rangeStr[5 + 1]; // max 65536
itoa(_range.index, rangeStr, 10);
strcat_P(topic, PSTR("_"));
strcat(topic, rangeStr);
}
Copy link
Member

Choose a reason for hiding this comment

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

Ranges are not part of Homie 3?

Copy link
Collaborator Author

@bodiroga bodiroga Nov 17, 2018

Choose a reason for hiding this comment

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

Ranges are how Node arrays are called in homie-esp8266, it's just an internal attribute name.

Copy link
Member

Choose a reason for hiding this comment

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

Be aware that "SendingPromise" is part of the Homie object, not of the Node. So if one Node sets the range it is used for other Nodes, too.
So Homierange needs to be reset after sending.

(TBH: I don't understad why it worked in V2?!)

Copy link

Choose a reason for hiding this comment

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

@euphi
How can we do that ?

Copy link
Member

@euphi euphi Jan 20, 2019

Choose a reason for hiding this comment

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

@jeanvdr :
Just add after line 57 (strcat(topic, rangeStr);):

  _range.isRange = false;
  _range.index = 0;

It is a quick workaround. I think the range-mechanism has to be rewritten.

Copy link

Choose a reason for hiding this comment

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

@euphi
Thanks


bool globalInputHandler(const HomieNode& node, const String& property, const HomieRange& range, const String& value) {
bool globalInputHandler(const HomieNode& node, const HomieRange& range, const String& property, const String& value) {
Copy link
Member

Choose a reason for hiding this comment

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

Ranges are not part of Homie 3?

Copy link
Collaborator Author

@bodiroga bodiroga Nov 17, 2018

Choose a reason for hiding this comment

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

Ranges are how Node arrays are called in homie-esp8266, it's just an internal attribute name.

@jmvaz
Copy link

jmvaz commented Nov 29, 2018

Hi! When PR will be merged?

@ThomDietrich
Copy link
Contributor

@marvinroger any chance you'll find time to look into this one?

@bodiroga would you be interested to invest more time as a maintainer of this project?

@davidgraeff
Copy link
Member

I just created arendst/Tasmota#4696. Would be nice to have all esp DIY firmwares out there supported. MySensors is also interested.

@bodiroga
Copy link
Collaborator Author

@bodiroga would you be interested to invest more time as a maintainer of this project?

My C++ knowledge is very limited to maintain a project like this, but I could help keeping the library up to date with the Homie convention 👍

@GideonLeGrange
Copy link

I wish we could get this merged and released 😕

I'm running my fork of @bodiroga 's develop-v3 branch and it's fine. ESPs running this code integrates easily into Openhab 2.4.

@jmvaz
Copy link

jmvaz commented Dec 25, 2018

Come on guys! A little Christmas gift :)

@manswiss
Copy link

manswiss commented Jan 3, 2019

Please merge it. I usw actualy this Form and work perfect.
For OpenHAB 2.4 is Homie 3.0 nessesaire.
Thanks.

return Interface::get().getSendingPromise().setNode(*this).setProperty(property).setQos(1).setRetained(true);
else
return Interface::get().getSendingPromise().setNode(*this).setProperty(property).setQos(1);
}
}
Copy link
Member

@euphi euphi Jan 8, 2019

Choose a reason for hiding this comment

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

Here a reference to SendingPromise MUST be returned. For now, this result in a crash if you want to set a Property that has not been configured.

Solutions:

  1. Change signature of HomieNode::setProperty to SendingPromise *HomieNode::setProperty(...) and return NULL --> caller can check for NULL
  2. Create error message and abort() --> Homie-ESP8266 enforces that you can only send to advertised properties
  3. Return a generic SendingPromise& to be able to send on arbitrary topics (old behaviour. Maybe that is what you wanted to do and just messed with the nested ifs?

I suggest to stick to the Homie-2.0 behaviour for now (third solution)

Copy link
Member

Choose a reason for hiding this comment

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

Just change the line 81 und 82 to one line:

if (iProperty && iProperty->isRetained())

void setDatatype(const char* datatype) { _datatype = datatype; }
void setFormat(const char* format) { _format = format; }
void setRetained(const bool retained = true) { _retained = retained; }

Copy link
Member

Choose a reason for hiding this comment

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

change to String (see detailed comment below)

const char* _name;
const char* _unit;
const char* _datatype;
const char* _format;
Copy link
Member

Choose a reason for hiding this comment

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

these should be stored as String, so it is automatically copied.

Of course most of the Strings that are set here a constants and thus in global memory.
However, it is possible to receive dynamically created strings here and these could be on stack memory. For example, the allowed values for an ENUM could be generated dynamically.

@gorec2005
Copy link

@bodiroga Thank for you work - it's work, but ota_update don't :-(

@mjcumming
Copy link

Is it possible to do a pull on this PR? How?

@dikodahan
Copy link

+1 on having this released! Even if it will only have partial functionality (like OTA not working yet in initial release).
On the same subject of updating this to Homie 3, the current Homie 2 implementation has a dependency on ArduinoJson 5.x while 6.x has been released for a while now. Would be worth to update that support as well along the way, though back to my first point, could be a later release not to delay the initial support release.

Thank you all for this work, Amazing stuff!!!

@euphi
Copy link
Member

euphi commented Jan 18, 2019

@mjcumming You can pull the fork from bodiroga and chekout develop-v3

git clone https://github.com/bodiroga/homie-esp8266
cd homie-esp8266
git checkout develop-v3

With platformio, you can change your lib_deps from "Homie" to "https://github.com/bodiroga/homie-esp8266#develop-v3".

@jeanvdr
Copy link

jeanvdr commented Jan 19, 2019

@bodiroga, gorec2005, dikodahan
for ota_update to work, since topic $online has disappeared from homie v3.0.0, you have to change to 3 lines:
line 16 : client.subscribe("{base_topic}{device_id}/$online".format(**userdata)) > client.subscribe("{base_topic}{device_id}/$state".format(**userdata))
line 69: elif msg.topic.endswith('$online'): > elif msg.topic.endswith('$state'):
line 70: if msg.payload == 'false': > if msg.payload != 'ready':

That's it

@mjcumming
Copy link

@euphi thank you for the instructions!

@euphi
Copy link
Member

euphi commented Jan 20, 2019

It seems this branch sometimes looses events:

Triggering WIFI_DISCONNECTED event...
↕ Attempting to connect to Wi-Fi...
〽 Sending statistics...
  • Wi-Fi signal quality: 100%
  • Uptime: 1488s
✔ Wi-Fi connected, IP: 192.168.0.145
Triggering WIFI_CONNECTED event...
↕ Attempting to connect to MQTT...
〽 Sending statistics...
  • Wi-Fi signal quality: 8%
  • Uptime: 1548s
〽 Sending statistics...
  • Wi-Fi signal quality: 8%
  • Uptime: 1608s

--> MQTT Connected event is missing, although the homie-esp8266 client reconnected to MQTT. The LED is still blinking quickly. However, the client receives set-commands and handles them correctly, so this is a minor issue.

@davidgraeff
Copy link
Member

Actually. The over-use of the Arduino String class and std::function will fragment the heap memory very fast and I assume this firmware is just crashing from time to time. The esp is quite fast with rebooting and reconnecting so that may not have been noticed yet.

@euphi
Copy link
Member

euphi commented Jan 20, 2019

@davidgraeff RTOS seems to be quite robust.

When using many debug outputs with format strings, I have some problems that outgoing MQTT messages are lost, but no crashes/resets.

All of my homie devices run very stable (uptime is months and I never saw any reset)

Exception:

  • one device using the WS2812FX library resets very often (5-30 minutes). I investigated a lot and haven't found any issue yet, so I more and more think this is a power supply issue.

  • some devices with bad Wifi connections start to reconnect/loose connection over and over, once they lost there wifi-connection for first time.
    I think that maybe the initial amount of MQTT traffic is too much for a slow/disturbed Wifi connection. However, this is not a reset.

  • one device has I2C problems every now and then. For now, I blame the I2C device (PCF8575) for that.

@euphi euphi changed the base branch from master to develop-v3 January 21, 2019 23:58
@euphi
Copy link
Member

euphi commented Jan 22, 2019

Initial development for Homie Convention V3.0.x by @bodiroga

@euphi euphi merged commit e22aec6 into homieiot:develop-v3 Jan 22, 2019
@bodiroga bodiroga deleted the develop-v3 branch April 16, 2019 12:50
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.

None yet