Skip to content

Commit

Permalink
Added sihas energy monitor for wwst (#578)
Browse files Browse the repository at this point in the history
* wwst(sihas_energy)

* Fix indents

* Change electrical divisor units from 1000 to 1

* Device profile and other changes

1. Adding power-meter-consumption-report-sihas
2. powerConsumptionReport should be updated every 15 minutes.
3. Applying ENERGY_METER_OFFSET value.

* Add variable declaration

Add variable declaration

* Deleting unnecessary variables

Deleting unnecessary variables

* update configuration report

update configuration report

* Convert identation to Spaces

.

* Changed the decimal point from 5 digits to 3 digits(W)

* Modified warning code

* Adding some tests for shinasys

* Updating tests for shinasys

* Update init.lua

Removed unnecessary calculation (math.floor).

* Removed lines with only whitespace

* Update test_zigbee_power_meter_consumption_report_sihas.lua

Update test code for powerConsumptionReport

* Update test_zigbee_power_meter_consumption_report_sihas.lua
  • Loading branch information
shinasys authored Jun 5, 2023
1 parent e40fedc commit beba748
Show file tree
Hide file tree
Showing 4 changed files with 268 additions and 4 deletions.
12 changes: 11 additions & 1 deletion drivers/SmartThings/zigbee-power-meter/fingerprints.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,17 @@ zigbeeManufacturer:
deviceLabel: SiHAS Energy Monitor
manufacturer: ShinaSystem
model: "PMM-300Z1"
deviceProfileName: power-meter
deviceProfileName: power-meter-consumption-report-sihas
- id: "ShinaSystem/PMM-300Z2"
deviceLabel: SiHAS Energy Monitor
manufacturer: ShinaSystem
model: "PMM-300Z2"
deviceProfileName: power-meter-consumption-report-sihas
- id: "ShinaSystem/PMM-300Z3"
deviceLabel: SiHAS Energy Monitor
manufacturer: ShinaSystem
model: "PMM-300Z3"
deviceProfileName: power-meter-consumption-report-sihas
zigbeeGeneric:
- id: "genericMeter"
deviceLabel: Zigbee Meter
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: power-meter-consumption-report-sihas
components:
- id: main
capabilities:
- id: powerMeter
version: 1
- id: energyMeter
version: 1
- id: powerConsumptionReport
version: 1
- id: firmwareUpdate
version: 1
- id: refresh
version: 1
categories:
- name: CurbPowerMeter
90 changes: 87 additions & 3 deletions drivers/SmartThings/zigbee-power-meter/src/shinasystems/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,43 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.

local capabilities = require "st.capabilities"
local constants = require "st.zigbee.constants"
local clusters = require "st.zigbee.zcl.clusters"
local SimpleMetering = clusters.SimpleMetering
local ElectricalMeasurement = clusters.ElectricalMeasurement

local ZIGBEE_POWER_METER_FINGERPRINTS = {
{ model = "PMM-300Z1" }
{ model = "PMM-300Z1" },
{ model = "PMM-300Z2" },
{ model = "PMM-300Z3" }
}

local POWERMETER_CONFIGURATION_V2 = {
{
cluster = SimpleMetering.ID,
attribute = SimpleMetering.attributes.CurrentSummationDelivered.ID,
minimum_interval = 5,
maximum_interval = 300,
data_type = SimpleMetering.attributes.CurrentSummationDelivered.base_type,
reportable_change = 1
},
{
cluster = SimpleMetering.ID,
attribute = SimpleMetering.attributes.InstantaneousDemand.ID,
minimum_interval = 5,
maximum_interval = 300,
data_type = SimpleMetering.attributes.InstantaneousDemand.base_type,
reportable_change = 1
},
{ -- reporting : no
cluster = ElectricalMeasurement.ID,
attribute = ElectricalMeasurement.attributes.ActivePower.ID,
minimum_interval = 0,
maximum_interval = 65535,
data_type = ElectricalMeasurement.attributes.ActivePower.base_type,
reportable_change = 1
}
}

local is_shinasystems_power_meter = function(opts, driver, device)
Expand All @@ -28,23 +61,74 @@ local is_shinasystems_power_meter = function(opts, driver, device)
return false
end

local function energy_meter_handler(driver, device, value, zb_rx)
local multiplier = device:get_field(constants.SIMPLE_METERING_MULTIPLIER_KEY) or 1
local divisor = device:get_field(constants.SIMPLE_METERING_DIVISOR_KEY) or 1000
local raw_value = value.value
local raw_value_kilowatts = raw_value * multiplier/divisor

local offset = device:get_field(constants.ENERGY_METER_OFFSET) or 0
if raw_value_kilowatts < offset then
--- somehow our value has gone below the offset, so we'll reset the offset, since the device seems to have
offset = 0
device:set_field(constants.ENERGY_METER_OFFSET, offset, {persist = true})
end
raw_value_kilowatts = raw_value_kilowatts - offset

local raw_value_watts = raw_value_kilowatts*1000
local delta_tick
local last_save_ticks = device:get_field("LAST_SAVE_TICK")

if last_save_ticks == nil then last_save_ticks = 0 end
delta_tick = os.time() - last_save_ticks

-- wwst energy certification : powerConsumptionReport capability values should be updated every 15 minutes.
-- Check if 15 minutes have passed since the reporting time of current_power_consumption.
if delta_tick >= 15*60 then
local delta_energy = 0.0
local current_power_consumption = device:get_latest_state("main", capabilities.powerConsumptionReport.ID, capabilities.powerConsumptionReport.powerConsumption.NAME)
if current_power_consumption ~= nil then
delta_energy = math.max(raw_value_watts - current_power_consumption.energy, 0.0)
end
device:emit_event(capabilities.powerConsumptionReport.powerConsumption({energy = raw_value_watts, deltaEnergy = delta_energy })) -- the unit of these values should be 'Wh'

local curr_save_tick = last_save_ticks + 15*60 -- Set the time to a regular interval by adding 15 minutes to the existing last_save_ticks.
-- If the time 15 minutes from now is less than the current time, set the current time as the last time.
if curr_save_tick + 15*60 < os.time() then
curr_save_tick = os.time()
end
device:set_field("LAST_SAVE_TICK", curr_save_tick, {persist = false})
end
device:emit_event(capabilities.energyMeter.energy({value = raw_value_kilowatts, unit = "kWh"}))
end

local do_configure = function(self, device)
device:refresh()
device:configure()
end

local device_init = function(self, device)
device:set_field(constants.SIMPLE_METERING_DIVISOR_KEY, 1000, {persist = true})
device:set_field(constants.ELECTRICAL_MEASUREMENT_DIVISOR_KEY, 1000, {persist = true})
for _, attribute in ipairs(POWERMETER_CONFIGURATION_V2) do
device:add_configured_attribute(attribute)
device:add_monitored_attribute(attribute)
end
end

local shinasystems_power_meter_handler = {
NAME = "shinasystems power meter handler",
zigbee_handlers = {
attr = {
[SimpleMetering.ID] = {
[SimpleMetering.attributes.CurrentSummationDelivered.ID] = energy_meter_handler
}
}
},
lifecycle_handlers = {
init = device_init,
doConfigure = do_configure,
},
can_handle = is_shinasystems_power_meter
}

return shinasystems_power_meter_handler
return shinasystems_power_meter_handler
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
-- Copyright 2022 SmartThings
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

-- Mock out globals
local test = require "integration_test"
local clusters = require "st.zigbee.zcl.clusters"
local ElectricalMeasurement = clusters.ElectricalMeasurement
local SimpleMetering = clusters.SimpleMetering
local capabilities = require "st.capabilities"
local zigbee_test_utils = require "integration_test.zigbee_test_utils"
local t_utils = require "integration_test.utils"

local mock_device = test.mock_device.build_test_zigbee_device(
{
profile = t_utils.get_profile_definition("power-meter-consumption-report-sihas.yml"),
zigbee_endpoints = {
[1] = {
id = 1,
model = "PMM-300Z2",
server_clusters = {SimpleMetering.ID, ElectricalMeasurement.ID}
}
}
}
)

zigbee_test_utils.prepare_zigbee_env_info()
local function test_init()
test.mock_device.add_test_device(mock_device)
zigbee_test_utils.init_noop_health_check_timer()
end

test.set_test_init_function(test_init)

test.register_message_test(
"ActivePower Report should be handled. Sensor value is in W, capability attribute value is in hectowatts",
{
{
channel = "zigbee",
direction = "receive",
message = { mock_device.id, ElectricalMeasurement.attributes.ACPowerDivisor:build_test_attr_report(mock_device, 0x01) }
},
{
channel = "zigbee",
direction = "receive",
message = { mock_device.id, ElectricalMeasurement.attributes.ActivePower:build_test_attr_report(mock_device,
27) },
},
{
channel = "capability",
direction = "send",
message = mock_device:generate_test_message("main", capabilities.powerMeter.power({ value = 27.0, unit = "W" }))
}
}
)

test.register_coroutine_test(
"SimpleMetering event should be handled by powerConsumptionReport capability",
function()
test.timer.__create_and_queue_test_time_advance_timer(15*60, "oneshot")
-- #1 : 15 minutes have passed
test.mock_time.advance_time(15*60)
test.socket.zigbee:__queue_receive({
mock_device.id,
SimpleMetering.attributes.CurrentSummationDelivered:build_test_attr_report(mock_device,1500)
})
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", capabilities.powerConsumptionReport.powerConsumption({energy = 1500.0, deltaEnergy = 0.0 }))
)
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", capabilities.energyMeter.energy({value = 1.5, unit = "kWh"}))
)
-- #2 : Not even 15 minutes passed
test.wait_for_events()
test.mock_time.advance_time(1*60)
test.socket.zigbee:__queue_receive({
mock_device.id,
SimpleMetering.attributes.CurrentSummationDelivered:build_test_attr_report(mock_device,1700)
})
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", capabilities.energyMeter.energy({value = 1.7, unit = "kWh"}))
)
-- #3 : 15 minutes have passed
test.wait_for_events()
test.mock_time.advance_time(14*60)
test.socket.zigbee:__queue_receive({
mock_device.id,
SimpleMetering.attributes.CurrentSummationDelivered:build_test_attr_report(mock_device,2000)
})
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", capabilities.powerConsumptionReport.powerConsumption({energy = 2000.0, deltaEnergy = 500.0 }))
)
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", capabilities.energyMeter.energy({value = 2.0, unit = "kWh"}))
)
end
)

test.register_coroutine_test(
"lifecycle configure event should configure device",
function ()
test.socket.zigbee:__set_channel_ordering("relaxed")
test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" })
test.socket.zigbee:__expect_send({
mock_device.id,
SimpleMetering.attributes.InstantaneousDemand:read(mock_device)
})
test.socket.zigbee:__expect_send({
mock_device.id,
SimpleMetering.attributes.CurrentSummationDelivered:read(mock_device)
})
test.socket.zigbee:__expect_send({
mock_device.id,
ElectricalMeasurement.attributes.ActivePower:read(mock_device)
})
test.socket.zigbee:__expect_send({
mock_device.id,
zigbee_test_utils.build_bind_request(mock_device,
zigbee_test_utils.mock_hub_eui,
SimpleMetering.ID)
})
test.socket.zigbee:__expect_send({
mock_device.id,
SimpleMetering.attributes.InstantaneousDemand:configure_reporting(mock_device, 5, 300, 1)
})
test.socket.zigbee:__expect_send({
mock_device.id,
SimpleMetering.attributes.CurrentSummationDelivered:configure_reporting(mock_device, 5, 300, 1)
})
test.socket.zigbee:__expect_send({
mock_device.id,
zigbee_test_utils.build_bind_request(mock_device,
zigbee_test_utils.mock_hub_eui,
ElectricalMeasurement.ID)
})
test.socket.zigbee:__expect_send({
mock_device.id,
ElectricalMeasurement.attributes.ActivePower:configure_reporting(mock_device, 0, 65535, 1)
})
mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" })
end
)

test.run_registered_tests()

0 comments on commit beba748

Please sign in to comment.