mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-07-02 10:01:37 +00:00
Add availability_mode for Home Assistant and publish availability only when its enabled for that device. #6281
This commit is contained in:
+5
-6
@@ -88,13 +88,12 @@ class Controller {
|
||||
this.extensions.push(new ExtensionSoftReset(...args));
|
||||
}
|
||||
|
||||
if (settings.get().advanced.availability_timeout || settings.get().availability) {
|
||||
if (settings.get().experimental.availability_new) {
|
||||
this.extensions.push(new ExtensionAvailabilityNew(...args));
|
||||
} else {
|
||||
this.extensions.push(new ExtensionAvailability(...args));
|
||||
}
|
||||
if (settings.get().experimental.availability_new) {
|
||||
this.extensions.push(new ExtensionAvailabilityNew(...args));
|
||||
} else if (settings.get().advanced.availability_timeout) {
|
||||
this.extensions.push(new ExtensionAvailability(...args));
|
||||
}
|
||||
|
||||
this.extensions.push(new ExtensionExternalExtension(...args));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import ExtensionTS from './extensionts';
|
||||
import logger from '../util/logger';
|
||||
import {sleep} from '../util/utils';
|
||||
import {sleep, isAvailabilityNewEnabledForDevice} from '../util/utils';
|
||||
import * as settings from '../util/settings';
|
||||
|
||||
const hours = (hours: number): number => 1000 * 60 * 60 * hours;
|
||||
@@ -9,9 +9,9 @@ const seconds = (seconds: number): number => 1000 * seconds;
|
||||
|
||||
// TODO
|
||||
// - State retrieval
|
||||
// - Home Assistant add availability mode
|
||||
// - Honour legacy availability_timeout, availability_blocklist and availability_passlist options.
|
||||
// - Enable for HA addon
|
||||
// - Add to setting schema
|
||||
class AvailabilityNew extends ExtensionTS {
|
||||
private timers: {[s: string]: NodeJS.Timeout} = {};
|
||||
private availabilityCache: {[s: string]: boolean} = {};
|
||||
@@ -25,10 +25,6 @@ class AvailabilityNew extends ExtensionTS {
|
||||
logger.warn('Using experimental new availability feature');
|
||||
}
|
||||
|
||||
private isEnabledForDevice(re: ResolvedEntity): boolean {
|
||||
return re.settings.hasOwnProperty('availability') ? !!re.settings.availability : !!settings.get().availability;
|
||||
}
|
||||
|
||||
private getTimeout(re: ResolvedEntity): number {
|
||||
if (typeof re.settings.availability === 'object' && re.settings.availability?.timeout != null) {
|
||||
return minutes(re.settings.availability.timeout);
|
||||
@@ -114,11 +110,10 @@ class AvailabilityNew extends ExtensionTS {
|
||||
override onMQTTConnected(): void {
|
||||
for (const device of this.zigbee.getClients()) {
|
||||
const re: ResolvedEntity = this.zigbee.resolveEntity(device);
|
||||
if (isAvailabilityNewEnabledForDevice(re, settings)) {
|
||||
// Publish initial availablility
|
||||
this.publishAvailability(re, true);
|
||||
|
||||
// Publish initial availablility
|
||||
this.publishAvailability(re, true);
|
||||
|
||||
if (this.isEnabledForDevice(re)) {
|
||||
this.resetTimer(re);
|
||||
|
||||
// If an active device is initially unavailable, ping it.
|
||||
@@ -134,8 +129,7 @@ class AvailabilityNew extends ExtensionTS {
|
||||
}
|
||||
|
||||
private publishAvailability(re: ResolvedEntity, logLastSeen: boolean): void {
|
||||
const enabled = this.isEnabledForDevice(re);
|
||||
if (enabled && logLastSeen) {
|
||||
if (logLastSeen) {
|
||||
const ago = Date.now() - re.device.lastSeen;
|
||||
if (this.isActiveDevice(re)) {
|
||||
logger.debug(
|
||||
@@ -145,7 +139,7 @@ class AvailabilityNew extends ExtensionTS {
|
||||
}
|
||||
}
|
||||
|
||||
const available = enabled ? this.isAvailable(re) : true;
|
||||
const available = this.isAvailable(re);
|
||||
if (this.availabilityCache[re.device.ieeeAddr] == available) {
|
||||
return;
|
||||
}
|
||||
@@ -158,7 +152,7 @@ class AvailabilityNew extends ExtensionTS {
|
||||
|
||||
private lastSeenChanged(data: {device: Device}): void {
|
||||
const re = this.zigbee.resolveEntity(data.device);
|
||||
if (this.isEnabledForDevice(re)) {
|
||||
if (isAvailabilityNewEnabledForDevice(re, settings)) {
|
||||
// Remove from ping queue, not necessary anymore since we know the device is online.
|
||||
this.removeFromPingQueue(re);
|
||||
this.resetTimer(re);
|
||||
|
||||
@@ -855,8 +855,11 @@ class HomeAssistant extends Extension {
|
||||
|
||||
// Availability payload
|
||||
payload.availability = [{topic: `${settings.get().mqtt.base_topic}/bridge/state`}];
|
||||
const availabilityEnabled = settings.get().availability || settings.get().advanced.availability_timeout ||
|
||||
resolvedEntity.settings.availability;
|
||||
payload.availability_mode = 'all';
|
||||
/* istanbul ignore next */
|
||||
const availabilityEnabled = settings.get().experimental.availability_new ?
|
||||
utils.isAvailabilityNewEnabledForDevice(resolvedEntity, settings) :
|
||||
settings.get().advanced.availability_timeout;
|
||||
if (resolvedEntity.type === 'device' && availabilityEnabled) {
|
||||
payload.availability.push({topic: `${settings.get().mqtt.base_topic}/${friendlyName}/availability`});
|
||||
}
|
||||
|
||||
@@ -267,6 +267,10 @@ function sanitizeImageParameter(parameter) {
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
function isAvailabilityNewEnabledForDevice(re, settings) {
|
||||
return re.settings.hasOwnProperty('availability') ? !!re.settings.availability : !!settings.get().availability;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
millisecondsToSeconds: (milliseconds) => milliseconds / 1000,
|
||||
secondsToMilliseconds: (seconds) => seconds * 1000,
|
||||
@@ -292,6 +296,7 @@ module.exports = {
|
||||
validateFriendlyName,
|
||||
loadModuleFromFile,
|
||||
loadModuleFromText,
|
||||
isAvailabilityNewEnabledForDevice,
|
||||
getKey,
|
||||
sanitizeImageParameter,
|
||||
removeNullPropertiesFromObject,
|
||||
|
||||
@@ -71,8 +71,6 @@ describe('Availability', () => {
|
||||
'online', {retain: true, qos: 0}, expect.any(Function));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote/availability',
|
||||
'online', {retain: true, qos: 0}, expect.any(Function));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color_2/availability',
|
||||
'online', {retain: true, qos: 0}, expect.any(Function));
|
||||
});
|
||||
|
||||
it('Should publish offline for active device when not seen for 10 minutes', async () => {
|
||||
|
||||
+30
-15
@@ -62,6 +62,7 @@ describe('HomeAssistant extension', () => {
|
||||
|
||||
payload = {
|
||||
"availability":[{"topic":"zigbee2mqtt/bridge/state"}],
|
||||
"availability_mode": "all",
|
||||
"brightness":true,
|
||||
"brightness_scale":254,
|
||||
"color_mode":true,
|
||||
@@ -93,6 +94,7 @@ describe('HomeAssistant extension', () => {
|
||||
|
||||
payload = {
|
||||
"availability":[{"topic":"zigbee2mqtt/bridge/state"}],
|
||||
"availability_mode": "all",
|
||||
"command_topic":"zigbee2mqtt/ha_discovery_group/set",
|
||||
"device":{
|
||||
"identifiers":["zigbee2mqtt_1221051039810110150109113116116_9"],
|
||||
@@ -132,6 +134,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -158,6 +161,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -184,6 +188,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -210,6 +215,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -237,6 +243,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -247,11 +254,8 @@ describe('HomeAssistant extension', () => {
|
||||
);
|
||||
|
||||
payload = {
|
||||
"availability":[
|
||||
{
|
||||
"topic":"zigbee2mqtt/bridge/state"
|
||||
}
|
||||
],
|
||||
"availability":[{"topic":"zigbee2mqtt/bridge/state"}],
|
||||
"availability_mode": "all",
|
||||
"command_topic":"zigbee2mqtt/wall_switch_double/left/set",
|
||||
"device":{
|
||||
"identifiers":[
|
||||
@@ -279,11 +283,8 @@ describe('HomeAssistant extension', () => {
|
||||
);
|
||||
|
||||
payload = {
|
||||
"availability":[
|
||||
{
|
||||
"topic":"zigbee2mqtt/bridge/state"
|
||||
}
|
||||
],
|
||||
"availability":[{"topic":"zigbee2mqtt/bridge/state"}],
|
||||
"availability_mode": "all",
|
||||
"command_topic":"zigbee2mqtt/wall_switch_double/right/set",
|
||||
"device":{
|
||||
"identifiers":[
|
||||
@@ -311,11 +312,8 @@ describe('HomeAssistant extension', () => {
|
||||
);
|
||||
|
||||
payload = {
|
||||
"availability":[
|
||||
{
|
||||
"topic":"zigbee2mqtt/bridge/state"
|
||||
}
|
||||
],
|
||||
"availability":[{"topic":"zigbee2mqtt/bridge/state"}],
|
||||
"availability_mode": "all",
|
||||
"brightness":true,
|
||||
"brightness_scale":254,
|
||||
"color_mode": true,
|
||||
@@ -388,6 +386,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -414,6 +413,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -440,6 +440,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -497,6 +498,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'From Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
'expire_after': 90,
|
||||
'icon': 'mdi:test',
|
||||
};
|
||||
@@ -524,6 +526,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'Not from Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
'expire_after': 30,
|
||||
'icon': 'mdi:test',
|
||||
};
|
||||
@@ -560,6 +563,7 @@ describe('HomeAssistant extension', () => {
|
||||
|
||||
payload = {
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
"command_topic": "zigbee2mqtt/my_switch/set",
|
||||
"device": {
|
||||
"identifiers": [
|
||||
@@ -659,6 +663,7 @@ describe('HomeAssistant extension', () => {
|
||||
"manufacturer":"Hampton Bay"
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -678,6 +683,7 @@ describe('HomeAssistant extension', () => {
|
||||
|
||||
payload = {
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
"away_mode_command_topic":"zigbee2mqtt/TS0601_thermostat/set/away_mode",
|
||||
"away_mode_state_template":"{{ value_json.away_mode }}",
|
||||
"away_mode_state_topic":"zigbee2mqtt/TS0601_thermostat",
|
||||
@@ -754,6 +760,7 @@ describe('HomeAssistant extension', () => {
|
||||
manufacturer: 'Keen Home'
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -789,6 +796,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -932,6 +940,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -1055,6 +1064,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}, {topic: 'zigbee2mqtt/weather_sensor/availability'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -1142,6 +1152,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -1213,6 +1224,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -1246,6 +1258,7 @@ describe('HomeAssistant extension', () => {
|
||||
"manufacturer":"IKEA"
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -1694,6 +1707,7 @@ describe('HomeAssistant extension', () => {
|
||||
'manufacturer': 'Xiaomi',
|
||||
},
|
||||
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
|
||||
'availability_mode': 'all',
|
||||
};
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -1714,6 +1728,7 @@ describe('HomeAssistant extension', () => {
|
||||
|
||||
const payload = {
|
||||
"availability":[{"topic":"zigbee2mqtt/bridge/state"}],
|
||||
"availability_mode": "all",
|
||||
"brightness":true,
|
||||
"brightness_scale":254,
|
||||
"color_mode":true,
|
||||
|
||||
Reference in New Issue
Block a user