Skip to content

Commit

Permalink
override info with device settings
Browse files Browse the repository at this point in the history
  • Loading branch information
sguernion committed Jan 30, 2024
1 parent b2ec683 commit 09c2a65
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 102 deletions.
1 change: 1 addition & 0 deletions .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jobs:
with:
node-version: 20
cache: pnpm
cache-dependency-path: './documentation/package-lock.json'

- name: Build Blog
env:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ jobs:
uses: docker/metadata-action@v5
with:
images: sguernion/rfxcom2mqtt
tags: |
type=raw,value=latest,enable={{is_default_branch}}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
Expand Down
74 changes: 37 additions & 37 deletions config.sample.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,45 +47,45 @@ rfxcom:
- remote
- security1

devices:
- id: '0x5C02'
title: 'Bathroom Temp & Hum'
devices:
- id: '0x5C02'
friendlyName: 'Bathroom Temp & Hum'

- id: '0xB9459A'
title: 'Garden motion'
- id: '0xB9459A'
friendlyName: 'Garden motion'

- id: '1001010/1'
name: 'CucuDimmer'
title: 'Kitchen Dimmer Light'
type: 'lighting2'
- id: '1001010/1'
name: 'CucuDimmer'
friendlyName: 'Kitchen Dimmer Light'
type: 'lighting2'

- id: '0x012E00FF'
name: 'Living Room switch'
- id: '0x012E00FF'
friendlyName: 'Living Room switch'

- id: '0x00ED400F'
name: 'Lights'
units:
- unitCode: '1'
name: 'Light1'
title: 'Living Room'
- unitCode: '2'
name: 'Light2'
title: 'Kitchen'
- unitCode: '3'
name: 'Light3'
title: 'Garage'
- unitCode: '4'
name: 'Light4'
title: 'Garden'
type: 'lighting2'
- id: '0x00ED400F'
name: 'Lights'
units:
- unitCode: '1'
name: 'Light1'
friendlyName: 'Living Room'
- unitCode: '2'
name: 'Light2'
friendlyName: 'Kitchen'
- unitCode: '3'
name: 'Light3'
friendlyName: 'Garage'
- unitCode: '4'
name: 'Light4'
friendlyName: 'Garden'
type: 'lighting2'

- id: '0x3D090F'
name: 'Switch1'
command: 'on'
title: 'Living Room Lights on'
type: 'lighting4'
- id: '0x3D090E'
name: 'Switch1'
command: 'off'
title: 'Living Room Lights off'
type: 'lighting4'
- id: '0x3D090F'
name: 'Switch1'
command: 'on'
friendlyName: 'Living Room Lights on'
type: 'lighting4'
- id: '0x3D090E'
name: 'Switch1'
command: 'off'
friendlyName: 'Living Room Lights off'
type: 'lighting4'
50 changes: 23 additions & 27 deletions documentation/usage/mqtt_topics_and_messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,43 +38,39 @@ Contains the state of the bridge, this message is published as retained. Payload

Contains information of an device.

Example payload on topic `"rfxcom2mqtt/devices/0x5C02"`:
Example payload on topic `"rfxcom2mqtt/devices/0x01A4F9BE/2"`:
```
{
"title": "Bathroom Temp & Hum",
"type":"temperaturehumidity1",
"subtype": 13,
"id": "0x5C03",
"seqnbr": 12,
"temperature": 18,
"humidity": 74,
"humidityStatus": 3,
"batteryLevel": 9,
"rssi": 6
}
{
"seqnbr": 4,
"subtype": 0,
"id": "0x01A4F9BE",
"unitCode": 2,
"commandNumber": 0,
"command": "Off",
"level": 0,
"rssi": 4,
"type": "lighting2",
"deviceName": [
"KlikAanKlikUit",
"HomeEasy UK",
"Chacon",
"NEXA",
"Intertechno"
],
"subTypeValue": "AC"
}
```

### Publish command examples (topic/payload)

```
rfxcom2mqtt/commmand/CucuDimmer
rfxcom2mqtt/cmd/lighting2/0/0x01A4F9BE/2/set
on
rfxcom2mqtt/commmand/CucuDimmer
rfxcom2mqtt/cmd/lighting2/0/0x01A4F9BE/2/set
off
rfxcom2mqtt/commmand/CucuDimmer
rfxcom2mqtt/cmd/lighting2/0/0x01A4F9BE/2/set
level 15
rfxcom2mqtt/commmand/Switch1 (lighting4, payload identifies device)
on
rfxcom2mqtt/commmand/Switch1
off
rfxcom2mqtt/commmand/Lights/Light1 (lighting2, unitName identifies device)
on
rfxcom2mqtt/commmand/Lights/Light1
off
```
21 changes: 14 additions & 7 deletions src/libs/Controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import {Settings,read} from './Settings';
import {Settings, SettingDevice, read} from './Settings';
import Discovery from './Discovery';
import Mqtt from './Mqtt';
import Rfxcom, {IRfxcom,MockRfxcom} from './RfxcomBridge';
Expand All @@ -25,7 +25,7 @@ export default class Controller implements MqttEventListener{
this.config = read(file);
logger.setLevel(this.config.loglevel);
logger.info("configuration : "+JSON.stringify(this.config));
this.rfxBridge = this.config.mock ? new MockRfxcom() : new Rfxcom(this.config.rfxcom);
this.rfxBridge = this.config.mock ? new MockRfxcom(this.config.rfxcom) : new Rfxcom(this.config.rfxcom);
this.mqttClient = new Mqtt(this.config)
this.discovery = new Discovery( this.mqttClient, this.rfxBridge, this.config );
this.mqttClient.addListener(this.discovery);
Expand Down Expand Up @@ -120,7 +120,7 @@ export default class Controller implements MqttEventListener{
let entityName = dn[3];
// Used for units and forms part of the device id
if (dn[4] !== undefined && dn[4].length > 0) {
entityName = entityName + '/' + dn[4];
entityName += '/' + dn[4];
}
this.rfxBridge.onCommand(deviceType, entityName, data.message);
return;
Expand All @@ -131,7 +131,7 @@ export default class Controller implements MqttEventListener{
}


sendToMQTT(type: any, evt: any,deviceConf: any) {
sendToMQTT(type: any, evt: any,deviceConf?: SettingDevice) {
logger.info("receive from rfxcom : "+JSON.stringify(evt));
// Add type to event!
evt.type = type;
Expand All @@ -145,17 +145,24 @@ export default class Controller implements MqttEventListener{
let topicEntity = deviceId;

// Get device config if available
if (deviceConf instanceof Object) {
if (deviceConf.name !== undefined) {
if (deviceConf?.name !== undefined) {
topicEntity = deviceConf.name;
}
}

const json = JSON.stringify(evt, null, 2);
const payload = JSON.parse(json);

if(payload.unitCode !== undefined && !this.rfxBridge.isGroup(payload)){
topicEntity += '/' + payload.unitCode;
if (deviceConf?.units) {
deviceConf?.units.forEach( unit => {
if(parseInt(unit.unitCode) === parseInt(payload.unitCode)){
if (unit.name !) {
topicEntity = unit.name;
}
}
});
}
}

this.mqttClient.publish(this.mqttClient.topics.devices + '/' + topicEntity, json, (error: any) => {});
Expand Down
83 changes: 63 additions & 20 deletions src/libs/Discovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

var rfxcom = require('rfxcom');
import {IRfxcom} from './RfxcomBridge';
import {Settings, SettingHass} from './Settings';
import {Settings, SettingHass, SettingDevice} from './Settings';
import Mqtt from './Mqtt';
import { DeviceEntity, DeviceBridge,BridgeInfo,MQTTMessage,MqttEventListener } from './models';
import utils from './utils';
Expand Down Expand Up @@ -92,9 +92,11 @@ export default class Discovery implements MqttEventListener{
export class HomeassistantDiscovery extends AbstractDiscovery{

protected state: State;
protected devicesConfig: SettingDevice[];

constructor(mqtt: Mqtt, rfxtrx: IRfxcom, config : Settings){
super(mqtt, rfxtrx, config);
this.devicesConfig = config.rfxcom.devices;
this.state = new State(config);
}

Expand Down Expand Up @@ -132,25 +134,43 @@ export class HomeassistantDiscovery extends AbstractDiscovery{

// get from save state
let entityState = this.state.get({id: entityName,type:deviceType,subtype:data.message.subtype})
entityState.deviceType = deviceType;
this.updateEntityStateFromValue(entityState,value);
this.rfxtrx.sendCommand(deviceType,subTypeValue,entityState.rfxFunction,id+"/"+unitCode);
this.mqtt.publish(this.mqtt.topics.devices + '/' + entityTopic, JSON.stringify(entityState), (error: any) => {},{retain: true, qos: 1});
this.rfxtrx.sendCommand(deviceType,subTypeValue,entityState.rfxFunction,entityTopic);
this.mqtt.publish(this.mqtt.topics.devices + '/' + entityName, JSON.stringify(entityState), (error: any) => {},{retain: true, qos: 1});
}

updateEntityStateFromValue(entityState: any,value: string){
if( entityState.type === 'lighting1' || entityState.type === 'lighting2' || entityState.type === 'lighting3'
|| entityState.type === 'lighting5' || entityState.type === 'lighting6') {
if (value === "On" || value === "Group On") {
//TODO load value from rfxcom commands
entityState.commandNumber = (value === "Group On")?4:1;
if( entityState.deviceType === 'lighting1' || entityState.deviceType === 'lighting2' || entityState.deviceType === 'lighting3'
|| entityState.deviceType === 'lighting5' || entityState.deviceType === 'lighting6' ) {
const cmd = value.toLowerCase().split(" ")
let command = cmd[0];
if (cmd[0] === "group") {
command = cmd[1];

}
if (command === "on") {
entityState.commandNumber = (cmd[0] === "group")?4:1; //WORK only for lithing2
entityState.rfxFunction = 'switchOn';
} else {
entityState.commandNumber = (value === "Group On")?3:0;
entityState.rfxFunction = 'switchOff';
} else if (command === "off") {
entityState.rfxFunction = (cmd[0] === "group")?3:0; //WORK only for lithing2
entityState.rfxCommand = 'switchOff';
}else{
if (cmd[0] === "level") {
entityState.rfxFunction = 'setLevel';
entityState.rfxOpt = cmd[1];
}
}
entityState.command = value;
}else if (entityState.deviceType === "lighting4") {
entityState.rfxFunction = 'sendData';
}else if (entityState.deviceType === "chime1") {
entityState.rfxFunction = 'chime';
} else {
logger.error('device type ('+entityState.deviceType+') not supported');
}



//TODO get command for other deviceType
}

Expand All @@ -160,19 +180,42 @@ export class HomeassistantDiscovery extends AbstractDiscovery{
const devicePrefix = this.config.discovery_device;
let id = payload.id;
let deviceId = payload.subTypeValue+"_"+id.replace("0x","");
let deviceTopic = payload.id
let deviceName = deviceId;
let entityId = payload.subTypeValue+"_"+id.replace("0x","");
let entityTopic = payload.id;
let entityName = payload.id;

let entityTopic = payload.id

const deviceConf = this.devicesConfig.find((dev: any) => dev.id === id);

if (deviceConf?.name !== undefined) {
entityTopic = deviceConf.name;
deviceTopic = deviceConf.name;
}


if(payload.unitCode !== undefined && !this.rfxtrx.isGroup(payload)){
entityId += '_' + payload.unitCode;
entityTopic += '/'+ payload.unitCode;
entityName += '_'+payload.unitCode;
if (deviceConf?.units) {
deviceConf?.units.forEach( unit => {
if(parseInt(unit.unitCode) === parseInt(payload.unitCode)){
if (unit.name !) {
entityTopic = unit.name;
}
}
});
}
}

this.state.set({id: entityName,type:payload.type,subtype:payload.subtype},payload,"event");

const deviceJson = new DeviceEntity([devicePrefix+'_'+deviceId],deviceId);
if (deviceConf?.friendlyName) {
deviceName = deviceConf?.friendlyName;
}

const deviceJson = new DeviceEntity([devicePrefix+'_'+deviceId,devicePrefix+'_'+deviceName],deviceName);

if( payload.rssi !== undefined ){
const json = {
Expand All @@ -182,16 +225,16 @@ export class HomeassistantDiscovery extends AbstractDiscovery{
entity_category: "diagnostic",
icon: "mdi:signal",
json_attributes_topic: this.topicDevice + '/' + entityTopic,
name: deviceId+" Linkquality",
object_id: deviceId+'_linkquality',
name: deviceName+" Linkquality",
object_id: deviceTopic+'_linkquality',
origin: this.discoveryOrigin,
state_class: "measurement",
state_topic: this.topicDevice + '/' + entityTopic,
unique_id: deviceId +'_linkquality_' + devicePrefix,
unique_id: deviceTopic +'_linkquality_' + devicePrefix,
unit_of_measurement:"dBm",
value_template: "{{ value_json.rssi }}"
};
this.publishDiscovery('sensor/' + deviceId +'/linkquality/config',JSON.stringify(json));
this.publishDiscovery('sensor/' + deviceTopic +'/linkquality/config',JSON.stringify(json));
}
if( payload.type === 'lighting1' || payload.type === 'lighting2' || payload.type === 'lighting3'
|| payload.type === 'lighting5' || payload.type === 'lighting6' ){
Expand Down Expand Up @@ -221,7 +264,7 @@ export class HomeassistantDiscovery extends AbstractDiscovery{
unique_id: entityId+'_'+devicePrefix,
value_template:"{{ value_json.command }}"
};
this.publishDiscovery('switch/' + entityId +'/config',JSON.stringify(json));
this.publishDiscovery('switch/' + entityTopic +'/config',JSON.stringify(json));
}

}
Expand Down
Loading

0 comments on commit 09c2a65

Please sign in to comment.