Skip to content

Commit

Permalink
Merge pull request #174 from icanos/feature/0.7.0
Browse files Browse the repository at this point in the history
Release 0.7.0
  • Loading branch information
SweVictor authored Mar 23, 2021
2 parents e986c8b + 8dd4397 commit afe7dba
Show file tree
Hide file tree
Showing 11 changed files with 767 additions and 419 deletions.
14 changes: 14 additions & 0 deletions plejd/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog hassio-plejd Home Assistant Plejd addon

## [0.7.0](https://github.com/icanos/hassio-plejd/tree/0.7.0) (2021-03-23)

[Full Changelog](https://github.com/icanos/hassio-plejd/compare/0.6.2...0.7.0)

**Closed issues:**

- \[plejd-ble\] Unable to connect. Software caused connection abort [\#173](https://github.com/icanos/hassio-plejd/issues/173)
- All logs seam to be OK but it´s not working anyway [\#171](https://github.com/icanos/hassio-plejd/issues/171)
- Include rooms as lights does not work in 0.6.1 [\#169](https://github.com/icanos/hassio-plejd/issues/169)

**Merged pull requests:**

- Feature/restructure ble [\#167](https://github.com/icanos/hassio-plejd/pull/167) ([SweVictor](https://github.com/SweVictor))

### [0.6.2](https://github.com/icanos/hassio-plejd/tree/0.6.2) (2021-02-27)

[Full Changelog](https://github.com/icanos/hassio-plejd/compare/0.6.1...0.6.2)
Expand Down
105 changes: 91 additions & 14 deletions plejd/DeviceRegistry.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
const Logger = require('./Logger');

const logger = Logger.getLogger('device-registry');
class DeviceRegistry {
apiSite;
cryptoKey = null;
Expand All @@ -19,20 +22,70 @@ class DeviceRegistry {
}

addPlejdDevice(device) {
this.plejdDevices[device.id] = device;
this.deviceIdsBySerial[device.serialNumber] = device.id;
if (!this.deviceIdsByRoom[device.roomId]) {
this.deviceIdsByRoom[device.roomId] = [];
const added = {
...this.plejdDevices[device.id],
...device,
};

this.plejdDevices = {
...this.plejdDevices,
[added.id]: added,
};

this.deviceIdsBySerial[added.serialNumber] = added.id;

logger.verbose(
`Added/updated device: ${JSON.stringify(added)}. ${
Object.keys(this.plejdDevices).length
} plejd devices in total.`,
);

if (added.roomId) {
const room = this.deviceIdsByRoom[added.roomId] || [];
if (!room.includes(added.roomId)) {
this.deviceIdsByRoom[added.roomId] = [...room, added.roomId];
}
logger.verbose(
`Added to room #${added.roomId}: ${JSON.stringify(this.deviceIdsByRoom[added.roomId])}`,
);
}
this.deviceIdsByRoom[device.roomId].push(device.id);

return added;
}

addScene(scene) {
this.sceneDevices[scene.id] = scene;
addRoomDevice(device) {
const added = {
...this.roomDevices[device.id],
...device,
};
this.roomDevices = {
...this.roomDevices,
[added.id]: added,
};

logger.verbose(
`Added/updated room device: ${JSON.stringify(added)}. ${
Object.keys(this.roomDevices).length
} room devices total.`,
);
return added;
}

setApiSite(siteDetails) {
this.apiSite = siteDetails;
addScene(scene) {
const added = {
...this.sceneDevices[scene.id],
...scene,
};
this.sceneDevices = {
...this.sceneDevices,
[added.id]: added,
};
logger.verbose(
`Added/updated scene: ${JSON.stringify(added)}. ${
Object.keys(this.sceneDevices).length
} scenes in total.`,
);
return added;
}

clearPlejdDevices() {
Expand All @@ -41,10 +94,6 @@ class DeviceRegistry {
this.deviceIdsBySerial = {};
}

addRoomDevice(device) {
this.roomDevices[device.id] = device;
}

clearRoomDevices() {
this.roomDevices = {};
}
Expand All @@ -62,7 +111,7 @@ class DeviceRegistry {
}

getDeviceBySerialNumber(serialNumber) {
return this.plejdDevices[this.deviceIdsBySerial[serialNumber]];
return this.getDevice(this.deviceIdsBySerial[serialNumber]);
}

getDeviceName(deviceId) {
Expand All @@ -76,6 +125,34 @@ class DeviceRegistry {
getSceneName(sceneId) {
return (this.sceneDevices[sceneId] || {}).name;
}

getState(deviceId) {
const device = this.getDevice(deviceId) || {};
if (device.dimmable) {
return {
state: device.state,
dim: device.dim,
};
}
return {
state: device.state,
};
}

setApiSite(siteDetails) {
this.apiSite = siteDetails;
}

setState(deviceId, state, dim) {
const device = this.getDevice(deviceId) || this.addPlejdDevice({ id: deviceId });
device.state = state;
if (dim && device.dimmable) {
device.dim = dim;
}
if (Logger.shouldLog('silly')) {
logger.silly(`Updated state: ${JSON.stringify(device)}`);
}
}
}

module.exports = DeviceRegistry;
12 changes: 1 addition & 11 deletions plejd/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,9 @@ ENV LANG C.UTF-8
SHELL ["/bin/bash", "-o", "pipefail", "-c"]

# Copy data for add-on
COPY ./*.js /plejd/
COPY ./config.json /plejd/
COPY ./Configuration.js /plejd/
COPY ./DeviceRegistry.js /plejd/
COPY ./Logger.js /plejd/
COPY ./main.js /plejd/
COPY ./MqttClient.js /plejd/
COPY ./package.json /plejd/
COPY ./PlejdAddon.js /plejd/
COPY ./PlejdApi.js /plejd/
COPY ./PlejdBLEHandler.js /plejd/
COPY ./Scene.js /plejd/
COPY ./SceneManager.js /plejd/
COPY ./SceneStep.js /plejd/

ARG BUILD_ARCH

Expand Down
128 changes: 70 additions & 58 deletions plejd/MqttClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ const getSwitchPayload = (device) => ({
class MqttClient extends EventEmitter {
deviceRegistry;

static EVENTS = {
connected: 'connected',
stateChanged: 'stateChanged',
};

constructor(deviceRegistry) {
super();

Expand All @@ -94,10 +99,10 @@ class MqttClient extends EventEmitter {

this.client.subscribe(startTopics, (err) => {
if (err) {
logger.error('Unable to subscribe to status topics');
logger.error('Unable to subscribe to status topics', err);
}

this.emit('connected');
this.emit(MqttClient.EVENTS.connected);
});

this.client.subscribe(getSubscribePath(), (err) => {
Expand All @@ -113,67 +118,70 @@ class MqttClient extends EventEmitter {
});

this.client.on('message', (topic, message) => {
if (startTopics.includes(topic)) {
logger.info('Home Assistant has started. lets do discovery.');
this.emit('connected');
} else {
const decodedTopic = decodeTopic(topic);
if (decodedTopic) {
let device = this.deviceRegistry.getDevice(decodedTopic.id);

const messageString = message.toString();
const isJsonMessage = messageString.startsWith('{');
const command = isJsonMessage ? JSON.parse(messageString) : messageString;

if (
!isJsonMessage
&& messageString === 'ON'
&& this.deviceRegistry.getScene(decodedTopic.id)
) {
// Guess that id that got state command without dim value belongs to Scene, not Device
// This guess could very well be wrong depending on the installation...
logger.warn(
`Device id ${decodedTopic.id} belongs to both scene and device, guessing Scene is what should be set to ON.`
+ 'OFF commands still sent to device.',
);
device = this.deviceRegistry.getScene(decodedTopic.id);
}

const deviceName = device ? device.name : '';

switch (decodedTopic.command) {
case 'set':
logger.verbose(
`Got mqtt SET command for ${decodedTopic.type}, ${deviceName} (${decodedTopic.id}): ${messageString}`,
try {
if (startTopics.includes(topic)) {
logger.info('Home Assistant has started. lets do discovery.');
this.emit(MqttClient.EVENTS.connected);
} else {
const decodedTopic = decodeTopic(topic);
if (decodedTopic) {
let device = this.deviceRegistry.getDevice(decodedTopic.id);

const messageString = message.toString();
const isJsonMessage = messageString.startsWith('{');
const command = isJsonMessage ? JSON.parse(messageString) : messageString;

if (
!isJsonMessage
&& messageString === 'ON'
&& this.deviceRegistry.getScene(decodedTopic.id)
) {
// Guess that id that got state command without dim value belongs to Scene, not Device
// This guess could very well be wrong depending on the installation...
logger.warn(
`Device id ${decodedTopic.id} belongs to both scene and device, guessing Scene is what should be set to ON. `
+ 'OFF commands still sent to device.',
);
device = this.deviceRegistry.getScene(decodedTopic.id);
}
const deviceName = device ? device.name : '';

switch (decodedTopic.command) {
case 'set':
logger.verbose(
`Got mqtt SET command for ${decodedTopic.type}, ${deviceName} (${decodedTopic.id}): ${messageString}`,
);

if (device) {
this.emit('stateChanged', device, command);
} else {
logger.warn(
`Device for topic ${topic} not found! Can happen if HA calls previously existing devices.`,
if (device) {
this.emit(MqttClient.EVENTS.stateChanged, device, command);
} else {
logger.warn(
`Device for topic ${topic} not found! Can happen if HA calls previously existing devices.`,
);
}
break;
case 'state':
case 'config':
case 'availability':
logger.verbose(
`Sent mqtt ${decodedTopic.command} command for ${
decodedTopic.type
}, ${deviceName} (${decodedTopic.id}). ${
decodedTopic.command === 'availability' ? messageString : ''
}`,
);
}
break;
case 'state':
case 'config':
case 'availability':
logger.verbose(
`Sent mqtt ${decodedTopic.command} command for ${
decodedTopic.type
}, ${deviceName} (${decodedTopic.id}). ${
decodedTopic.command === 'availability' ? messageString : ''
}`,
);
break;
default:
logger.verbose(`Warning: Unknown command ${decodedTopic.command} in decoded topic`);
break;
default:
logger.verbose(`Warning: Unknown command ${decodedTopic.command} in decoded topic`);
}
} else {
logger.verbose(
`Warning: Got unrecognized mqtt command on '${topic}': ${message.toString()}`,
);
}
} else {
logger.verbose(
`Warning: Got unrecognized mqtt command on '${topic}': ${message.toString()}`,
);
}
} catch (err) {
logger.error(`Error processing mqtt message on topic ${topic}`, err);
}
});
}
Expand All @@ -182,6 +190,10 @@ class MqttClient extends EventEmitter {
this.client.reconnect();
}

cleanup() {
this.client.removeAllListeners();
}

disconnect(callback) {
this.deviceRegistry.allDevices.forEach((device) => {
this.client.publish(getAvailabilityTopic(device), 'offline');
Expand Down
Loading

0 comments on commit afe7dba

Please sign in to comment.