Skip to content

Commit

Permalink
feat: mark entities as unavailable on disconnect, shutdown & crash
Browse files Browse the repository at this point in the history
This implementation makes the HA availability a bit more robust. In
comparison to the previous version it makes use of the last will message
to also mark entities available if the connection is lost or the
application doesn't exit gracefully. It also extends availability
support to distributed entities with the exception of the state-unlocked
ones.

Closes #181, closes #348
  • Loading branch information
mKeRix committed May 23, 2021
1 parent 1a4d662 commit 91b7cc1
Show file tree
Hide file tree
Showing 4 changed files with 606 additions and 174 deletions.
56 changes: 52 additions & 4 deletions src/integrations/home-assistant/entity-config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
import { Device } from './device';

export const PROPERTY_DENYLIST = [
'component',
'configTopic',
'commandStore',
'_instanceStatusTopic',
'_entityStatusTopic',
'distributed',
'stateLocked',
];

export type AvailabilityStatus = 'online' | 'offline';
type AvailabilityMode = 'all' | 'any' | 'latest';

export class Availability {
payloadAvailable: AvailabilityStatus = 'online';
payloadNotAvailable: AvailabilityStatus = 'offline';

constructor(public topic: string) {}
}

export abstract class EntityConfig {
uniqueId: string;
component: string;
Expand All @@ -10,9 +30,13 @@ export abstract class EntityConfig {
configTopic: string;
stateTopic: string;
jsonAttributesTopic: string;
availabilityTopic: string;
payloadAvailable = 'online';
payloadNotAvailable = 'offline';
availabilityMode: AvailabilityMode = 'all';

distributed = false;
stateLocked = true;

private _instanceStatusTopic: string;
private readonly _entityStatusTopic: string;

protected constructor(
component: string,
Expand All @@ -28,6 +52,30 @@ export abstract class EntityConfig {
this.configTopic = `${this.component}/room-assistant/${id}/config`;
this.stateTopic = `room-assistant/${this.component}/${id}/state`;
this.jsonAttributesTopic = `room-assistant/${this.component}/${id}/attributes`;
this.availabilityTopic = `room-assistant/${this.component}/${id}/status`;
this._entityStatusTopic = `room-assistant/${this.component}/${id}/status`;
}

get availability(): Array<Availability> {
if (this.distributed && !this.stateLocked) {
return [];
} else {
return [
new Availability(this._entityStatusTopic),
new Availability(this._instanceStatusTopic),
];
}
}

setInstanceStatusTopic(topic: string): void {
this._instanceStatusTopic = topic;
}

shallowClone(): Record<string, unknown> {
const clone: Record<string, unknown> = Object.assign({}, this) as Record<
string,
unknown
>;
clone.availability = this.availability;
return clone;
}
}
9 changes: 8 additions & 1 deletion src/integrations/home-assistant/home-assistant.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,20 @@ import { EntitiesModule } from '../../entities/entities.module';
import { StatusModule } from '../../status/status.module';
import { HomeAssistantHealthIndicator } from './home-assistant.health';
import { ClusterModule } from '../../cluster/cluster.module';
import { ScheduleModule } from '@nestjs/schedule';

@Module({})
export default class HomeAssistantModule {
static forRoot(): DynamicModule {
return {
module: HomeAssistantModule,
imports: [ConfigModule, EntitiesModule, ClusterModule, StatusModule],
imports: [
ConfigModule,
EntitiesModule,
ClusterModule,
StatusModule,
ScheduleModule.forRoot(),
],
providers: [HomeAssistantService, HomeAssistantHealthIndicator],
};
}
Expand Down
Loading

0 comments on commit 91b7cc1

Please sign in to comment.