Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor non-string keyed objects into arrays #116

Merged
merged 3 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 54 additions & 47 deletions app/enervent.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,54 +38,61 @@ export const AVAILABLE_SETTINGS = {
'defrostingAllowed': 55,
}

export const AVAILABLE_ALARMS = {
// Alarm number
// Name and descr based on Enervent EN EDA Modbus regirsters: 3x0385
// Waste air is used in some places where we don't care about if it's before (Extract)
// or after (Exhaust) the HRC. Filter sits before HRC, and fans after HRC.

1: { name: 'TE5SupplyAirAfterHRCold', description: 'TE5 Supply air after heat recovery cold' },
2: { name: 'TE10SupplyAirAfterHeaterCold', description: 'TE10 Supply air after heater cold' },
3: { name: 'TE10SupplyAirAfterHeaterHot', description: 'TE10 Supply air after heater hot' },
4: { name: 'TE20RoomTempHot', description: 'TE20 Room temperature hot' },
5: { name: 'TE30ExtractAirCold', description: 'TE30 Extract air cold' },
6: { name: 'TE30ExtractAirHot', description: 'TE30 Extract air hot' },
7: { name: 'HPError', description: 'Heatpump' },
8: { name: 'EHError', description: 'Electrical heater' },
9: { name: 'ReturnWaterCold', description: 'Return water cold' },
10: { name: 'HRError', description: 'Heat recovery' },
11: { name: 'CoolingError', description: 'Cooling' },
12: { name: 'EmergencyStop', description: 'Emergency stop' },
13: { name: 'FireRisk', description: 'Fire risk' },
14: { name: 'ServiceReminder', description: 'Service reminder' },
15: { name: 'EHPDA', description: 'Electrical heater pressure switch' },
16: { name: 'SupplyFilterDirty', description: 'Supply filter dirty' },
17: { name: 'ExtractFilterDirty', description: 'Waste filter dirty' },
20: { name: 'SupplyFanPressureError', description: 'Supply fan pressure' },
21: { name: 'ExtractFanPressureError', description: 'Waste fan pressure' },
}
export const AVAILABLE_ALARMS = [
{ name: 'TE5SupplyAirAfterHRCold', description: 'TE5 Supply air after heat recovery cold', type: 1 },
{ name: 'TE10SupplyAirAfterHeaterCold', description: 'TE10 Supply air after heater cold', type: 2 },
{ name: 'TE10SupplyAirAfterHeaterHot', description: 'TE10 Supply air after heater hot', type: 3 },
{ name: 'TE20RoomTempHot', description: 'TE20 Room temperature hot', type: 4 },
{ name: 'TE30ExtractAirCold', description: 'TE30 Extract air cold', type: 5 },
{ name: 'TE30ExtractAirHot', description: 'TE30 Extract air hot', type: 6 },
{ name: 'HPError', description: 'Heatpump', type: 7 },
{ name: 'EHError', description: 'Electrical heater', type: 8 },
{ name: 'ReturnWaterCold', description: 'Return water cold', type: 9 },
{ name: 'HRError', description: 'Heat recovery', type: 10 },
{ name: 'CoolingError', description: 'Cooling', type: 11 },
{ name: 'EmergencyStop', description: 'Emergency stop', type: 12 },
{ name: 'FireRisk', description: 'Fire risk', type: 13 },
{ name: 'ServiceReminder', description: 'Service reminder', type: 14 },
{ name: 'EHPDA', description: 'Electrical heater pressure switch', type: 15 },
{ name: 'SupplyFilterDirty', description: 'Supply filter dirty', type: 16 },
{ name: 'ExtractFilterDirty', description: 'Waste filter dirty', type: 17 },
{ name: 'SupplyFanPressureError', description: 'Supply fan pressure', type: 20 },
{ name: 'ExtractFanPressureError', description: 'Waste fan pressure', type: 21 },
]

export const SENSOR_TYPE_NONE = 'NONE'
export const SENSOR_TYPE_CO2 = 'CO2'
export const SENSOR_TYPE_RH = 'RH'
export const SENSOR_TYPE_ROOM_TEMP = 'ROOM_TEMP'

// 0=NA, 1=CO2_1, 2=CO2_2, 3=CO2_3, 4=RH_1, 5=RH_2, 6=RH_3, 7=OUT_TERM, 8=ROOM_TERM_1,
// 9=ROOM_TERM_2, 10=ROOM_TERM_3, 11=TEMP_SP, 12=Time relay, 13=External heating disable, 14=External cooling disable,
// 15=PDE10, 16=PDE30
export const ANALOG_INPUT_SENSOR_TYPES = {
export const ANALOG_INPUT_SENSOR_TYPES = [
// Skip sensor types we can't handle
0: { type: SENSOR_TYPE_NONE },
1: { type: SENSOR_TYPE_CO2, name: 'analogInputCo21', description: 'CO2 #1' },
2: { type: SENSOR_TYPE_CO2, name: 'analogInputCo22', description: 'CO2 #1' },
3: { type: SENSOR_TYPE_CO2, name: 'analogInputCo23', description: 'CO2 #1' },
4: { type: SENSOR_TYPE_RH, name: 'analogInputHumidity1', description: 'RH #1' },
5: { type: SENSOR_TYPE_RH, name: 'analogInputHumidity2', description: 'RH #1' },
6: { type: SENSOR_TYPE_RH, name: 'analogInputHumidity3', description: 'RH #1' },
8: { type: SENSOR_TYPE_ROOM_TEMP, name: 'analogInputRoomTemperature1', description: 'Room temperature #1' },
9: { type: SENSOR_TYPE_ROOM_TEMP, name: 'analogInputRoomTemperature2', description: 'Room temperature #1' },
10: { type: SENSOR_TYPE_ROOM_TEMP, name: 'analogInputRoomTemperature3', description: 'Room temperature #1' },
}
{ type: 0, sensorType: SENSOR_TYPE_NONE },
{ type: 1, sensorType: SENSOR_TYPE_CO2, name: 'analogInputCo21', description: 'CO2 #1' },
{ type: 2, sensorType: SENSOR_TYPE_CO2, name: 'analogInputCo22', description: 'CO2 #1' },
{ type: 3, sensorType: SENSOR_TYPE_CO2, name: 'analogInputCo23', description: 'CO2 #1' },
{ type: 4, sensorType: SENSOR_TYPE_RH, name: 'analogInputHumidity1', description: 'RH #1' },
{ type: 5, sensorType: SENSOR_TYPE_RH, name: 'analogInputHumidity2', description: 'RH #1' },
{ type: 6, sensorType: SENSOR_TYPE_RH, name: 'analogInputHumidity3', description: 'RH #1' },
{
type: 8,
sensorType: SENSOR_TYPE_ROOM_TEMP,
name: 'analogInputRoomTemperature1',
description: 'Room temperature #1',
},
{
type: 9,
sensorType: SENSOR_TYPE_ROOM_TEMP,
name: 'analogInputRoomTemperature2',
description: 'Room temperature #1',
},
{
type: 10,
sensorType: SENSOR_TYPE_ROOM_TEMP,
name: 'analogInputRoomTemperature3',
description: 'Room temperature #1',
},
]

export const AUTOMATION_TYPE_LEGACY_EDA = 'LEGACY_EDA'
export const AUTOMATION_TYPE_EDA = 'EDA'
Expand Down Expand Up @@ -201,9 +208,9 @@ export const parseStateBitField = (state) => {

export const hasRoomTemperatureSensor = (sensorTypesResult) => {
for (let i = 0; i < 6; i++) {
const sensorType = ANALOG_INPUT_SENSOR_TYPES[sensorTypesResult.data[i]]
const sensor = ANALOG_INPUT_SENSOR_TYPES.find((sensor) => sensor.type === sensorTypesResult.data[i])

if (sensorType.type === SENSOR_TYPE_ROOM_TEMP) {
if (sensor?.sensorType === SENSOR_TYPE_ROOM_TEMP) {
return true
}
}
Expand All @@ -215,17 +222,17 @@ export const parseAnalogSensors = (sensorTypesResult, sensorValuesResult) => {
const sensorReadings = {}

for (let i = 0; i < 6; i++) {
const sensorType = ANALOG_INPUT_SENSOR_TYPES[sensorTypesResult.data[i]]
const sensor = ANALOG_INPUT_SENSOR_TYPES.find((sensor) => sensor.type === sensorTypesResult.data[i])

switch (sensorType.type) {
switch (sensor?.sensorType) {
// Use raw value
case SENSOR_TYPE_CO2:
case SENSOR_TYPE_RH:
sensorReadings[sensorType.name] = sensorValuesResult.data[i]
sensorReadings[sensor.name] = sensorValuesResult.data[i]
break
// Parse as temperature
case SENSOR_TYPE_ROOM_TEMP:
sensorReadings[sensorType.name] = parseTemperature(sensorValuesResult.data[i])
sensorReadings[sensor.name] = parseTemperature(sensorValuesResult.data[i])
break
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/homeassistant.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ export const configureMqttDiscovery = async (modbusClient, mqttClient) => {
// Binary sensors for alarms
let binarySensorConfigurationMap = {}

for (const [, alarm] of Object.entries(AVAILABLE_ALARMS)) {
for (const alarm of AVAILABLE_ALARMS) {
binarySensorConfigurationMap[alarm.name] = createAlarmConfiguration(configurationBase, alarm)
}

Expand Down
22 changes: 10 additions & 12 deletions app/modbus.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -339,17 +339,15 @@ export const getDeviceInformation = async (modbusClient) => {
}

export const getAlarmSummary = async (modbusClient) => {
let alarmSummary = { ...AVAILABLE_ALARMS }
const alarmSummary = []
const newestAlarm = await getNewestAlarm(modbusClient)

for (const type in alarmSummary) {
// Use "off" as the default alarm state, most likely to be true
alarmSummary[type].state = 0

// Use the state from the newest alarm
if (type === newestAlarm.type) {
alarmSummary[type].state = newestAlarm.state
}
for (const alarm of AVAILABLE_ALARMS) {
// Add each available alarm and have the state reflect the currently active alarm's state, if any
alarmSummary.push({
...alarm,
state: alarm.type === newestAlarm?.type ? newestAlarm?.state : 0,
})
}

return alarmSummary
Expand All @@ -362,13 +360,13 @@ export const getNewestAlarm = async (modbusClient) => {
const state = result.data[1]
const timestamp = parseAlarmTimestamp(result)

if (AVAILABLE_ALARMS[type] === undefined) {
const alarm = AVAILABLE_ALARMS.find((alarm) => alarm.type === type)
if (alarm === undefined) {
return null
}

return {
...AVAILABLE_ALARMS[type],
type,
...alarm,
state,
timestamp,
}
Expand Down
7 changes: 3 additions & 4 deletions tests/enervent.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -181,14 +181,13 @@ test('parseAnalogSensors', () => {
'analogInputCo21': 450,
})

// Multitude of sensors
typesResult = { data: [1, 2, 4, 5, 8, 9] }
valuesResult = { data: [450, 481, 45, 46, 192, 201] }
// Multitude of sensors, including ones we don't support
typesResult = { data: [1, 2, 4, 7, 8, 9] }
valuesResult = { data: [450, 481, 45, 210, 192, 201] }
expect(parseAnalogSensors(typesResult, valuesResult)).toEqual({
'analogInputCo21': 450,
'analogInputCo22': 481,
'analogInputHumidity1': 45,
'analogInputHumidity2': 46,
'analogInputRoomTemperature1': 19.2,
'analogInputRoomTemperature2': 20.1,
})
Expand Down
Loading