Skip to content

Commit 808f227

Browse files
authored
Merge pull request #34 from svrooij/feature/new-socket-comms
Feature/new socket comms
2 parents e0fa1ee + 3000d12 commit 808f227

24 files changed

+333
-107
lines changed

.github/workflows/build-and-release.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- uses: actions/checkout@v2
1919
- uses: actions/setup-node@v1
2020
with:
21-
node-version: 12
21+
node-version: 16
2222
- name: Install depencencies
2323
run: npm ci
2424
- name: Run tests

.vscode/launch.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"request": "launch",
1010
"name": "Launch Program",
1111
"program": "${workspaceFolder}/bin/run",
12-
"preLaunchTask": "tsc: build - tsconfig.json",
12+
// "preLaunchTask": "tsc: build - tsconfig.json",
13+
"preLaunchTask": "Build",
1314
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
1415
"envFile": "${workspaceFolder}/.env"
1516
},

.vscode/tasks.json

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
// See https://go.microsoft.com/fwlink/?LinkId=733558
3+
// for the documentation about the tasks.json format
4+
"version": "2.0.0",
5+
"tasks": [
6+
{
7+
"label": "Build",
8+
"type": "shell",
9+
"command": "npm run prepack",
10+
"windows": {
11+
"command": "npm run prepack-win"
12+
}
13+
}
14+
]
15+
}

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:14-alpine as node-original
1+
FROM node:16-alpine as node-original
22
FROM node-original as install
33
WORKDIR /usr/src/app
44
COPY package*.json ./

package-lock.json

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66
"scripts": {
77
"compile": "tsc",
88
"copy-www": "cp -r ./src/output/wwwroot ./dist/output/wwwroot",
9+
"copy-www-win": "xcopy .\\src\\output\\wwwroot .\\dist\\output\\wwwroot /E/H/C/I/d/Y",
910
"coverage": "jest --coverage",
11+
"jest": "jest",
1012
"lint": "eslint ./src --ext .js,.jsx,.ts,.tsx",
1113
"lint-fix": "eslint ./src --ext .js,.jsx,.ts,.tsx --fix",
1214
"test": "npm run lint && jest",
13-
"prepack": "npm run compile && npm run copy-www"
15+
"prepack": "npm run compile && npm run copy-www",
16+
"prepack-win": "npm run compile && npm run copy-www-win"
1417
},
1518
"repository": {
1619
"type": "git",

src/config.ts

+19
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export interface MqttConfig {
2222
distinctFields: Array<string>;
2323
prefix: string;
2424
url: string;
25+
last_reset?: string;
26+
last_reset_solar?: string;
2527
}
2628

2729
const defaultMqttConfig: Partial<MqttConfig> = {
@@ -35,13 +37,15 @@ const defaultMqttConfig: Partial<MqttConfig> = {
3537
export interface SunspecConfig {
3638
host: string;
3739
port: number;
40+
interval?: number;
3841
}
3942

4043
export interface OutputConfig {
4144
debug: boolean;
4245
influx?: InfluxOutputOptions;
4346
jsonSocket?: number;
4447
mqtt?: MqttConfig;
48+
mqttSocket?: number;
4549
post?: HttpPostConfig;
4650
rawSocket?: number;
4751
webserver?: number;
@@ -120,20 +124,28 @@ export class ConfigLoader {
120124
.describe('mqtt-discovery', 'Emit auto-discovery message')
121125
.boolean('mqtt-discovery')
122126
.describe('mqtt-discovery-prefix', 'Autodiscovery prefix')
127+
.describe('mqtt-last-reset', 'If set, this value is added to mqtt as \'last_reset\'')
128+
.string('mqtt-last-reset')
129+
.describe('mqtt-last-reset-solar', 'If set, this value is added to mqtt as \'last_reset\'')
130+
.string('mqtt-last-reset-solar')
123131
.describe('influx-url', 'Influxdb server url')
124132
.describe('influx-token', 'Influxdb server token')
125133
.describe('influx-bucket', 'Influx bucket')
126134
.describe('influx-org', 'Influx organization')
127135
.describe('tcp-server', 'Expose JSON TCP socket on this port')
136+
.describe('tcp-server-mqtt', 'Expose JSON TCP socket on this port')
128137
.describe('raw-tcp-server', 'Expose RAW TCP socket on this port')
129138
.conflicts('port', 'socket')
130139
.describe('debug', 'Enable debug output')
131140
.boolean('debug')
132141
.describe('sunspec-modbus', 'IP of solar inverter with modbus TCP enabled')
133142
.describe('sunspec-modbus-port', 'modbus TCP port')
143+
.describe('sunspec-interval', 'Interval for solar reading')
134144
.number('sunspec-modbus-port')
145+
.number('sunspec-modbus-interval')
135146
.number('web-server')
136147
.number('tcp-server')
148+
.number('tcp-server-mqtt')
137149
.number('raw-tcp-server')
138150
.number('post-interval')
139151
.describe('enc-aad', 'Additional authentication data, if your meter encrypts data (eg. Luxemburg)')
@@ -167,6 +179,10 @@ export class ConfigLoader {
167179
config.outputs.jsonSocket = args['tcp-server'];
168180
}
169181

182+
if (args['tcp-server-mqtt']) {
183+
config.outputs.mqttSocket = args['tcp-server-mqtt'];
184+
}
185+
170186
if (typeof args['mqtt-url'] === 'string') {
171187
config.outputs.mqtt = {
172188
discovery: args['mqtt-discovery'] === true,
@@ -175,6 +191,8 @@ export class ConfigLoader {
175191
distinctFields: args['mqtt-distinct-fields']?.split(',') ?? defaultMqttConfig.distinctFields ?? [],
176192
prefix: args['mqtt-topic'] ?? 'smartmeter',
177193
url: args['mqtt-url'],
194+
last_reset: args['mqtt-last-reset'],
195+
last_reset_solar: args['mqtt-last-reset-solar'],
178196
};
179197
}
180198

@@ -208,6 +226,7 @@ export class ConfigLoader {
208226
config.solar = {
209227
host: args['sunspec-modbus'],
210228
port: args['sunspec-modbus-port'],
229+
interval: args['sunspec-interval'],
211230
} as SunspecConfig;
212231
}
213232

src/modbus-solar-input.ts

-36
This file was deleted.

src/output/debug-output.ts

+7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import P1Reader from '../p1-reader';
2+
import BaseSolarReader from '../solar/base-solar-input';
23
import { Output } from './output';
34

45
/**
@@ -24,6 +25,12 @@ export default class DebugOutput implements Output {
2425
});
2526
}
2627

28+
addSolar(solarReader: BaseSolarReader): void {
29+
solarReader.on('solar', (data) => {
30+
console.log(' - solar %s', JSON.stringify(data));
31+
});
32+
}
33+
2734
close(): Promise<void> {
2835
// You should stop the started servers here, when building your own output.
2936
return Promise.resolve();

src/output/http-output.ts

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ export default class HttpOutput extends IntervalOutput {
2424
}));
2525
}
2626

27+
// addSolar(solarReader: BaseSolarReader): void {
28+
29+
// }
30+
2731
private sendEvent(data: DsmrMessage): Promise<Response> {
2832
const flatData = this.config.fields
2933
? this.filterData(HttpOutput.flatten(data))

src/output/influx-output.ts

+13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { InfluxDB, Point, WriteApi } from '@influxdata/influxdb-client';
22
import P1Reader from '../p1-reader';
3+
import BaseSolarReader from '../solar/base-solar-input';
34
import { Output } from './output';
45

56
export interface InfluxOutputOptions {
@@ -51,6 +52,18 @@ export class InfluxOutput implements Output {
5152
});
5253
}
5354

55+
addSolar(solarReader: BaseSolarReader): void {
56+
solarReader.on('solar', (data) => {
57+
const solarPoint = new Point('solar');
58+
if (data.serial) solarPoint.tag('invertor', data.serial);
59+
if (data.acPower && data.lifetimeProduction) {
60+
solarPoint.intField('lifetime-production', data.lifetimeProduction);
61+
solarPoint.floatField('ac-power', data.acPower);
62+
this.writeApi.writePoint(solarPoint);
63+
}
64+
});
65+
}
66+
5467
async close(): Promise<void> {
5568
await this.writeApi.flush();
5669
await this.writeApi.close();

src/output/interval-output.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
import EventEmitter from 'events';
1+
import { EventEmitter } from 'events';
22
import TypedEmitter from 'typed-emitter';
3+
import { SunspecResult } from '@svrooij/sunspec/lib/sunspec-result';
34
import P1Reader, { Usage } from '../p1-reader';
45
import { Output } from './output';
56
import DsmrMessage from '../dsmr-message';
7+
import BaseSolarReader from '../solar/base-solar-input';
68

79

810
interface IntervalOutputEvents {
911
dsmr: (result: DsmrMessage) => void;
1012
gasUsage: (usage: Usage) => void;
1113
usage: (usage: Usage) => void;
14+
solar: (solar: Partial<SunspecResult>) => void;
1215
}
1316

14-
export default abstract class IntervalOutput extends (EventEmitter as unknown as new () => TypedEmitter<IntervalOutputEvents>) implements Output {
17+
export default abstract class IntervalOutput extends (EventEmitter as new () => TypedEmitter<IntervalOutputEvents>) implements Output {
1518
private timer?: NodeJS.Timeout;
1619

1720
private publishNextEvent: boolean;
@@ -46,6 +49,10 @@ export default abstract class IntervalOutput extends (EventEmitter as unknown as
4649
}, (this.interval ?? 60) * 1000);
4750
}
4851

52+
addSolar(solarReader: BaseSolarReader): void {
53+
solarReader.on('solar', (data) => { this.emit('solar', data); });
54+
}
55+
4956
close(): Promise<void> {
5057
if (this.timer) {
5158
clearInterval(this.timer);

0 commit comments

Comments
 (0)