mirror of
https://github.com/adrianjagielak/home-assistant-futurehome.git
synced 2025-09-13 07:37:09 +00:00
Add support for 'door_lock' service
This commit is contained in:
parent
3939b37d88
commit
77096d6732
@ -35,8 +35,6 @@ Some services are more common than others; some are deprecated entirely.
|
|||||||
<!--
|
<!--
|
||||||
todo: pairing
|
todo: pairing
|
||||||
todo add info about factory reset hub to restore 30 day trial
|
todo add info about factory reset hub to restore 30 day trial
|
||||||
todo: links to the .ts service implementations below
|
|
||||||
todo: service names and not just raw service identifiers?
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
| Name | Service | Example device | Implementation status | Home Assistant entity |
|
| Name | Service | Example device | Implementation status | Home Assistant entity |
|
||||||
@ -48,7 +46,7 @@ todo: service names and not just raw service identifiers?
|
|||||||
| Chargepoint | [chargepoint](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/chargepoint.ts) | [Futurehome Charge](https://www.futurehome.io/en_no/shop/charge) | ✅ | [Sensor](https://www.home-assistant.io/integrations/sensor/), [Switch](https://www.home-assistant.io/integrations/switch/), [Number](https://www.home-assistant.io/integrations/number/), [Select](https://www.home-assistant.io/integrations/select/) |
|
| Chargepoint | [chargepoint](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/chargepoint.ts) | [Futurehome Charge](https://www.futurehome.io/en_no/shop/charge) | ✅ | [Sensor](https://www.home-assistant.io/integrations/sensor/), [Switch](https://www.home-assistant.io/integrations/switch/), [Number](https://www.home-assistant.io/integrations/number/), [Select](https://www.home-assistant.io/integrations/select/) |
|
||||||
| Color control | [color_ctrl](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/color_ctrl.ts) | | ✅ | [Light](https://www.home-assistant.io/integrations/light/) |
|
| Color control | [color_ctrl](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/color_ctrl.ts) | | ✅ | [Light](https://www.home-assistant.io/integrations/light/) |
|
||||||
| Complex Alarm System | [complex_alarm_system](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/complex_alarm_system.ts) | | |
|
| Complex Alarm System | [complex_alarm_system](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/complex_alarm_system.ts) | | |
|
||||||
| Door lock| [door_lock](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/door_lock.ts) | | |
|
| Door lock | [door_lock](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/door_lock.ts) | | ✅ | [Lock](https://www.home-assistant.io/integrations/lock/), [Binary sensor](https://www.home-assistant.io/integrations/binary_sensor/), [Switch](https://www.home-assistant.io/integrations/switch/), [Number](https://www.home-assistant.io/integrations/number/), [Button](https://www.home-assistant.io/integrations/button/), [Select](https://www.home-assistant.io/integrations/select/) |
|
||||||
| ??? | [doorman](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/doorman.ts) | | |
|
| ??? | [doorman](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/doorman.ts) | | |
|
||||||
| Fan | [fan_ctrl](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/fan_ctrl.ts) | | ✅ | [Fan](https://www.home-assistant.io/integrations/fan/) |
|
| Fan | [fan_ctrl](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/fan_ctrl.ts) | | ✅ | [Fan](https://www.home-assistant.io/integrations/fan/) |
|
||||||
| Light | [light](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/light.ts) | | |
|
| Light | [light](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/light.ts) | | |
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
- Refactored sensors.
|
- Refactored sensors.
|
||||||
- Added support for 'meter_*' services (electricity meters, gas meters, water meters, heating meters, cooling meters).
|
- Added support for 'meter_*' services (electricity meters, gas meters, water meters, heating meters, cooling meters).
|
||||||
- Added support for 'sound_switch' service (sound emitters).
|
- Added support for 'sound_switch' service (sound emitters).
|
||||||
|
- Added support for 'door_lock' service (door locks).
|
||||||
|
|
||||||
## 0.1.5 (25.07.2025)
|
## 0.1.5 (25.07.2025)
|
||||||
|
|
||||||
|
@ -34,8 +34,6 @@ Some services are more common than others; some are deprecated entirely.
|
|||||||
<!--
|
<!--
|
||||||
todo: pairing
|
todo: pairing
|
||||||
todo add info about factory reset hub to restore 30 day trial
|
todo add info about factory reset hub to restore 30 day trial
|
||||||
todo: links to the .ts service implementations below
|
|
||||||
todo: service names and not just raw service identifiers?
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
| Name | Service | Example device | Implementation status | Home Assistant entity |
|
| Name | Service | Example device | Implementation status | Home Assistant entity |
|
||||||
@ -47,7 +45,7 @@ todo: service names and not just raw service identifiers?
|
|||||||
| Chargepoint | [chargepoint](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/chargepoint.ts) | [Futurehome Charge](https://www.futurehome.io/en_no/shop/charge) | ✅ | [Sensor](https://www.home-assistant.io/integrations/sensor/), [Switch](https://www.home-assistant.io/integrations/switch/), [Number](https://www.home-assistant.io/integrations/number/), [Select](https://www.home-assistant.io/integrations/select/) |
|
| Chargepoint | [chargepoint](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/chargepoint.ts) | [Futurehome Charge](https://www.futurehome.io/en_no/shop/charge) | ✅ | [Sensor](https://www.home-assistant.io/integrations/sensor/), [Switch](https://www.home-assistant.io/integrations/switch/), [Number](https://www.home-assistant.io/integrations/number/), [Select](https://www.home-assistant.io/integrations/select/) |
|
||||||
| Color control | [color_ctrl](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/color_ctrl.ts) | | ✅ | [Light](https://www.home-assistant.io/integrations/light/) |
|
| Color control | [color_ctrl](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/color_ctrl.ts) | | ✅ | [Light](https://www.home-assistant.io/integrations/light/) |
|
||||||
| Complex Alarm System | [complex_alarm_system](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/complex_alarm_system.ts) | | |
|
| Complex Alarm System | [complex_alarm_system](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/complex_alarm_system.ts) | | |
|
||||||
| Door lock| [door_lock](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/door_lock.ts) | | |
|
| Door lock | [door_lock](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/door_lock.ts) | | ✅ | [Lock](https://www.home-assistant.io/integrations/lock/), [Binary sensor](https://www.home-assistant.io/integrations/binary_sensor/), [Switch](https://www.home-assistant.io/integrations/switch/), [Number](https://www.home-assistant.io/integrations/number/), [Button](https://www.home-assistant.io/integrations/button/), [Select](https://www.home-assistant.io/integrations/select/) |
|
||||||
| ??? | [doorman](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/doorman.ts) | | |
|
| ??? | [doorman](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/doorman.ts) | | |
|
||||||
| Fan | [fan_ctrl](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/fan_ctrl.ts) | | ✅ | [Fan](https://www.home-assistant.io/integrations/fan/) |
|
| Fan | [fan_ctrl](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/fan_ctrl.ts) | | ✅ | [Fan](https://www.home-assistant.io/integrations/fan/) |
|
||||||
| Light | [light](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/light.ts) | | |
|
| Light | [light](https://github.com/adrianjagielak/home-assistant-futurehome/blob/master/futurehome/src/services/light.ts) | | |
|
||||||
|
@ -13,6 +13,7 @@ import { basic__components } from '../services/basic';
|
|||||||
import { battery__components } from '../services/battery';
|
import { battery__components } from '../services/battery';
|
||||||
import { chargepoint__components } from '../services/chargepoint';
|
import { chargepoint__components } from '../services/chargepoint';
|
||||||
import { color_ctrl__components } from '../services/color_ctrl';
|
import { color_ctrl__components } from '../services/color_ctrl';
|
||||||
|
import { door_lock__components } from '../services/door_lock';
|
||||||
import { fan_ctrl__components } from '../services/fan_ctrl';
|
import { fan_ctrl__components } from '../services/fan_ctrl';
|
||||||
import { indicator_ctrl__components } from '../services/indicator_ctrl';
|
import { indicator_ctrl__components } from '../services/indicator_ctrl';
|
||||||
import { media_player__components } from '../services/media_player';
|
import { media_player__components } from '../services/media_player';
|
||||||
@ -149,6 +150,7 @@ const serviceHandlers: {
|
|||||||
battery: battery__components,
|
battery: battery__components,
|
||||||
chargepoint: chargepoint__components,
|
chargepoint: chargepoint__components,
|
||||||
color_ctrl: color_ctrl__components,
|
color_ctrl: color_ctrl__components,
|
||||||
|
door_lock: door_lock__components,
|
||||||
fan_ctrl: fan_ctrl__components,
|
fan_ctrl: fan_ctrl__components,
|
||||||
indicator_ctrl: indicator_ctrl__components,
|
indicator_ctrl: indicator_ctrl__components,
|
||||||
media_player: media_player__components,
|
media_player: media_player__components,
|
||||||
|
@ -1947,7 +1947,7 @@
|
|||||||
"client": {
|
"client": {
|
||||||
"name": "Smart Speaker"
|
"name": "Smart Speaker"
|
||||||
},
|
},
|
||||||
"id": 1001,
|
"id": 1003,
|
||||||
"model": "zigbee - Futurehome - Smart Speaker",
|
"model": "zigbee - Futurehome - Smart Speaker",
|
||||||
"modelAlias": "Smart Speaker",
|
"modelAlias": "Smart Speaker",
|
||||||
"type": {
|
"type": {
|
||||||
@ -1968,12 +1968,12 @@
|
|||||||
"services": {
|
"services": {
|
||||||
"media_player": {
|
"media_player": {
|
||||||
"name": "media_player",
|
"name": "media_player",
|
||||||
"addr": "/rt:dev/rn:zigbee/ad:1/sv:media_player/ad:1001_0",
|
"addr": "/rt:dev/rn:zigbee/ad:1/sv:media_player/ad:1003_0",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"props": {
|
"props": {
|
||||||
"sup_playback": ["play", "pause", "next_track", "previous_track"],
|
"sup_playback": ["play", "pause", "next_track", "previous_track"],
|
||||||
"sup_modes": ["repeat", "repeat_one", "shuffle", "crossfade"],
|
"sup_modes": ["repeat", "repeat_one", "shuffle", "crossfade"],
|
||||||
"sup_metadata": ["album", "track", "artist", "image_url"]
|
"sup_metadata": ["album", "track", "artist"]
|
||||||
},
|
},
|
||||||
"intf": [
|
"intf": [
|
||||||
"cmd.playback.set",
|
"cmd.playback.set",
|
||||||
@ -2412,5 +2412,135 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"metadata": null
|
"metadata": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client": {
|
||||||
|
"name": "Door lock"
|
||||||
|
},
|
||||||
|
"fimp": {
|
||||||
|
"adapter": "zwave-ad",
|
||||||
|
"address": "92",
|
||||||
|
"group": "ch_0"
|
||||||
|
},
|
||||||
|
"functionality": "security",
|
||||||
|
"id": 74,
|
||||||
|
"lrn": true,
|
||||||
|
"model": "zw_560_3_1",
|
||||||
|
"modelAlias": "Door lock",
|
||||||
|
"param": {
|
||||||
|
"alarms": {
|
||||||
|
"lock": ["manual_lock", "manual_unlock"]
|
||||||
|
},
|
||||||
|
"autoLock": "on",
|
||||||
|
"batteryLevel": "ok",
|
||||||
|
"batteryPercentage": 100,
|
||||||
|
"lockState": "unlocked",
|
||||||
|
"openState": "open",
|
||||||
|
"presence": false,
|
||||||
|
"supportedAlarms": {
|
||||||
|
"lock": ["rf_not_locked"]
|
||||||
|
},
|
||||||
|
"timestamp": "2020-02-11 15:38:12 +0100",
|
||||||
|
"zwaveConfigParameters": [
|
||||||
|
{
|
||||||
|
"parameter": 1,
|
||||||
|
"size": 1,
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"problem": false,
|
||||||
|
"room": null,
|
||||||
|
"services": {
|
||||||
|
"alarm_lock": {
|
||||||
|
"addr": "/rt:dev/rn:zw/ad:1/sv:alarm_lock/ad:92_0",
|
||||||
|
"enabled": true,
|
||||||
|
"intf": ["cmd.alarm.get_report", "evt.alarm.report"],
|
||||||
|
"props": {
|
||||||
|
"is_secure": true,
|
||||||
|
"is_unsecure": false,
|
||||||
|
"sup_events": ["rf_not_locked"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"basic": {
|
||||||
|
"addr": "/rt:dev/rn:zw/ad:1/sv:basic/ad:92_0",
|
||||||
|
"enabled": true,
|
||||||
|
"intf": ["cmd.lvl.get_report", "cmd.lvl.set", "evt.lvl.report"],
|
||||||
|
"props": {
|
||||||
|
"is_secure": true,
|
||||||
|
"is_unsecure": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"battery": {
|
||||||
|
"addr": "/rt:dev/rn:zw/ad:1/sv:battery/ad:92_0",
|
||||||
|
"enabled": true,
|
||||||
|
"intf": ["cmd.lvl.get_report", "evt.alarm.report", "evt.lvl.report"],
|
||||||
|
"props": {
|
||||||
|
"is_secure": false,
|
||||||
|
"is_unsecure": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dev_sys": {
|
||||||
|
"addr": "/rt:dev/rn:zw/ad:1/sv:dev_sys/ad:92_0",
|
||||||
|
"enabled": true,
|
||||||
|
"intf": [
|
||||||
|
"cmd.config.get_report",
|
||||||
|
"cmd.config.set",
|
||||||
|
"cmd.group.add_members",
|
||||||
|
"cmd.group.delete_members",
|
||||||
|
"cmd.group.get_members",
|
||||||
|
"cmd.ping.send",
|
||||||
|
"evt.config.report",
|
||||||
|
"evt.group.members_report",
|
||||||
|
"evt.ping.report"
|
||||||
|
],
|
||||||
|
"props": {
|
||||||
|
"is_secure": false,
|
||||||
|
"is_unsecure": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"door_lock": {
|
||||||
|
"addr": "/rt:dev/rn:zw/ad:1/sv:door_lock/ad:92_0",
|
||||||
|
"enabled": true,
|
||||||
|
"intf": ["cmd.lock.get_report", "cmd.lock.set", "evt.lock.report"],
|
||||||
|
"props": {
|
||||||
|
"is_secure": true,
|
||||||
|
"is_unsecure": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sensor_presence": {
|
||||||
|
"addr": "/rt:dev/rn:zw/ad:1/sv:sensor_presence/ad:92_0",
|
||||||
|
"enabled": true,
|
||||||
|
"intf": ["cmd.presence.get_report", "evt.presence.report"],
|
||||||
|
"props": {
|
||||||
|
"is_secure": true,
|
||||||
|
"is_unsecure": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user_code": {
|
||||||
|
"addr": "/rt:dev/rn:zw/ad:1/sv:user_code/ad:92_0",
|
||||||
|
"enabled": true,
|
||||||
|
"intf": [
|
||||||
|
"cmd.usercode.clear",
|
||||||
|
"cmd.usercode.clear_all",
|
||||||
|
"cmd.usercode.get",
|
||||||
|
"cmd.usercode.set"
|
||||||
|
],
|
||||||
|
"props": {
|
||||||
|
"is_secure": true,
|
||||||
|
"is_unsecure": false,
|
||||||
|
"sup_users": 52
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"supports": ["clear", "poll"],
|
||||||
|
"thing": 57,
|
||||||
|
"type": {
|
||||||
|
"subtype": null,
|
||||||
|
"supported": {
|
||||||
|
"door_lock": []
|
||||||
|
},
|
||||||
|
"type": null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1245,10 +1245,10 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1001,
|
"id": 1003,
|
||||||
"services": [
|
"services": [
|
||||||
{
|
{
|
||||||
"addr": "/rt:dev/rn:zigbee/ad:1/sv:media_player/ad:1001_0",
|
"addr": "/rt:dev/rn:zigbee/ad:1/sv:media_player/ad:1003_0",
|
||||||
"attributes": [
|
"attributes": [
|
||||||
{
|
{
|
||||||
"name": "playback",
|
"name": "playback",
|
||||||
@ -1772,5 +1772,65 @@
|
|||||||
"name": "sound_switch"
|
"name": "sound_switch"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 74,
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"addr": "/rt:dev/rn:zw/ad:1/sv:door_lock/ad:92_0",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"name": "lock",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"props": {
|
||||||
|
"timeout_s": "254",
|
||||||
|
"unsecured_desc": ""
|
||||||
|
},
|
||||||
|
"ts": "2020-02-11 15:38:12 +0100",
|
||||||
|
"val": {
|
||||||
|
"bolt_is_locked": false,
|
||||||
|
"door_is_closed": false,
|
||||||
|
"is_secured": false,
|
||||||
|
"latch_is_closed": false
|
||||||
|
},
|
||||||
|
"val_t": "bool_map"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "door_lock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"addr": "/rt:dev/rn:zw/ad:1/sv:dev_sys/ad:92_0",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"name": "state",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"ts": "2020-01-29 17:35:54 +0100",
|
||||||
|
"val": "DOWN",
|
||||||
|
"val_t": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "error",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"props": {
|
||||||
|
"msg": "TRANSMIT_COMPLETE_NO_ACK",
|
||||||
|
"src": "nodeId=92_0;service=door_lock"
|
||||||
|
},
|
||||||
|
"ts": "2020-01-23 14:58:07 +0100",
|
||||||
|
"val": "TX_ERROR",
|
||||||
|
"val_t": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "dev_sys"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
539
futurehome/src/services/door_lock.ts
Normal file
539
futurehome/src/services/door_lock.ts
Normal file
@ -0,0 +1,539 @@
|
|||||||
|
import { sendFimpMsg } from '../fimp/fimp';
|
||||||
|
import {
|
||||||
|
VinculumPd7Device,
|
||||||
|
VinculumPd7Service,
|
||||||
|
} from '../fimp/vinculum_pd7_device';
|
||||||
|
import { HaMqttComponent } from '../ha/mqtt_components/_component';
|
||||||
|
import { BinarySensorDeviceClass } from '../ha/mqtt_components/_enums';
|
||||||
|
import {
|
||||||
|
CommandHandlers,
|
||||||
|
ServiceComponentsCreationResult,
|
||||||
|
} from '../ha/publish_device';
|
||||||
|
|
||||||
|
export function door_lock__components(
|
||||||
|
topicPrefix: string,
|
||||||
|
device: VinculumPd7Device,
|
||||||
|
svc: VinculumPd7Service,
|
||||||
|
_svcName: string,
|
||||||
|
): ServiceComponentsCreationResult | undefined {
|
||||||
|
const components: Record<string, HaMqttComponent> = {};
|
||||||
|
const commandHandlers: CommandHandlers = {};
|
||||||
|
|
||||||
|
const stateTopic = `${topicPrefix}/state`;
|
||||||
|
|
||||||
|
// Main lock component
|
||||||
|
if (
|
||||||
|
svc.intf?.includes('cmd.lock.set') &&
|
||||||
|
svc.intf?.includes('evt.lock.report')
|
||||||
|
) {
|
||||||
|
const lockCommandTopic = `${topicPrefix}${svc.addr}/lock/command`;
|
||||||
|
|
||||||
|
components[`${svc.addr}_lock`] = {
|
||||||
|
unique_id: `${svc.addr}_lock`,
|
||||||
|
platform: 'lock',
|
||||||
|
name: 'Lock',
|
||||||
|
command_topic: lockCommandTopic,
|
||||||
|
state_topic: stateTopic,
|
||||||
|
optimistic: false,
|
||||||
|
// Map the lock state based on is_secured component
|
||||||
|
value_template: `{{ 'LOCKED' if value_json['${svc.addr}'].lock.is_secured else 'UNLOCKED' }}`,
|
||||||
|
payload_lock: 'LOCK',
|
||||||
|
payload_unlock: 'UNLOCK',
|
||||||
|
};
|
||||||
|
|
||||||
|
commandHandlers[lockCommandTopic] = async (payload: string) => {
|
||||||
|
const isLock = payload === 'LOCK';
|
||||||
|
|
||||||
|
await sendFimpMsg({
|
||||||
|
address: svc.addr,
|
||||||
|
service: 'door_lock',
|
||||||
|
cmd: 'cmd.lock.set',
|
||||||
|
val_t: 'bool',
|
||||||
|
val: isLock,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Individual lock components as binary sensors
|
||||||
|
const supComponents = svc.props?.sup_components || [];
|
||||||
|
|
||||||
|
for (const component of supComponents) {
|
||||||
|
let deviceClass: BinarySensorDeviceClass;
|
||||||
|
let name: string;
|
||||||
|
|
||||||
|
switch (component) {
|
||||||
|
case 'is_secured':
|
||||||
|
deviceClass = 'lock';
|
||||||
|
name = 'Secured';
|
||||||
|
break;
|
||||||
|
case 'door_is_closed':
|
||||||
|
deviceClass = 'door';
|
||||||
|
name = 'Door Closed';
|
||||||
|
break;
|
||||||
|
case 'bolt_is_locked':
|
||||||
|
deviceClass = 'lock';
|
||||||
|
name = 'Bolt Locked';
|
||||||
|
break;
|
||||||
|
case 'latch_is_closed':
|
||||||
|
deviceClass = 'lock';
|
||||||
|
name = 'Latch Closed';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
deviceClass = 'lock';
|
||||||
|
name = component
|
||||||
|
.replace(/_/g, ' ')
|
||||||
|
.replace(/\b\w/g, (l: string) => l.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
components[`${svc.addr}_${component}`] = {
|
||||||
|
unique_id: `${svc.addr}_${component}`,
|
||||||
|
platform: 'binary_sensor',
|
||||||
|
name: name,
|
||||||
|
device_class: deviceClass,
|
||||||
|
entity_category: 'diagnostic',
|
||||||
|
state_topic: stateTopic,
|
||||||
|
value_template: `{{ 'ON' if value_json['${svc.addr}'].lock.${component} else 'OFF' }}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-lock switch
|
||||||
|
if (
|
||||||
|
svc.intf?.includes('cmd.auto_lock.set') &&
|
||||||
|
svc.intf?.includes('evt.auto_lock.report')
|
||||||
|
) {
|
||||||
|
const autoLockCommandTopic = `${topicPrefix}${svc.addr}/auto_lock/command`;
|
||||||
|
|
||||||
|
components[`${svc.addr}_auto_lock`] = {
|
||||||
|
unique_id: `${svc.addr}_auto_lock`,
|
||||||
|
platform: 'switch',
|
||||||
|
name: 'Auto Lock',
|
||||||
|
entity_category: 'config',
|
||||||
|
command_topic: autoLockCommandTopic,
|
||||||
|
state_topic: stateTopic,
|
||||||
|
optimistic: false,
|
||||||
|
value_template: `{{ 'ON' if value_json['${svc.addr}'].auto_lock else 'OFF' }}`,
|
||||||
|
payload_on: 'ON',
|
||||||
|
payload_off: 'OFF',
|
||||||
|
};
|
||||||
|
|
||||||
|
commandHandlers[autoLockCommandTopic] = async (payload: string) => {
|
||||||
|
const enabled = payload === 'ON';
|
||||||
|
|
||||||
|
await sendFimpMsg({
|
||||||
|
address: svc.addr,
|
||||||
|
service: 'door_lock',
|
||||||
|
cmd: 'cmd.auto_lock.set',
|
||||||
|
val_t: 'bool',
|
||||||
|
val: enabled,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Volume control
|
||||||
|
if (
|
||||||
|
svc.intf?.includes('cmd.volume.set') &&
|
||||||
|
svc.intf?.includes('evt.volume.report')
|
||||||
|
) {
|
||||||
|
const volumeCommandTopic = `${topicPrefix}${svc.addr}/volume/command`;
|
||||||
|
const minVolume = svc.props?.min_volume ?? 0;
|
||||||
|
const maxVolume = svc.props?.max_volume ?? 10;
|
||||||
|
|
||||||
|
components[`${svc.addr}_volume`] = {
|
||||||
|
unique_id: `${svc.addr}_volume`,
|
||||||
|
platform: 'number',
|
||||||
|
name: 'Volume',
|
||||||
|
entity_category: 'config',
|
||||||
|
command_topic: volumeCommandTopic,
|
||||||
|
state_topic: stateTopic,
|
||||||
|
optimistic: false,
|
||||||
|
min: minVolume,
|
||||||
|
max: maxVolume,
|
||||||
|
step: 1,
|
||||||
|
value_template: `{{ value_json['${svc.addr}'].volume | default(${minVolume}) }}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
commandHandlers[volumeCommandTopic] = async (payload: string) => {
|
||||||
|
const volume = parseInt(payload, 10);
|
||||||
|
if (Number.isNaN(volume) || volume < minVolume || volume > maxVolume) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sendFimpMsg({
|
||||||
|
address: svc.addr,
|
||||||
|
service: 'door_lock',
|
||||||
|
cmd: 'cmd.volume.set',
|
||||||
|
val_t: 'int',
|
||||||
|
val: volume,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock configuration button
|
||||||
|
if (svc.intf?.includes('cmd.lock.get_configuration')) {
|
||||||
|
const getConfigCommandTopic = `${topicPrefix}${svc.addr}/get_configuration/command`;
|
||||||
|
|
||||||
|
components[`${svc.addr}_get_config`] = {
|
||||||
|
unique_id: `${svc.addr}_get_config`,
|
||||||
|
platform: 'button',
|
||||||
|
name: 'Get Configuration',
|
||||||
|
entity_category: 'diagnostic',
|
||||||
|
command_topic: getConfigCommandTopic,
|
||||||
|
};
|
||||||
|
|
||||||
|
commandHandlers[getConfigCommandTopic] = async (_payload: string) => {
|
||||||
|
await sendFimpMsg({
|
||||||
|
address: svc.addr,
|
||||||
|
service: 'door_lock',
|
||||||
|
cmd: 'cmd.lock.get_configuration',
|
||||||
|
val_t: 'null',
|
||||||
|
val: null,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get lock report button
|
||||||
|
if (svc.intf?.includes('cmd.lock.get_report')) {
|
||||||
|
const getLockReportCommandTopic = `${topicPrefix}${svc.addr}/get_lock_report/command`;
|
||||||
|
|
||||||
|
components[`${svc.addr}_get_lock_report`] = {
|
||||||
|
unique_id: `${svc.addr}_get_lock_report`,
|
||||||
|
platform: 'button',
|
||||||
|
name: 'Get Lock Report',
|
||||||
|
entity_category: 'diagnostic',
|
||||||
|
command_topic: getLockReportCommandTopic,
|
||||||
|
};
|
||||||
|
|
||||||
|
commandHandlers[getLockReportCommandTopic] = async (_payload: string) => {
|
||||||
|
await sendFimpMsg({
|
||||||
|
address: svc.addr,
|
||||||
|
service: 'door_lock',
|
||||||
|
cmd: 'cmd.lock.get_report',
|
||||||
|
val_t: 'null',
|
||||||
|
val: null,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get auto-lock report button
|
||||||
|
if (svc.intf?.includes('cmd.auto_lock.get_report')) {
|
||||||
|
const getAutoLockReportCommandTopic = `${topicPrefix}${svc.addr}/get_auto_lock_report/command`;
|
||||||
|
|
||||||
|
components[`${svc.addr}_get_auto_lock_report`] = {
|
||||||
|
unique_id: `${svc.addr}_get_auto_lock_report`,
|
||||||
|
platform: 'button',
|
||||||
|
name: 'Get Auto Lock Report',
|
||||||
|
entity_category: 'diagnostic',
|
||||||
|
command_topic: getAutoLockReportCommandTopic,
|
||||||
|
};
|
||||||
|
|
||||||
|
commandHandlers[getAutoLockReportCommandTopic] = async (
|
||||||
|
_payload: string,
|
||||||
|
) => {
|
||||||
|
await sendFimpMsg({
|
||||||
|
address: svc.addr,
|
||||||
|
service: 'door_lock',
|
||||||
|
cmd: 'cmd.auto_lock.get_report',
|
||||||
|
val_t: 'null',
|
||||||
|
val: null,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get volume report button
|
||||||
|
if (svc.intf?.includes('cmd.volume.get_report')) {
|
||||||
|
const getVolumeReportCommandTopic = `${topicPrefix}${svc.addr}/get_volume_report/command`;
|
||||||
|
|
||||||
|
components[`${svc.addr}_get_volume_report`] = {
|
||||||
|
unique_id: `${svc.addr}_get_volume_report`,
|
||||||
|
platform: 'button',
|
||||||
|
name: 'Get Volume Report',
|
||||||
|
entity_category: 'diagnostic',
|
||||||
|
command_topic: getVolumeReportCommandTopic,
|
||||||
|
};
|
||||||
|
|
||||||
|
commandHandlers[getVolumeReportCommandTopic] = async (_payload: string) => {
|
||||||
|
await sendFimpMsg({
|
||||||
|
address: svc.addr,
|
||||||
|
service: 'door_lock',
|
||||||
|
cmd: 'cmd.volume.get_report',
|
||||||
|
val_t: 'null',
|
||||||
|
val: null,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operation type configuration
|
||||||
|
const supOpTypes = svc.props?.sup_op_types || [];
|
||||||
|
if (
|
||||||
|
supOpTypes.length > 0 &&
|
||||||
|
svc.intf?.includes('cmd.lock.set_configuration')
|
||||||
|
) {
|
||||||
|
const opTypeCommandTopic = `${topicPrefix}${svc.addr}/operation_type/command`;
|
||||||
|
|
||||||
|
components[`${svc.addr}_operation_type`] = {
|
||||||
|
unique_id: `${svc.addr}_operation_type`,
|
||||||
|
platform: 'select',
|
||||||
|
name: 'Operation Type',
|
||||||
|
entity_category: 'config',
|
||||||
|
command_topic: opTypeCommandTopic,
|
||||||
|
state_topic: stateTopic,
|
||||||
|
optimistic: false,
|
||||||
|
options: supOpTypes,
|
||||||
|
value_template: `{{ value_json['${svc.addr}'].configuration.operation_type | default('${supOpTypes[0]}') }}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
commandHandlers[opTypeCommandTopic] = async (payload: string) => {
|
||||||
|
if (!supOpTypes.includes(payload)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a partial configuration update
|
||||||
|
await sendFimpMsg({
|
||||||
|
address: svc.addr,
|
||||||
|
service: 'door_lock',
|
||||||
|
cmd: 'cmd.lock.set_configuration',
|
||||||
|
val_t: 'object',
|
||||||
|
val: {
|
||||||
|
operation_type: payload,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-relock time configuration (if supported)
|
||||||
|
if (svc.props?.supports_auto_relock) {
|
||||||
|
const minAutoRelock = svc.props?.min_auto_relock_time ?? 0;
|
||||||
|
const maxAutoRelock = svc.props?.max_auto_relock_time ?? 65535;
|
||||||
|
const autoRelockTimeCommandTopic = `${topicPrefix}${svc.addr}/auto_relock_time/command`;
|
||||||
|
|
||||||
|
components[`${svc.addr}_auto_relock_time`] = {
|
||||||
|
unique_id: `${svc.addr}_auto_relock_time`,
|
||||||
|
platform: 'number',
|
||||||
|
name: 'Auto Relock Time',
|
||||||
|
entity_category: 'config',
|
||||||
|
command_topic: autoRelockTimeCommandTopic,
|
||||||
|
state_topic: stateTopic,
|
||||||
|
optimistic: false,
|
||||||
|
min: minAutoRelock,
|
||||||
|
max: maxAutoRelock,
|
||||||
|
step: 1,
|
||||||
|
unit_of_measurement: 's',
|
||||||
|
value_template: `{{ value_json['${svc.addr}'].configuration.auto_relock_time | default(0) }}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
commandHandlers[autoRelockTimeCommandTopic] = async (payload: string) => {
|
||||||
|
const time = parseInt(payload, 10);
|
||||||
|
if (Number.isNaN(time) || time < minAutoRelock || time > maxAutoRelock) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sendFimpMsg({
|
||||||
|
address: svc.addr,
|
||||||
|
service: 'door_lock',
|
||||||
|
cmd: 'cmd.lock.set_configuration',
|
||||||
|
val_t: 'object',
|
||||||
|
val: {
|
||||||
|
auto_relock_time: time,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hold and release time configuration (if supported)
|
||||||
|
if (svc.props?.supports_hold_and_release) {
|
||||||
|
const minHoldRelease = svc.props?.min_hold_and_release_time ?? 1;
|
||||||
|
const maxHoldRelease = svc.props?.max_hold_and_release_time ?? 65535;
|
||||||
|
const holdReleaseTimeCommandTopic = `${topicPrefix}${svc.addr}/hold_release_time/command`;
|
||||||
|
|
||||||
|
components[`${svc.addr}_hold_release_time`] = {
|
||||||
|
unique_id: `${svc.addr}_hold_release_time`,
|
||||||
|
platform: 'number',
|
||||||
|
name: 'Hold & Release Time',
|
||||||
|
entity_category: 'config',
|
||||||
|
command_topic: holdReleaseTimeCommandTopic,
|
||||||
|
state_topic: stateTopic,
|
||||||
|
optimistic: false,
|
||||||
|
min: minHoldRelease,
|
||||||
|
max: maxHoldRelease,
|
||||||
|
step: 1,
|
||||||
|
unit_of_measurement: 's',
|
||||||
|
value_template: `{{ value_json['${svc.addr}'].configuration.hold_and_release_time | default(0) }}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
commandHandlers[holdReleaseTimeCommandTopic] = async (payload: string) => {
|
||||||
|
const time = parseInt(payload, 10);
|
||||||
|
if (
|
||||||
|
Number.isNaN(time) ||
|
||||||
|
time < minHoldRelease ||
|
||||||
|
time > maxHoldRelease
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sendFimpMsg({
|
||||||
|
address: svc.addr,
|
||||||
|
service: 'door_lock',
|
||||||
|
cmd: 'cmd.lock.set_configuration',
|
||||||
|
val_t: 'object',
|
||||||
|
val: {
|
||||||
|
hold_and_release_time: time,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block to block configuration (if supported)
|
||||||
|
if (svc.props?.supports_block_to_block) {
|
||||||
|
const blockToBlockCommandTopic = `${topicPrefix}${svc.addr}/block_to_block/command`;
|
||||||
|
|
||||||
|
components[`${svc.addr}_block_to_block`] = {
|
||||||
|
unique_id: `${svc.addr}_block_to_block`,
|
||||||
|
platform: 'switch',
|
||||||
|
name: 'Block to Block',
|
||||||
|
entity_category: 'config',
|
||||||
|
command_topic: blockToBlockCommandTopic,
|
||||||
|
state_topic: stateTopic,
|
||||||
|
optimistic: false,
|
||||||
|
value_template: `{{ 'ON' if value_json['${svc.addr}'].configuration.block_to_block else 'OFF' }}`,
|
||||||
|
payload_on: 'ON',
|
||||||
|
payload_off: 'OFF',
|
||||||
|
};
|
||||||
|
|
||||||
|
commandHandlers[blockToBlockCommandTopic] = async (payload: string) => {
|
||||||
|
const enabled = payload === 'ON';
|
||||||
|
|
||||||
|
await sendFimpMsg({
|
||||||
|
address: svc.addr,
|
||||||
|
service: 'door_lock',
|
||||||
|
cmd: 'cmd.lock.set_configuration',
|
||||||
|
val_t: 'object',
|
||||||
|
val: {
|
||||||
|
block_to_block: enabled,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Twist assist configuration (if supported)
|
||||||
|
if (svc.props?.supports_twist_assist) {
|
||||||
|
const twistAssistCommandTopic = `${topicPrefix}${svc.addr}/twist_assist/command`;
|
||||||
|
|
||||||
|
components[`${svc.addr}_twist_assist`] = {
|
||||||
|
unique_id: `${svc.addr}_twist_assist`,
|
||||||
|
platform: 'switch',
|
||||||
|
name: 'Twist Assist',
|
||||||
|
entity_category: 'config',
|
||||||
|
command_topic: twistAssistCommandTopic,
|
||||||
|
state_topic: stateTopic,
|
||||||
|
optimistic: false,
|
||||||
|
value_template: `{{ 'ON' if value_json['${svc.addr}'].configuration.twist_assist else 'OFF' }}`,
|
||||||
|
payload_on: 'ON',
|
||||||
|
payload_off: 'OFF',
|
||||||
|
};
|
||||||
|
|
||||||
|
commandHandlers[twistAssistCommandTopic] = async (payload: string) => {
|
||||||
|
const enabled = payload === 'ON';
|
||||||
|
|
||||||
|
await sendFimpMsg({
|
||||||
|
address: svc.addr,
|
||||||
|
service: 'door_lock',
|
||||||
|
cmd: 'cmd.lock.set_configuration',
|
||||||
|
val_t: 'object',
|
||||||
|
val: {
|
||||||
|
twist_assist: enabled,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock timeout configuration (if timed operation is supported)
|
||||||
|
if (supOpTypes.includes('timed')) {
|
||||||
|
const minTimeoutSeconds = svc.props?.min_lock_timeout_seconds ?? 0;
|
||||||
|
const maxTimeoutSeconds = svc.props?.max_lock_timeout_seconds ?? 59;
|
||||||
|
const minTimeoutMinutes = svc.props?.min_lock_timeout_minutes ?? 0;
|
||||||
|
const maxTimeoutMinutes = svc.props?.max_lock_timeout_minutes ?? 253;
|
||||||
|
|
||||||
|
// Lock timeout seconds
|
||||||
|
const timeoutSecondsCommandTopic = `${topicPrefix}${svc.addr}/lock_timeout_seconds/command`;
|
||||||
|
|
||||||
|
components[`${svc.addr}_lock_timeout_seconds`] = {
|
||||||
|
unique_id: `${svc.addr}_lock_timeout_seconds`,
|
||||||
|
platform: 'number',
|
||||||
|
name: 'Lock Timeout Seconds',
|
||||||
|
entity_category: 'config',
|
||||||
|
command_topic: timeoutSecondsCommandTopic,
|
||||||
|
state_topic: stateTopic,
|
||||||
|
optimistic: false,
|
||||||
|
min: minTimeoutSeconds,
|
||||||
|
max: maxTimeoutSeconds,
|
||||||
|
step: 1,
|
||||||
|
unit_of_measurement: 's',
|
||||||
|
value_template: `{{ value_json['${svc.addr}'].configuration.lock_timeout_seconds | default(0) }}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
commandHandlers[timeoutSecondsCommandTopic] = async (payload: string) => {
|
||||||
|
const seconds = parseInt(payload, 10);
|
||||||
|
if (
|
||||||
|
Number.isNaN(seconds) ||
|
||||||
|
seconds < minTimeoutSeconds ||
|
||||||
|
seconds > maxTimeoutSeconds
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sendFimpMsg({
|
||||||
|
address: svc.addr,
|
||||||
|
service: 'door_lock',
|
||||||
|
cmd: 'cmd.lock.set_configuration',
|
||||||
|
val_t: 'object',
|
||||||
|
val: {
|
||||||
|
lock_timeout_seconds: seconds,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Lock timeout minutes
|
||||||
|
const timeoutMinutesCommandTopic = `${topicPrefix}${svc.addr}/lock_timeout_minutes/command`;
|
||||||
|
|
||||||
|
components[`${svc.addr}_lock_timeout_minutes`] = {
|
||||||
|
unique_id: `${svc.addr}_lock_timeout_minutes`,
|
||||||
|
platform: 'number',
|
||||||
|
name: 'Lock Timeout Minutes',
|
||||||
|
entity_category: 'config',
|
||||||
|
command_topic: timeoutMinutesCommandTopic,
|
||||||
|
state_topic: stateTopic,
|
||||||
|
optimistic: false,
|
||||||
|
min: minTimeoutMinutes,
|
||||||
|
max: maxTimeoutMinutes,
|
||||||
|
step: 1,
|
||||||
|
unit_of_measurement: 'min',
|
||||||
|
value_template: `{{ value_json['${svc.addr}'].configuration.lock_timeout_minutes | default(0) }}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
commandHandlers[timeoutMinutesCommandTopic] = async (payload: string) => {
|
||||||
|
const minutes = parseInt(payload, 10);
|
||||||
|
if (
|
||||||
|
Number.isNaN(minutes) ||
|
||||||
|
minutes < minTimeoutMinutes ||
|
||||||
|
minutes > maxTimeoutMinutes
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sendFimpMsg({
|
||||||
|
address: svc.addr,
|
||||||
|
service: 'door_lock',
|
||||||
|
cmd: 'cmd.lock.set_configuration',
|
||||||
|
val_t: 'object',
|
||||||
|
val: {
|
||||||
|
lock_timeout_minutes: minutes,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
components,
|
||||||
|
commandHandlers,
|
||||||
|
};
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user