12
12
-- See the License for the specific language governing permissions and
13
13
-- limitations under the License.
14
14
15
- -- ZCL
15
+ local battery_defaults = require " st.zigbee.defaults.battery_defaults"
16
+ local capabilities = require " st.capabilities"
16
17
local zcl_clusters = require " st.zigbee.zcl.clusters"
17
- local TemperatureMeasurement = zcl_clusters .TemperatureMeasurement
18
+ local device_management = require " st.zigbee.device_management"
19
+
20
+ local IASZone = zcl_clusters .IASZone
21
+ local IlluminanceMeasurement = zcl_clusters .IlluminanceMeasurement
18
22
local OccupancySensing = zcl_clusters .OccupancySensing
19
23
local PowerConfiguration = zcl_clusters .PowerConfiguration
20
- local battery_defaults = require " st.zigbee.defaults.battery_defaults "
24
+ local TemperatureMeasurement = zcl_clusters . TemperatureMeasurement
21
25
22
- local capabilities = require " st.capabilities"
26
+ local BATTERY_MIN_VOLTAGE = 2.3
27
+ local BATTERY_MAX_VOLTAGE = 3.0
28
+ local DEFAULT_OCCUPIED_TO_UNOCCUPIED_DELAY = 240
29
+ local DEFAULT_UNOCCUPIED_TO_OCCUPIED_DELAY = 0
30
+ local DEFAULT_UNOCCUPIED_TO_OCCUPIED_THRESHOLD = 0
23
31
24
- local FRIENT_TEMP_CONFIG = {
25
- minimum_interval = 30 ,
26
- maximum_interval = 300 ,
27
- reportable_change = 100 ,
28
- endpoint = 0x26
29
- }
32
+ local OCCUPANCY_ENDPOINT = 0x22
33
+ local TAMPER_ENDPOINT = 0x23
34
+ local POWER_CONFIGURATION_ENDPOINT = 0x23
35
+ local TEMPERATURE_ENDPOINT = 0x26
36
+ local ILLUMINANCE_ENDPOINT = 0x27
30
37
31
- local FRIENT_BATTERY_CONFIG = {
32
- minimum_interval = 30 ,
33
- maximum_interval = 21600 ,
34
- reportable_change = 1 ,
35
- endpoint = 0x23
38
+ local FRIENT_DEVICE_FINGERPRINTS = {
39
+ { mfr = " frient A/S" , model = " MOSZB-140" },
40
+ { mfr = " frient A/S" , model = " MOSZB-141" }
36
41
}
37
42
43
+ local function can_handle_frient_motion_sensor (opts , driver , device )
44
+ for _ , fingerprint in ipairs (FRIENT_DEVICE_FINGERPRINTS ) do
45
+ if device :get_manufacturer () == fingerprint .mfr and device :get_model () == fingerprint .model then
46
+ return true
47
+ end
48
+ end
49
+ return false
50
+ end
51
+
38
52
local function occupancy_attr_handler (driver , device , occupancy , zb_rx )
39
- device :emit_event (
40
- occupancy .value == 1 and capabilities .motionSensor .motion .active () or capabilities .motionSensor .motion .inactive ()
41
- )
53
+ device :emit_event (occupancy .value == 0x01 and capabilities .motionSensor .motion .active () or capabilities .motionSensor .motion .inactive ())
54
+ end
55
+
56
+ local function generate_event_from_zone_status (driver , device , zone_status , zb_rx )
57
+ device :emit_event (zone_status :is_tamper_set () and capabilities .tamperAlert .tamper .detected () or capabilities .tamperAlert .tamper .clear ())
58
+ end
59
+
60
+ local function ias_zone_status_attr_handler (driver , device , attr_val , zb_rx )
61
+ generate_event_from_zone_status (driver , device , attr_val , zb_rx )
62
+ end
63
+
64
+ local function ias_zone_status_change_handler (driver , device , zb_rx )
65
+ generate_event_from_zone_status (driver , device , zb_rx .body .zcl_body .zone_status , zb_rx )
66
+ end
67
+
68
+
69
+ local CONFIGURATIONS = {
70
+ [OCCUPANCY_ENDPOINT ] = {
71
+ cluster = OccupancySensing .ID ,
72
+ attribute = OccupancySensing .attributes .Occupancy .ID ,
73
+ data_type = OccupancySensing .attributes .Occupancy .base_type ,
74
+ minimum_interval = 0 ,
75
+ maximum_interval = 3600 ,
76
+ endpoint = OCCUPANCY_ENDPOINT
77
+ },
78
+ [TAMPER_ENDPOINT ] = {
79
+ cluster = IASZone .ID ,
80
+ attribute = IASZone .attributes .ZoneStatus .ID ,
81
+ minimum_interval = 30 ,
82
+ maximum_interval = 300 ,
83
+ data_type = IASZone .attributes .ZoneStatus .base_type ,
84
+ reportable_change = 1 ,
85
+ endpoint = TAMPER_ENDPOINT
86
+ },
87
+ [TEMPERATURE_ENDPOINT ] = {
88
+ cluster = TemperatureMeasurement .ID ,
89
+ attribute = TemperatureMeasurement .attributes .MeasuredValue .ID ,
90
+ minimum_interval = 30 ,
91
+ maximum_interval = 3600 ,
92
+ data_type = TemperatureMeasurement .attributes .MeasuredValue .base_type ,
93
+ reportable_change = 10 ,
94
+ endpoint = TEMPERATURE_ENDPOINT
95
+ },
96
+ [ILLUMINANCE_ENDPOINT ] = {
97
+ cluster = IlluminanceMeasurement .ID ,
98
+ attribute = IlluminanceMeasurement .attributes .MeasuredValue .ID ,
99
+ data_type = IlluminanceMeasurement .attributes .MeasuredValue .base_type ,
100
+ minimum_interval = 10 ,
101
+ maximum_interval = 3600 ,
102
+ reportable_change = 0x2711 ,
103
+ endpoint = ILLUMINANCE_ENDPOINT
104
+ }
105
+ }
106
+
107
+ local function device_init (driver , device )
108
+ battery_defaults .build_linear_voltage_init (BATTERY_MIN_VOLTAGE , BATTERY_MAX_VOLTAGE )(driver , device )
109
+
110
+ local attribute
111
+ attribute = CONFIGURATIONS [OCCUPANCY_ENDPOINT ]
112
+ -- binding is directly triggered for specific endpoint in do_configure
113
+ device :add_monitored_attribute (attribute )
114
+
115
+ if device :supports_capability_by_id (capabilities .temperatureMeasurement .ID ) then
116
+ attribute = CONFIGURATIONS [TEMPERATURE_ENDPOINT ]
117
+ device :add_configured_attribute (attribute )
118
+ device :add_monitored_attribute (attribute )
119
+ end
120
+ if device :supports_capability_by_id (capabilities .illuminanceMeasurement .ID ) then
121
+ attribute = CONFIGURATIONS [ILLUMINANCE_ENDPOINT ]
122
+ device :add_configured_attribute (attribute )
123
+ device :add_monitored_attribute (attribute )
124
+ end
125
+ if device :supports_capability_by_id (capabilities .tamperAlert .ID ) then
126
+ attribute = CONFIGURATIONS [TAMPER_ENDPOINT ]
127
+ device :add_configured_attribute (attribute )
128
+ device :add_monitored_attribute (attribute )
129
+ end
42
130
end
43
131
132
+ local function device_added (driver , device )
133
+ device :emit_event (capabilities .motionSensor .motion .inactive ())
134
+ if device :supports_capability_by_id (capabilities .tamperAlert .ID ) then
135
+ device :emit_event (capabilities .tamperAlert .tamper .clear ())
136
+ end
137
+ end
138
+
139
+ local function do_refresh (driver , device )
140
+ device :send (OccupancySensing .attributes .Occupancy :read (device ):to_endpoint (OCCUPANCY_ENDPOINT ))
141
+ device :send (PowerConfiguration .attributes .BatteryVoltage :read (device ):to_endpoint (POWER_CONFIGURATION_ENDPOINT ))
142
+
143
+ if device :supports_capability_by_id (capabilities .temperatureMeasurement .ID ) then
144
+ device :send (TemperatureMeasurement .attributes .MeasuredValue :read (device ):to_endpoint (TEMPERATURE_ENDPOINT ))
145
+ end
146
+ if device :supports_capability_by_id (capabilities .illuminanceMeasurement .ID ) then
147
+ device :send (IlluminanceMeasurement .attributes .MeasuredValue :read (device ):to_endpoint (ILLUMINANCE_ENDPOINT ))
148
+ end
149
+ if device :supports_capability_by_id (capabilities .tamperAlert .ID ) then
150
+ device :send (IASZone .attributes .ZoneStatus :read (device ):to_endpoint (TAMPER_ENDPOINT ))
151
+ end
152
+ end
153
+
154
+
44
155
local function do_configure (driver , device )
45
156
device :configure ()
46
- device :send (PowerConfiguration .attributes .BatteryVoltage :configure_reporting (
47
- device ,
48
- FRIENT_BATTERY_CONFIG .minimum_interval ,
49
- FRIENT_BATTERY_CONFIG .maximum_interval ,
50
- FRIENT_BATTERY_CONFIG .reportable_change
51
- ):to_endpoint (FRIENT_BATTERY_CONFIG .endpoint ))
52
- device :send (TemperatureMeasurement .attributes .MeasuredValue :configure_reporting (
53
- device ,
54
- FRIENT_TEMP_CONFIG .minimum_interval ,
55
- FRIENT_TEMP_CONFIG .maximum_interval ,
56
- FRIENT_TEMP_CONFIG .reportable_change
57
- ):to_endpoint (FRIENT_TEMP_CONFIG .endpoint ))
157
+ device :send (device_management .build_bind_request (
158
+ device ,
159
+ zcl_clusters .OccupancySensing .ID ,
160
+ driver .environment_info .hub_zigbee_eui ,
161
+ OCCUPANCY_ENDPOINT
162
+ ))
163
+
164
+ device :send (OccupancySensing .attributes .PIROccupiedToUnoccupiedDelay :write (device , tonumber (DEFAULT_OCCUPIED_TO_UNOCCUPIED_DELAY )):to_endpoint (OCCUPANCY_ENDPOINT ))
165
+ device :send (OccupancySensing .attributes .PIRUnoccupiedToOccupiedDelay :write (device , tonumber (DEFAULT_UNOCCUPIED_TO_OCCUPIED_DELAY )):to_endpoint (OCCUPANCY_ENDPOINT ))
166
+ device :send (OccupancySensing .attributes .PIRUnoccupiedToOccupiedThreshold :write (device , tonumber (DEFAULT_UNOCCUPIED_TO_OCCUPIED_THRESHOLD )):to_endpoint (OCCUPANCY_ENDPOINT ))
167
+ device :send (OccupancySensing .attributes .Occupancy :configure_reporting (device , 0 , 3600 ):to_endpoint (OCCUPANCY_ENDPOINT ))
168
+
169
+ device .thread :call_with_delay (5 , function ()
170
+ do_refresh (driver , device )
171
+ end )
58
172
end
59
173
60
- local function added_handler (driver , device )
61
- device :refresh ()
174
+ local function info_changed (driver , device , event , args )
175
+ for name , value in pairs (device .preferences ) do
176
+ if (device .preferences [name ] ~= nil and args .old_st_store .preferences [name ] ~= device .preferences [name ]) then
177
+ if (name == " temperatureSensitivity" ) then
178
+ local input = device .preferences .temperatureSensitivity
179
+ local temperatureSensitivity = math.floor (input * 100 + 0.5 )
180
+ device :send (TemperatureMeasurement .attributes .MeasuredValue :configure_reporting (device , 30 , 3600 , temperatureSensitivity ):to_endpoint (TEMPERATURE_ENDPOINT ))
181
+ elseif (name == " occupiedToUnoccupiedD" ) then
182
+ local occupiedToUnoccupiedDelay = device .preferences .occupiedToUnoccupiedD or DEFAULT_OCCUPIED_TO_UNOCCUPIED_DELAY
183
+ device :send (OccupancySensing .attributes .PIROccupiedToUnoccupiedDelay :write (device , occupiedToUnoccupiedDelay ):to_endpoint (OCCUPANCY_ENDPOINT ))
184
+ elseif (name == " unoccupiedToOccupiedD" ) then
185
+ local occupiedToUnoccupiedD = device .preferences .unoccupiedToOccupiedD or DEFAULT_UNOCCUPIED_TO_OCCUPIED_DELAY
186
+ device :send (OccupancySensing .attributes .PIRUnoccupiedToOccupiedDelay :write (device , occupiedToUnoccupiedD ):to_endpoint (OCCUPANCY_ENDPOINT ))
187
+ elseif (name == " unoccupiedToOccupiedT" ) then
188
+ local unoccupiedToOccupiedThreshold = device .preferences .unoccupiedToOccupiedT or DEFAULT_UNOCCUPIED_TO_OCCUPIED_THRESHOLD
189
+ device :send (OccupancySensing .attributes .PIRUnoccupiedToOccupiedThreshold :write (device ,unoccupiedToOccupiedThreshold ):to_endpoint (OCCUPANCY_ENDPOINT ))
190
+ end
191
+ end
192
+ end
62
193
end
63
194
64
- local frient_driver = {
65
- NAME = " Frient Sensor" ,
195
+ local frient_motion_driver = {
196
+ NAME = " frient motion driver" ,
197
+ lifecycle_handlers = {
198
+ added = device_added ,
199
+ doConfigure = do_configure ,
200
+ init = device_init ,
201
+ infoChanged = info_changed
202
+ },
66
203
zigbee_handlers = {
204
+ cluster = {
205
+ [IASZone .ID ] = {
206
+ [IASZone .client .commands .ZoneStatusChangeNotification .ID ] = ias_zone_status_change_handler
207
+ }
208
+ },
67
209
attr = {
68
210
[OccupancySensing .ID ] = {
69
211
[OccupancySensing .attributes .Occupancy .ID ] = occupancy_attr_handler
212
+ },
213
+ [IASZone .ID ] = {
214
+ [IASZone .attributes .ZoneStatus .ID ] = ias_zone_status_attr_handler
70
215
}
71
216
}
72
217
},
73
- lifecycle_handlers = {
74
- init = battery_defaults . build_linear_voltage_init ( 2.3 , 3.0 ),
75
- added = added_handler ,
76
- doConfigure = do_configure
218
+ capability_handlers = {
219
+ [ capabilities . refresh . ID ] = {
220
+ [ capabilities . refresh . commands . refresh . NAME ] = do_refresh
221
+ }
77
222
},
78
- can_handle = function (opts , driver , device , ...)
79
- return device :get_manufacturer () == " frient A/S"
80
- end
223
+ can_handle = can_handle_frient_motion_sensor
81
224
}
82
-
83
- return frient_driver
225
+ return frient_motion_driver
0 commit comments