Skip to content

Latest commit

 

History

History
181 lines (142 loc) · 8.53 KB

README-dev.md

File metadata and controls

181 lines (142 loc) · 8.53 KB

Logo

ioBroker.e3dc-rscp

Developer manual

This adapter is based on @iobroker/create-adapter v1.31.0 It was developed looking at E3/DC's sample application which is written in C++.

The sample application package also contains the official tag lists, which are necessary to interpret RSCP frames semantically.

Timestamp Handling

  • RSCP: 8 byte BigInt [seconds], 4 byte Int [nanoseconds]
  • internal processing is done via Float [seconds] and Date
  • ioBroker object tree: human readable String like "2022-01-30 12:00:00.000"

DB Namespace Semantics

In the DB namespace, time series can be requested from E3/DC:

  • TIME_START = start of observed time range
  • TIME_SPAN = length of observed time range; minimum is 900 seconds, i.e. 15 minutes
  • TIME_INTERVAL = sampling rate; minimum is 900 seconds, i.e. 15 minutes

Results contain GRAPH_INDEX, described as "relative position within the graph". The data point's timestamp can be approximated like

TIMESTAMP = TIME_START + GRAPH_INDEX * TIME_INTERVAL

Here is some data from test calls:

SPAN INTERVAL GRAPH_INDEX increment # of values
7200 60 13.6 8
3600 60 13.6 4
3600 120 6.8 4
120 3600 ERR ERR
120 120 ERR ERR
360 120 ERR ERR
1800 120 6.8 2
900 120 6.8 1
86400 3600 1 24
86400 60 13.6 96
86400 7200 1 12
86400 1800 1 48
43200 900 1 48
86400 900 1 96
172800 3600 1 32
172800 1800 1 64
172800 7200 1 16
345600 7200 1 16

Observations:

  • It looks like E3/DC stores values in 15 minutes resolution.
  • TIME_SPAN below 900 seconds causes an error response.
  • TIME_INTERVAL below 900 seconds is treated like 900 seconds.
  • For HISTORY_DATA_DAY, it seems that TIME_SPAN maximum is 32 hours.
  • Typically, the first data value is at TIME_START + TIME_INTERVAL/2
  • One additional value is (always?) transmitted at the end of TIME_SPAN.

Introducing a new setter tag

While RSCP is not strictly built according to patterns, most of the settable attributes involve four tags, e.g. for EMS.WB_DISCHARGE_BAT_UNTIL we have

  • TAG_EMS_REQ_WB_DISCHARGE_BAT_UNTIL - request current value from E3/DC
  • TAG_EMS_WB_DISCHARGE_BAT_UNTIL - response: the requested value from E3/DC
  • TAG_EMS_REQ_SET_WB_DISCHARGE_BAT_UNTIL - request E3/DC to set the value
  • TAG_EMS_SET_WB_DISCHARGE_BAT_UNTIL - response: the value set in E3/DC

Within ioBroker's object tree, all four tags will be represented in one object: e3dc-rscp.0.EMS.WB_DISCHARGE_BAT_UNTIL

NOTE: the following describes how to handle the standard case as depicted above. Peculiarities like

  • container frames involved (e.g. EMS_MAX_CHARGE_POWER)
  • need to repeat requests to make E3/DC hold a value (e.g. SET_POWER)
  • nested structures (e.g. IDLE_PERIODS, DB.HISTORY)

are not described here, but handled by dedicated code (e.g. a new queue...() function in main.js).

1. Make sure the four tags are listed correctly in RscpTags.json and io-package.json

First, check RscpTags.json. If the REQ tag was unknown before, also add a polling interval default in io-package.json. NOTE: do not add the REQ_SET tag to io-package.json; only the REQ tag is used for polling.

"polling_intervals": [
    ...
    {
        "tag": "TAG_EMS_REQ_WB_DISCHARGE_BAT_UNTIL",
        "interval": "M"
    },
    ...
],

Do not add the REQ_SET tag; it will not be polled regularly.

NOTE: after modifying io-package.json, you have to do reinstall the adapter from-the-scratch and re-ernter the configuration - do not load a config json file, this will disable the new io-package.json entries.

2. Declare object to represent a setter and assign setter tag

const mapChangedIdToSetTags = {
  ...
  "EMS.WB_DISCHARGE_BAT_UNTIL": ["", "TAG_EMS_REQ_SET_WB_DISCHARGE_BAT_UNTIL"],
}

3. Add getter tag to subsystem data request (if not already there)

This includes the value in regular polling so that we will see E3/DC side changes in the object tree.

queueEmsRequestData( sml ) {
...
    this.addTagtoFrame( "TAG_EMS_REQ_WB_DISCHARGE_BAT_UNTIL", sml );
...
}

4. Add an object to the object tree

This is necessary because it is writable and thererfore non-standard.

await this.setObjectNotExistsAsync( "EMS.WB_DISCHARGE_BAT_UNTIL", {
  type: "state",
  common: {
    name: systemDictionary["WB_DISCHARGE_BAT_UNTIL"][this.language],
    type: "number",
    role: "value",
    read: false,
    write: true,
    unit: "%",
  },
  native: {},
} );

5. System dictionary entry

The systemDictionary["WB_DISCHARGE_BAT_UNTIL"][this.language] above must be filled with a meaningful text in admin/en/translations.json

"WB_DISCHARGE_BAT_UNTIL": "Set wallbox discharge battery until",

After entering English text, call

npm run translate all

to generate translations to all other languages.

Done. No extra send function in such standard cases! queueSetValue() will send the value after it was changed in the object tree.

RSCP API Documentation

Original sources

The S10 portal contains basic documentation

Additional sources

In addition, the following projects, among others, deal with the RSCP interface:

The rest of this Developer manual will be modified/removed when processed and done, respectively.

Best Practices

We've collected some best practices regarding ioBroker development and coding in general. If you're new to ioBroker or Node.js, you should check them out. If you're already experienced, you should also take a look at them - you might learn something new :)

Scripts in package.json

Several npm scripts are predefined for your convenience. You can run them using npm run <scriptname>

Script name Description
test:js Executes the tests you defined in *.test.js files.
test:package Ensures your package.json and io-package.json are valid.
test:unit Tests the adapter startup with unit tests (fast, but might require module mocks to work).
test:integration Tests the adapter startup with an actual instance of ioBroker.
test Performs a minimal test run on package files and your tests.
check Performs a type-check on your code (without compiling anything).
lint Runs ESLint to check your code for formatting errors and potential bugs.

Writing tests

When done right, testing code is invaluable, because it gives you the confidence to change your code while knowing exactly if and when something breaks. A good read on the topic of test-driven development is https://hackernoon.com/introduction-to-test-driven-development-tdd-61a13bc92d92. Although writing tests before the code might seem strange at first, but it has very clear upsides.

The template provides you with basic tests for the adapter startup and package files. It is recommended that you add your own tests into the mix.