227 lines
8.4 KiB
TypeScript
227 lines
8.4 KiB
TypeScript
'use strict';
|
|
import { BufferLengthError } from './errors/BufferLengthError';
|
|
import { InvalidValueError } from './errors/InvalidValueError';
|
|
import { DPT } from './definitions';
|
|
|
|
/**
|
|
* <strong>19.001</strong> Date & Time
|
|
*
|
|
* <pre>
|
|
* +-7-+-6-+-5-+-4-+-3-+-2-+-1-+-0-+-7-+-6-+-5-+-4-+-3-+-2-+-1-+-0-+
|
|
* Field Names | (Year) | 0 0 0 0 (Month) |
|
|
* Encoding | U U U U U U U U | r r r r U U U U |
|
|
* +-7-+-6-+-5-+-4-+-3-+-2-+-1-+-0-+-7-+-6-+-5-+-4-+-3-+-2-+-1-+-0-+
|
|
* | 0 0 0 (Day Of Month) | (DayOfWeek) (Hour) |
|
|
* | r r r U U U U U | U U U U U U U U |
|
|
* +-7-+-6-+-5-+-4-+-3-+-2-+-1-+-0-+-7-+-6-+-5-+-4-+-3-+-2-+-1-+-0-+
|
|
* | 0 0 (Minutes) | 0 0 (Seconds) |
|
|
* | r r U U U U U U | r r U U U U U U |
|
|
* +-7-+-6-+-5-+-4-+-3-+-2-+-1-+-0-+-7-+-6-+-5-+-4-+-3-+-2-+-1-+-0-+
|
|
* | F WD NWD NY ND NDoW NT SST| CLQ SRC 0 0 0 0 0 0 |
|
|
* | B B B B B B B B | B B r r r r r r |
|
|
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
* Format: 8 octets (U<sub>8</sub> [r<sub>4</sub>U<sub>4</sub>] [r<sub>3</sub>U<sub>5</sub>] [r<sub>3</sub>U<sub>5</sub>] [r<sub>2</sub>U<sub>6</sub>] [r<sub>2</sub>U<sub>6</sub>] B<sub>16</sub>)
|
|
* Encoding:
|
|
* Year = [0 .. 255]
|
|
* 0 = year 1900
|
|
* 255 = year 2155
|
|
* Month = [1 .. 12]
|
|
* DayOfMonth = [1 .. 31]
|
|
* DayOfWeek = [0 .. 7]
|
|
* 1 = Monday, 2 = Tuesday, 3 = Wednesday, 4 = Thursday, 5 = Friday, 6 = Saturday, 7 = Sunday, 0 = any day
|
|
* Hour = [0 .. 24]
|
|
* Minutes = [0 .. 59]
|
|
* Seconds = [0 .. 59]
|
|
* (F) Fault = {0, 1}
|
|
* 0 = Normal (no fault)
|
|
* 1 = Fault
|
|
* (WD) WorkingDay = {0, 1}
|
|
* 0 = No Working Day
|
|
* 1 = Working Day
|
|
* (NWD) NoWorkingDay = {0, 1}
|
|
* 0 = WorkingDay field valid
|
|
* 1 = WorkingDay field not valid
|
|
* (NY) NoYear = {0, 1}
|
|
* 0 = Year field valid
|
|
* 1 = Year field not valid
|
|
* (ND) NoDate = {0, 1}
|
|
* 0 = Month and DayOfMonth fields valid
|
|
* 1 = Month and DayOfMonth fields not valid
|
|
* (NDoW) NoDayOfWeek = {0, 1}
|
|
* 0 = DayOfWeek field valid
|
|
* 1 = DayOfWeek field not valid
|
|
* (NT) NoTime = {0, 1}
|
|
* 0 = Hour, Minutes and Seconds valid
|
|
* 1 = Hour, Minutes and Seconds not valid
|
|
* (SST) Standard Summer Time = {0, 1}
|
|
* 0 = UTC+x (standard time)
|
|
* 1 = UTC+x +1h (summer daylight saving time)
|
|
* (CLQ) QualityOfClock = {0, 1}
|
|
* 0 = Clock without external synchronization signal
|
|
* 1 = Clock with external synchronization signal (DCF 77, VideoText, ...)
|
|
* (SRC) SynchronisationSourceReliability = {0, 1}
|
|
* 0 = Unreliable Synchronisation (mains, local quartz)
|
|
* 1 = Reliable Synchronisation (radio, internet)
|
|
* </pre>
|
|
* <p>
|
|
* The encoding of the hour is within the range [0 .. 24] instead of [0 .. 23]. When the hour is set to "24", the
|
|
* values of octet 3 (Minutes) and 2 (Seconds) have to be set to zero.
|
|
* <p>
|
|
* "Fault" is set if one ore more supported fields of the Date & Time information are corrupted. "Fault" is set e.g.
|
|
* power-down if battery backup was not sufficient, after 1st start up of device (clock un-configured) or radio-clock
|
|
* (DCF 77) had no reception for a very long time. "Fault" is usually cleared automatically by the device if the
|
|
* local clock is set or clock data is refreshed
|
|
* <p>
|
|
* The receiver (e.g. a room unit, MMI) will interpret Date&Time with "Fault" as corrupted and will either ignore
|
|
* the message or show --:--:-- or blinking 00:00:00 (as known from Video recorders after power-up).
|
|
*/
|
|
export class DPT19Result {
|
|
dateTime: Date = new Date()
|
|
dayOfWeek: number = undefined
|
|
f: boolean = false
|
|
wd: boolean = false
|
|
nwd: boolean = false
|
|
ny: boolean = false
|
|
nd: boolean = false
|
|
ndow: boolean = false
|
|
nt: boolean = false
|
|
sst: boolean = false
|
|
internalClock: boolean = true // Assume external clock
|
|
reliability: boolean = false;
|
|
}
|
|
export class DPT19 implements DPT {
|
|
id = '19';
|
|
name = '8-byte Date+Time';
|
|
bufferLength = 8;
|
|
|
|
/**
|
|
* Decode a buffer
|
|
*
|
|
* @param buffer the buffer
|
|
* @returns the DPT value
|
|
*/
|
|
decoder(buffer: Buffer): DPT19Result {
|
|
if (buffer.length !== this.bufferLength)
|
|
throw new BufferLengthError(`Invalid buffer length ${buffer.length}/${buffer} for DPT8. Expected ${this.bufferLength}.`);
|
|
|
|
const byte8 = buffer.readUInt8(0)
|
|
const byte7 = buffer.readUInt8(1)
|
|
const byte6 = buffer.readUInt8(2)
|
|
const byte5 = buffer.readUInt8(3)
|
|
const byte4 = buffer.readUInt8(4)
|
|
const byte3 = buffer.readUInt8(5)
|
|
const byte2 = buffer.readUInt8(6)
|
|
const byte1 = buffer.readUInt8(7)
|
|
|
|
let year = byte8
|
|
let month = byte7 & 0xF
|
|
const dayOfMonth = byte6 & 0x1F
|
|
let dayOfWeek = byte5 >> 5
|
|
const hourOfDay = byte5 & 0x1F
|
|
const minutes = byte4 & 0x3F
|
|
const seconds = byte3 & 0x3F
|
|
const f = !!(byte2 & 0x80)
|
|
const wd = !!(byte2 & 0x40)
|
|
const nwd = !!(byte2 & 0x20)
|
|
const ny = !!(byte2 & 0x10)
|
|
const nd = !!(byte2 & 0x8)
|
|
const ndow = !!(byte2 & 0x4)
|
|
const nt = !!(byte2 & 0x2)
|
|
const suti = !!(byte2 & 0x1)
|
|
const clq = !!(byte1 & 0x80)
|
|
const reliability = !!(byte1 & 0x40)
|
|
|
|
year += 1900
|
|
|
|
// Convert month from knx to JavaScript (12 => 11, 11 => 10, ...)
|
|
month -= 1
|
|
|
|
// Convert day of week from knx to JavaScript (7 => 0, 0 => undefined)
|
|
if (dayOfWeek === 7) {
|
|
dayOfWeek = 0
|
|
} else if (dayOfWeek === 0) {
|
|
dayOfWeek = undefined
|
|
}
|
|
|
|
// Check minutes and seconds if hours equals 24
|
|
if (hourOfDay === 24 && (minutes !== 0 || seconds !== 0)) {
|
|
throw new RangeError('Invalid time (hour of day is 24, but minutes or seconds are not 0)')
|
|
}
|
|
|
|
return {
|
|
dateTime: new Date(year, month, dayOfMonth, hourOfDay, minutes, seconds),
|
|
dayOfWeek: dayOfWeek,
|
|
f: f,
|
|
wd: wd,
|
|
nwd: nwd,
|
|
ny: ny,
|
|
nd: nd,
|
|
ndow: ndow,
|
|
nt: nt,
|
|
sst: suti,
|
|
internalClock: clq,
|
|
reliability: reliability
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Encode a buffer
|
|
*
|
|
* @param value the value to be converted to buffer
|
|
* @returns the buffer
|
|
*/
|
|
encoder(value: DPT19Result | Date): Buffer {
|
|
if (value === undefined || value === null)
|
|
throw new InvalidValueError(`Invalid value [${value}]`)
|
|
|
|
let temp: DPT19Result;
|
|
|
|
if (value.constructor.name === 'Date') {
|
|
temp = new DPT19Result()
|
|
temp.dateTime = value as Date
|
|
temp.dayOfWeek = (value as Date).getDay()
|
|
} else {
|
|
temp = value as DPT19Result
|
|
}
|
|
|
|
let dayOfWeek = temp.dayOfWeek
|
|
const f = !!temp.f ? 1 : 0
|
|
const wd = !!temp.wd ? 1 : 0
|
|
const nwd = !!temp.nwd ? 1 : 0
|
|
const ny = !!temp.ny ? 1 : 0
|
|
const nd = !!temp.nd ? 1 : 0
|
|
const ndow = !!temp.ndow ? 1 : 0
|
|
const nt = !!temp.nt ? 1 : 0
|
|
const sst = !!temp.sst ? 1 : 0
|
|
const clq = !!temp.internalClock ? 1 : 0
|
|
const reliability = !!temp.reliability ? 1 : 0
|
|
|
|
// Convert day of week from JavaScript to knx (0 => 7, undefined => 0)
|
|
if (typeof dayOfWeek === 'undefined') {
|
|
dayOfWeek = 0
|
|
} else if (dayOfWeek === 0) {
|
|
dayOfWeek = 7
|
|
}
|
|
|
|
const buffer = Buffer.alloc(8, 0)
|
|
buffer.writeUInt8(temp.dateTime.getFullYear() - 1900, 0)
|
|
buffer.writeUInt8(temp.dateTime.getMonth() + 1, 1)
|
|
buffer.writeUInt8(temp.dateTime.getDate(), 2)
|
|
buffer.writeUInt8((dayOfWeek << 5) | temp.dateTime.getHours(), 3)
|
|
buffer.writeUInt8(temp.dateTime.getMinutes(), 4)
|
|
buffer.writeUInt8(temp.dateTime.getSeconds(), 5)
|
|
buffer.writeUInt8((f << 7) | (wd << 6) | (nwd << 5) | (ny << 4) | (nd << 3) | (ndow << 2) | (nt << 1) | sst, 6)
|
|
buffer.writeUInt8(clq << 7 | reliability << 6, 7)
|
|
|
|
return buffer
|
|
}
|
|
subtypes: {
|
|
// 19.001
|
|
'001': {
|
|
name: 'DPT_DateTime',
|
|
desc: 'datetime',
|
|
},
|
|
};
|
|
|
|
}
|