knxultimate
Version:
KNX IP protocol implementation for Node. This is the ENGINE of Node-Red KNX-Ultimate node.
225 lines (195 loc) • 5.53 kB
text/typescript
/**
* Validates KNX Data Point Type 213 conversions.
*
* Written in Italy with love, sun and passion, by Massimo Saccani.
*
* Released under the MIT License.
* Use at your own risk; the author assumes no liability for damages.
*/
import { describe, test } from 'node:test'
import assert from 'node:assert/strict'
import DPT213 from '../../src/dptlib/dpt213'
describe('DPT213 (4x 16-Bit Signed Value)', () => {
describe('formatAPDU', () => {
test('should correctly format valid temperature values', () => {
const validTemp = {
Comfort: 21.4,
Standby: 20,
Economy: 18.2,
BuildingProtection: -1,
}
const result = DPT213.formatAPDU(validTemp)
assert.ok(Buffer.isBuffer(result))
assert.equal(result?.length, 8)
})
test('should handle boundary values', () => {
const boundaryValues = {
Comfort: -271.98,
Standby: 655.32,
Economy: -200,
BuildingProtection: 500,
}
const result = DPT213.formatAPDU(boundaryValues)
assert.ok(Buffer.isBuffer(result))
assert.equal(result?.length, 8)
})
test('should return undefined for values outside range', () => {
const invalidTemp = {
Comfort: -273,
Standby: 20,
Economy: 18.2,
BuildingProtection: -1,
}
const result = DPT213.formatAPDU(invalidTemp)
assert.equal(result, null)
const invalidTemp2 = {
Comfort: 21.4,
Standby: 656,
Economy: 18.2,
BuildingProtection: -1,
}
const result2 = DPT213.formatAPDU(invalidTemp2)
assert.equal(result2, null)
})
test('should return undefined for invalid object structure', () => {
// Test missing properties
const result1 = DPT213.formatAPDU({
Comfort: 21.4,
Standby: 20,
Economy: 18.2,
// BuildingProtection missing
})
assert.equal(result1, null)
// Test wrong property names
const result2 = DPT213.formatAPDU({
comfort: 21.4,
standby: 20,
economy: 18.2,
buildingProtection: -1,
} as any)
assert.equal(result2, null)
})
})
describe('fromBuffer', () => {
test('should correctly parse valid buffer', () => {
const validTemp = {
Comfort: 21.4,
Standby: 20,
Economy: 18.2,
BuildingProtection: -1,
}
const buffer = DPT213.formatAPDU(validTemp)
assert.ok(buffer)
const result = DPT213.fromBuffer(buffer)
assert.ok(result)
// Check properties existence and type
const properties = [
'Comfort',
'Standby',
'Economy',
'BuildingProtection',
]
properties.forEach((key) => {
assert.ok(key in result, `Property ${key} should exist`)
assert.equal(
typeof result[key],
'number',
`${key} should be a number`,
)
})
})
test('should handle boundary values', () => {
// Using values slightly within limits to avoid rounding issues
const boundaryValues = {
Comfort: -271.98,
Standby: 655.32,
Economy: -200,
BuildingProtection: 500,
}
const buffer = DPT213.formatAPDU(boundaryValues)
assert.ok(buffer)
const result = DPT213.fromBuffer(buffer)
assert.ok(result)
// Check properties existence and type
const properties = [
'Comfort',
'Standby',
'Economy',
'BuildingProtection',
]
properties.forEach((key) => {
assert.ok(key in result, `Property ${key} should exist`)
assert.equal(
typeof result[key],
'number',
`${key} should be a number`,
)
})
// Check values are within valid range
const inRange = (val: number) => val >= -272 && val <= 655.34
assert.ok(inRange(result.Comfort), 'Comfort should be in range')
assert.ok(inRange(result.Standby), 'Standby should be in range')
assert.ok(inRange(result.Economy), 'Economy should be in range')
assert.ok(
inRange(result.BuildingProtection),
'BuildingProtection should be in range',
)
})
test('should return null for invalid buffer length', () => {
assert.equal(DPT213.fromBuffer(Buffer.alloc(6)), null)
assert.equal(DPT213.fromBuffer(Buffer.alloc(10)), null)
assert.equal(DPT213.fromBuffer(Buffer.alloc(0)), null)
})
test('should maintain precision for decimal values', () => {
const testValues = {
Comfort: 21.42,
Standby: 20.06,
Economy: 18.24,
BuildingProtection: -1.08,
}
const apdu = DPT213.formatAPDU(testValues)
assert.ok(Buffer.isBuffer(apdu))
const result = DPT213.fromBuffer(apdu)
assert.ok(result)
const delta = 0.03
assert.ok(Math.abs(result.Comfort - testValues.Comfort) < delta)
assert.ok(Math.abs(result.Standby - testValues.Standby) < delta)
assert.ok(Math.abs(result.Economy - testValues.Economy) < delta)
assert.ok(
Math.abs(
result.BuildingProtection - testValues.BuildingProtection,
) < delta,
)
})
})
describe('Round trip', () => {
test('should maintain values after encode/decode cycle', () => {
const originalValues = {
Comfort: 21.4,
Standby: 20,
Economy: 18.2,
BuildingProtection: -1,
}
const encoded = DPT213.formatAPDU(originalValues)
assert.ok(Buffer.isBuffer(encoded))
const decoded = DPT213.fromBuffer(encoded)
assert.ok(decoded)
const delta = 0.03
assert.ok(
Math.abs(decoded.Comfort - originalValues.Comfort) < delta,
)
assert.ok(
Math.abs(decoded.Standby - originalValues.Standby) < delta,
)
assert.ok(
Math.abs(decoded.Economy - originalValues.Economy) < delta,
)
assert.ok(
Math.abs(
decoded.BuildingProtection -
originalValues.BuildingProtection,
) < delta,
)
})
})
})