-
Notifications
You must be signed in to change notification settings - Fork 463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Aqara Light Switch H2 #1822
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
name: light-power-energy-powerConsumption | ||
components: | ||
- id: main | ||
capabilities: | ||
- id: switch | ||
version: 1 | ||
- id: powerMeter | ||
version: 1 | ||
- id: energyMeter | ||
version: 1 | ||
- id: powerConsumptionReport | ||
version: 1 | ||
- id: firmwareUpdate | ||
version: 1 | ||
- id: refresh | ||
version: 1 | ||
categories: | ||
- name: Light |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,6 +48,7 @@ local COMPONENT_TO_ENDPOINT_MAP = "__component_to_endpoint_map" | |
-- rather than COMPONENT_TO_ENDPOINT_MAP. | ||
local COMPONENT_TO_ENDPOINT_MAP_BUTTON = "__component_to_endpoint_map_button" | ||
local IS_PARENT_CHILD_DEVICE = "__is_parent_child_device" | ||
local IS_AQARA_SWITCH_DEVICE = "__is_aqara_switch_device" | ||
local COLOR_TEMP_BOUND_RECEIVED = "__colorTemp_bound_received" | ||
local COLOR_TEMP_MIN = "__color_temp_min" | ||
local COLOR_TEMP_MAX = "__color_temp_max" | ||
|
@@ -65,6 +66,7 @@ local ON_OFF_SWITCH_ID = 0x0103 | |
local ON_OFF_DIMMER_SWITCH_ID = 0x0104 | ||
local ON_OFF_COLOR_DIMMER_SWITCH_ID = 0x0105 | ||
local GENERIC_SWITCH_ID = 0x000F | ||
local ELECTRICAL_SENSOR_ID = 0x0510 | ||
local device_type_profile_map = { | ||
[ON_OFF_LIGHT_DEVICE_TYPE_ID] = "light-binary", | ||
[DIMMABLE_LIGHT_DEVICE_TYPE_ID] = "light-level", | ||
|
@@ -147,12 +149,20 @@ local device_type_attribute_map = { | |
clusters.Switch.events.LongPress, | ||
clusters.Switch.events.ShortRelease, | ||
clusters.Switch.events.MultiPressComplete | ||
}, | ||
[ELECTRICAL_SENSOR_ID] = { | ||
clusters.ElectricalPowerMeasurement.attributes.ActivePower, | ||
clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyImported, | ||
clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyExported, | ||
clusters.ElectricalEnergyMeasurement.attributes.PeriodicEnergyExported | ||
} | ||
} | ||
|
||
local child_device_profile_overrides = { | ||
{ vendor_id = 0x1321, product_id = 0x000C, child_profile = "switch-binary" }, | ||
{ vendor_id = 0x1321, product_id = 0x000D, child_profile = "switch-binary" }, | ||
{ vendor_id = 0x115F, product_id = 0x1008, child_profile = "light-power-energy-powerConsumption" }, -- 2 switch | ||
{ vendor_id = 0x115F, product_id = 0x1009, child_profile = "light-power-energy-powerConsumption" }, -- 4 switch | ||
} | ||
|
||
local detect_matter_thing | ||
|
@@ -256,6 +266,7 @@ local HELD_THRESHOLD = 1 | |
local STATIC_BUTTON_PROFILE_SUPPORTED = {1, 2, 3, 4, 5, 6, 7, 8} | ||
|
||
local DEFERRED_CONFIGURE = "__DEFERRED_CONFIGURE" | ||
local BUTTON_DEVICE_PROFILED = "__button_device_profiled" | ||
|
||
-- Some switches will send a MultiPressComplete event as part of a long press sequence. Normally the driver will create a | ||
-- button capability event on receipt of MultiPressComplete, but in this case that would result in an extra event because | ||
|
@@ -270,6 +281,7 @@ local SUPPORTS_MULTI_PRESS = "__multi_button" -- for MSM devices (MomentarySwitc | |
local INITIAL_PRESS_ONLY = "__initial_press_only" -- for devices that support MS (MomentarySwitch), but not MSR (MomentarySwitchRelease) | ||
|
||
local HUE_MANUFACTURER_ID = 0x100B | ||
local AQARA_MANUFACTURER_ID = 0x115F | ||
|
||
--helper function to create list of multi press values | ||
local function create_multi_press_values_list(size, supportsHeld) | ||
|
@@ -405,15 +417,24 @@ local function find_default_endpoint(device) | |
return device.MATTER_DEFAULT_ENDPOINT | ||
end | ||
|
||
local function assign_child_profile(device, child_ep) | ||
local function assign_child_profile(device, child_ep, ep_sequence) | ||
local profile | ||
|
||
-- check if device has an overridden child profile that differs from the profile | ||
-- that would match the child's device type | ||
for _, fingerprint in ipairs(child_device_profile_overrides) do | ||
if device.manufacturer_info.vendor_id == fingerprint.vendor_id and | ||
device.manufacturer_info.product_id == fingerprint.product_id then | ||
return fingerprint.child_profile | ||
if device.manufacturer_info.vendor_id == AQARA_MANUFACTURER_ID then | ||
if ep_sequence == 1 then | ||
-- To add Electrical Sensor only to the first EDGE_CHILD(light-power-energy-powerConsumption) | ||
-- The profile of the second EDGE_CHILD is determined in the "for" loop below (e.g., light-binary) | ||
device:set_field(IS_AQARA_SWITCH_DEVICE, true, {persist = true}) | ||
return fingerprint.child_profile | ||
end | ||
else | ||
return fingerprint.child_profile | ||
end | ||
end | ||
end | ||
|
||
|
@@ -436,6 +457,9 @@ local function assign_child_profile(device, child_ep) | |
end | ||
|
||
local function do_configure(driver, device) | ||
if device:get_field(BUTTON_DEVICE_PROFILED) then | ||
return | ||
end | ||
local energy_eps = embedded_cluster_utils.get_endpoints(device, clusters.ElectricalEnergyMeasurement.ID) | ||
local power_eps = embedded_cluster_utils.get_endpoints(device, clusters.ElectricalPowerMeasurement.ID) | ||
local valve_eps = embedded_cluster_utils.get_endpoints(device, clusters.ValveConfigurationAndControl.ID) | ||
|
@@ -537,12 +561,14 @@ local function initialize_switch(driver, device) | |
component_map_used = true | ||
end | ||
|
||
local ep_sequence = 0 | ||
for _, ep in ipairs(switch_eps) do | ||
if device:supports_server_cluster(clusters.OnOff.ID, ep) then | ||
num_switch_server_eps = num_switch_server_eps + 1 | ||
if ep ~= main_endpoint then -- don't create a child device that maps to the main endpoint | ||
ep_sequence = ep_sequence + 1 | ||
local name = string.format("%s %d", device.label, num_switch_server_eps) | ||
local child_profile = assign_child_profile(device, ep) | ||
local child_profile = assign_child_profile(device, ep, ep_sequence) | ||
driver:try_create_device( | ||
{ | ||
type = "EDGE_CHILD", | ||
|
@@ -579,6 +605,7 @@ local function initialize_switch(driver, device) | |
end | ||
device:try_update_metadata({profile = profile_name}) | ||
device:set_field(DEFERRED_CONFIGURE, true) | ||
device:set_field(BUTTON_DEVICE_PROFILED, true) | ||
elseif #button_eps > 0 then | ||
local battery_support = false | ||
if device.manufacturer_info.vendor_id ~= HUE_MANUFACTURER_ID and | ||
|
@@ -599,6 +626,7 @@ local function initialize_switch(driver, device) | |
if profile_name then | ||
device:try_update_metadata({profile = profile_name}) | ||
device:set_field(DEFERRED_CONFIGURE, true) | ||
device:set_field(BUTTON_DEVICE_PROFILED, true) | ||
else | ||
configure_buttons(device) | ||
end | ||
|
@@ -680,7 +708,9 @@ local function device_init(driver, device) | |
end | ||
local main_endpoint = find_default_endpoint(device) | ||
for _, ep in ipairs(device.endpoints) do | ||
if ep.endpoint_id ~= main_endpoint and ep.endpoint_id ~= 0 then | ||
if ep.endpoint_id ~= main_endpoint and | ||
(device:get_field(IS_AQARA_SWITCH_DEVICE) or ep.endpoint_id ~= 0) then | ||
-- insert energy management into InteractionRequest list when IS_AQARA_SWITCH_DEVICE | ||
Comment on lines
-683
to
+713
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What attribute are we expecting to track for the Aqara switch at its 0 endpoint? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
[ELECTRICAL_SENSOR_ID] = {
clusters.ElectricalPowerMeasurement.attributes.ActivePower,
clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyImported,
clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyExported,
clusters.ElectricalEnergyMeasurement.attributes.PeriodicEnergyExported
} |
||
local id = 0 | ||
for _, dt in ipairs(ep.device_types) do | ||
id = math.max(id, dt.device_type_id) | ||
|
@@ -983,7 +1013,11 @@ local function cumul_energy_exported_handler(driver, device, ib, response) | |
if ib.data.elements.energy then | ||
local watt_hour_value = ib.data.elements.energy.value / CONVERSION_CONST_MILLIWATT_TO_WATT | ||
device:set_field(TOTAL_EXPORTED_ENERGY, watt_hour_value) | ||
device:emit_event(capabilities.energyMeter.energy({ value = watt_hour_value, unit = "Wh" })) | ||
if device:get_field(IS_AQARA_SWITCH_DEVICE) then | ||
device:emit_event_for_endpoint(1, capabilities.energyMeter.energy({ value = watt_hour_value, unit = "Wh" })) | ||
else | ||
Comment on lines
+1016
to
+1018
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this- is unnecessary There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
device:emit_event(capabilities.energyMeter.energy({ value = watt_hour_value, unit = "Wh" })) | ||
end | ||
end | ||
end | ||
|
||
|
@@ -1046,7 +1080,11 @@ end | |
local function active_power_handler(driver, device, ib, response) | ||
if ib.data.value then | ||
local watt_value = ib.data.value / CONVERSION_CONST_MILLIWATT_TO_WATT | ||
device:emit_event(capabilities.powerMeter.power({ value = watt_value, unit = "W"})) | ||
if device:get_field(IS_AQARA_SWITCH_DEVICE) then | ||
device:emit_event_for_endpoint(1, capabilities.powerMeter.power({ value = watt_value, unit = "W"})) | ||
else | ||
Comment on lines
+1083
to
+1085
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this- is unnecessary There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
device:emit_event(capabilities.powerMeter.power({ value = watt_value, unit = "W"})) | ||
end | ||
end | ||
end | ||
|
||
|
@@ -1174,6 +1212,7 @@ local matter_driver_template = { | |
[clusters.ElectricalPowerMeasurement.attributes.ActivePower.ID] = active_power_handler, | ||
}, | ||
[clusters.ElectricalEnergyMeasurement.ID] = { | ||
[clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyImported.ID] = energy_report_handler_factory(true), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sorry for the confusion- see this PR which will change the system to only use imported and not exported energy. This fixup be merged before this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After that PR is merged, I will merge this PR. |
||
[clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyExported.ID] = energy_report_handler_factory(true), | ||
[clusters.ElectricalEnergyMeasurement.attributes.PeriodicEnergyExported.ID] = energy_report_handler_factory(false), | ||
}, | ||
|
@@ -1234,6 +1273,7 @@ local matter_driver_template = { | |
clusters.PowerSource.attributes.BatPercentRemaining, | ||
}, | ||
[capabilities.energyMeter.ID] = { | ||
clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyImported, | ||
clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyExported, | ||
clusters.ElectricalEnergyMeasurement.attributes.PeriodicEnergyExported | ||
}, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no reason to overcomplicate this with multiple returns. There just needs to be extra logic before the return for the aqara devices.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If two On/Off Light devices are added as EDGE_CHILD, only the first device is defined as
fingerprint.child_profile
(light-power-energy-powerConsumption
profile), and the profile of the second device is to select thelight-binary
profile by thefor loop
logic below.Therefore, we need to keep the existing logic as it is, so I added comments in the source code.