Package: pyduro
PyDuroA Pypi library to communicate with Aduro (H1) wood/pellet burner via NBE
communication.AboutThis library is intented to communicate with NBE capable burners (typically
Adura hybrid stoves).RequestNBE is an UDP protocol that uses ASCII frames to communicate. Here is an example
frame (this one is used to discover a burner):abcdefghijkl041837 \x02000012345678904361465775pad 013NBE Discovery\x04And here the different part of the frame:abcdefghijkl-appId, 12 alphanumerical (upper and lowercase) characters -
Uniquely identify the application that is talking with the oven(you can
put here whatever you want)123456-controllerId, 6 digits - This is the "Serial number" of your
oven(the same as the one you entered in the official Aduro application, you
can find it on a sticker on the oven, often inside of the door)spaceor*or-- Encryption level of the frame(' ' = not
encrypted, '*' = RSA encrypted, '-' = XTEA encrypted)\x02-startChar, the ASCII character0x02(so 1 char, not "\x02")-
This is the separator to identify the beginning of the request00-function, 2 digits - Identify the type of request(see below)00-sequenceNumber, 2 digits - To identify the request when run in
sequence(optional, you can leave this at 00 if you don't care about the
order of your requests)1234567890-pinCode, 10 digit - The password to connect to your oven(the same as the one you entered in the official Aduro application, you can
find it on a sticker on the oven, often inside of the door)4361465775-time, 10 digits - The timestamp at which the request as
been issued('{:0>10.10}'.format(str(time()))in Python do the trick)pad_(literally the string "pad ") - Extra space reserved for future use013-payloadSize, 3 digits - The size of the actual payload of the
request(that comes right after this)NBE Discovery-payload, max 495 bytes - The actual payload of the
request(here in the example it's a discovery request).\x04-endChar, the ASCII character0x04(so 1 char, not "\x04")-
This is the separator to identify the end of the payloadResponseA response will always be formed in pretty much the same way:abcdefghijkl123456\x0200000065Serial=123456;IP=192.168.1.250;Type=v13std;Ver=705;Build=38;Lang=0\x04abcdefghijkl-appId, 12 alphanumerical (upper and lowercase) characters -
Uniquely identify the application that the oven  is talking to123456-controllerId, 6 digits - This is the "Serial number" of the
oven responding\x02-startChar, the ASCII character0x02(so 1 char, not "\x02")-
This is the separator to identify the beginning of the response00-function, 2 digits - Identify the type of response(see below)00-sequenceNumber, 2 digits - To identify the request this response is
for when using a sequence0-status, 1 digit - The status of the response(0 = success, >0 =
error)013-payloadSize, 3 digits - The size of the actual payload of the
response(that comes right after this)Serial=12345;IP=192.168.1.250;Type=v13std;Ver=705;Build=38;Lang=0-payload, max 1007 bytes - The actual payload of the response(here in the
example it's a discovery response in the form of a semicolon separated list of
key/values pair).\x04-endChar, the ASCII character0x04(so 1 char, not "\x04")-
This is the separator to identify the end of the payloadFunctionsThere is a limited set of functions you can use in the NBE protocol:0: Discovery1: Read settings value2: Set settings value3: Read setup range4: Read operating data5: Read advanced data6: Read consumption data7: Read chart data8: Read event log9: Read info10: Read available programsNote that your burner might not support all of them and might also support
others (for example,11is a supported type on Aduro H1 burners).More infoYou can learn more about NBE withthis repository(I highly recommend you to play with the CLI to discover more about the protocol)and in particularthis documentationas well as the various implementations.You can also readthis issueand the implementation of the protocol in the PyDuro library.InstallationpipinstallpyduroLib usageSimply import the actions and use them:frompyduro.actionsimportdiscover,get,set,rawdiscover.run()get.run(burner_address="<burner IP address>",serial="<burner serial number>",pin_code="<burner pin code>",function_name="<settings|range|operating|advanced|consumption|chart|logs|info|versions>",path="<path>")set.run(burner_address="<burner IP address>",serial="<burner serial number>",pin_code="<burner pin code>",path="<path>"value="<value>")raw.run(burner_address="<burner IP address>",serial="<burner serial number>",pin_code="<burner pin code>",function_id="<function ID>",payload="<payload>")ResponseEvery response from a burner will be composed with the same fields:frame: the whole raw NBE frame receivedburner_address: the burner IP address from which the response originatedburner_port: the burner UDP port from which the response originatedapp_id: the application ID the response is intended to(when using PyDuro will always be___pyduro___)serial: the burner serial number from which the response originatedfunction: the function identifier the response is intended tosequence_number: the identifier of the request the response is intended tostatus: the status of the response(0 = success, >0 = error)payload_size: the size of the payload of the responsepayload: the actual response payloadYou can also use theparse_payloadmethod that will return:a string if the payload is a stringa dict if the payload is a semicolon separated list of fields(name=value)a list of the payload is a semicolon separated list of valuesCLI usageIntegrated helppython-mpyduro--helpDiscover a burnerpython-mpyduro[discover]The CLI will exit with 0 if a burner is found, 1 otherwise.Get the status of a burnerpython-mpyduro-b<burnerIPaddress>-s<burnerserialnumber>-p<burnerpincode>statusThe result will be output as a JSON object that you can then manipulate withjqfor example.The CLI will exit with the return code return by the burner (0 = success, >0 =
error).Examplespython-mpyduro-b192.168.1.250-s1234-p12345678status

>{>"boiler_temp":"14.9",
>"boiler_ref":"20.0",
>"content":"-2038",
>"dhw_temp":"13.6",
>"dhw_ref":"0.0",
>[...]>}Get information from a burnerpython-mpyduro-b<burnerIPaddress>-s<burnerserialnumber>-p<burnerpincode>get<settings|range|operating|advanced|consumption|chart|logs|info|versions>"<path>"The result will be output as a JSON object that you can then manipulate withjqfor example.The CLI will exit with the return code return by the burner (0 = success, >0 =
error).Forlogsaction, you can pass "now" as path (which is also the default
value) to get the latest logs from your burner.Forsettingsaction, you have to pass one of the valid following path:"boiler","hot_water""regulation""weather""weather2""oxygen""cleaning""hopper""fan""auger""ignition""pump""sun""vacuum""misc""alarm""manual"To see all sub element of a path, add.*at the end of the path.To see a specific element of a path, add.<element name>at the end of the
path.Forconsumptionaction, you can pass one of the following path:"total_hours","total_days""total_months""total_years""dhw_hours""dhw_days""dhw_months""dhw_years""counter"Examplespython-mpyduro-b192.168.1.250-s1234-p12345678getoperating

>{>"NA":"38",
>"air_flow":"0",
>"air_quality":"0",
>"ashbox_contact":"0.0",
>"ashbox_minutes":"0.0",
>"back_pressure":"0",
>"boiler_pump_state":"0",
>"boiler_ref":"19.0",
>[...]>}python-mpyduro-b192.168.1.250-s1234-p12345678getoperating"boiler_ref">"boiler_ref=19.0"python-mpyduro-b192.168.1.250-s1234-p12345678getsettings"misc.*">{>[...]>"start":"0",
>"stop":"0",
>[...]>}python-mpyduro-b192.168.1.250-s1234-p12345678getsettings"misc.start">"start=0"Note that you can pass "" as a path to get the full response from your
burner.If you don't give a pass (or give an empty one) then "" will be used
as default.Update a burner's settingpython-mpyduro-b<burnerIPaddress>-s<burnerserialnumber>-p<burnerpincode>set"<path>""<value>"The CLI will exit with the return code return by the burner (0 = success, >0 =
error).Examplespython-mpyduro-b192.168.1.250-s1234-p12345678set"misc.start""1"python-mpyduro-b192.168.1.250-s1234-p12345678set"misc.stop""1"