forked from Enapter/marketplace
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from C-Labs-Global/CMDev
Main PR
- Loading branch information
Showing
4 changed files
with
376 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
################################################################################ | ||
# This .gitignore file was automatically created by Microsoft(R) Visual Studio. | ||
################################################################################ | ||
|
||
/.vs/marketplace.fork/v17/.suo | ||
/.vs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# LG RESU (10|16)h Prime (http) | ||
|
||
This _Enapter Device Blueprint_ integrates **LG RESU 10h and 16h Prime** lithium battery control and monitoring via an undocumented http interface on the battery | ||
|
||
Use an Enapter Virtual UCM to load this blueprint and set the IP address of the LG Battery. | ||
|
||
Please ensure that your installer is connecting the LAN connection of the battery to your network. | ||
|
||
## References | ||
|
||
- [LG RESU Prime Battery product page](https://www.lgessbattery.com/us/home-battery/product-info.lg) |
252 changes: 252 additions & 0 deletions
252
battery_management_systems/lg_resu_prime_http/firmware.lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,252 @@ | ||
json = require 'json' | ||
|
||
-- Configuration variables must be also defined | ||
-- in `write_configuration` command arguments in manifest.yml | ||
IP_ADDRESS_CONFIG = 'ip_address' | ||
|
||
MYModel='' | ||
|
||
function main() | ||
scheduler.add(30000, sendmyproperties) | ||
scheduler.add(15000, sendmytelemetry) | ||
|
||
config.init({ | ||
[IP_ADDRESS_CONFIG] = { type = 'string', required = true } | ||
}) | ||
|
||
end | ||
|
||
function registration() | ||
end | ||
|
||
function sendmyproperties() | ||
local properties = {} | ||
local values, err = config.read_all() | ||
if err then | ||
enapter.log('cannot read config: '..tostring(err), 'error') | ||
else | ||
for name, val in pairs(values) do | ||
properties[name] = val | ||
end | ||
end | ||
if (MYModel~='') then | ||
properties['model']=MYModel | ||
end | ||
enapter.send_properties(properties) | ||
end | ||
|
||
function sendmytelemetry() | ||
local json = require('json') | ||
|
||
local values, err = config.read_all() | ||
if err then | ||
enapter.log('cannot read config: '..tostring(err), 'error') | ||
return nil, 'cannot_read_config' | ||
else | ||
local ip_address = values[IP_ADDRESS_CONFIG] | ||
local telemetry = {} | ||
|
||
local response, err = http.get('http://'..ip_address..'/getbmsdata') | ||
if err then | ||
enapter.log('Cannot do request: '..err, 'error') | ||
return | ||
elseif response.code ~= 200 then | ||
enapter.log('Request returned non-OK code: '..response.code, 'error') | ||
return | ||
end | ||
|
||
local tres=response.body | ||
-- http results in a html block that needs to be converted to json | ||
tres=string.gsub(tres, '<caption>BMS Data</caption><tr><th class=\"text%-center\">Item</th><th class=\"text%-center\">Value</th></tr>','{') | ||
tres=string.gsub(tres, '</td></tr><tr><td>', '", "') | ||
tres=string.gsub(tres, '<tr><td>',' "') | ||
tres=string.gsub(tres, '</td><td>','": "') | ||
tres=string.gsub(tres, '</td></tr>', '" }') | ||
--enapter.log('Request succeeded: '..tres, 'info') | ||
|
||
local deco=json.decode(tres) | ||
telemetry["battery_soc"] = tonumber(deco.SOC)/100 | ||
telemetry["battery_power"]= tonumber(deco.Current) | ||
local cur=tonumber(deco.Current) | ||
telemetry["battery_temp"]= tonumber(deco.Temperature)/10 | ||
telemetry["lastresponse"]='All Good' | ||
-- this should have the correct battery status but shows only 0x0001 no matter of state | ||
local tcur=tonumber(deco.OperationModeStatus); | ||
telemetry["battery_energy"]= tonumber(deco.SOH) | ||
telemetry["battery_voltage"]= tonumber(deco.AvgCellVoltage)/100 | ||
|
||
if (MYModel=='') then | ||
if (tonumber(deco.SOH)==10000) then | ||
MYModel="LG RESU10 Prime" | ||
else | ||
MYModel="LG RESU16 Prime" | ||
end | ||
end | ||
|
||
if (tcur==0) then | ||
telemetry["status"]='Off' | ||
elseif (tcur==1) then | ||
telemetry["status"]='Standby' | ||
elseif (tcur==2) then | ||
telemetry["status"]='Initializing' | ||
elseif (tcur==3) then | ||
telemetry["status"]='Charging' | ||
elseif (tcur==4) then | ||
telemetry["status"]='Discharging' | ||
elseif (tcur==5) then | ||
telemetry["status"]='Fault' | ||
elseif (tcur==7) then | ||
telemetry["status"]='Idle' | ||
end | ||
|
||
-- fallback since OperationModeStatus does not work correctly | ||
if (cur==0) then | ||
telemetry["status"]='Standby' | ||
elseif (cur>0) then | ||
telemetry["status"]='Charging' | ||
else | ||
telemetry["status"]='Discharging' | ||
end | ||
|
||
enapter.send_telemetry(telemetry) | ||
end | ||
end | ||
|
||
--------------------------------- | ||
-- Stored Configuration API | ||
--------------------------------- | ||
|
||
config = {} | ||
|
||
-- Initializes config options. Registers required UCM commands. | ||
-- @param options: key-value pairs with option name and option params | ||
-- @example | ||
-- config.init({ | ||
-- address = { type = 'string', required = true }, | ||
-- unit_id = { type = 'number', default = 1 }, | ||
-- reconnect = { type = 'boolean', required = true } | ||
-- }) | ||
function config.init(options) | ||
assert(next(options) ~= nil, 'at least one config option should be provided') | ||
assert(not config.initialized, 'config can be initialized only once') | ||
for name, params in pairs(options) do | ||
local type_ok = params.type == 'string' or params.type == 'number' or params.type == 'boolean' | ||
assert(type_ok, 'type of `'..name..'` option should be either string or number or boolean') | ||
end | ||
|
||
enapter.register_command_handler('write_configuration', config.build_write_configuration_command(options)) | ||
enapter.register_command_handler('read_configuration', config.build_read_configuration_command(options)) | ||
|
||
config.options = options | ||
config.initialized = true | ||
end | ||
|
||
-- Reads all initialized config options | ||
-- @return table: key-value pairs | ||
-- @return nil|error | ||
function config.read_all() | ||
local result = {} | ||
|
||
for name, _ in pairs(config.options) do | ||
local value, err = config.read(name) | ||
if err then | ||
return nil, 'cannot read `'..name..'`: '..err | ||
else | ||
result[name] = value | ||
end | ||
end | ||
|
||
return result, nil | ||
end | ||
|
||
-- @param name string: option name to read | ||
-- @return string | ||
-- @return nil|error | ||
function config.read(name) | ||
local params = config.options[name] | ||
assert(params, 'undeclared config option: `'..name..'`, declare with config.init') | ||
|
||
local ok, value, ret = pcall(function() | ||
return storage.read(name) | ||
end) | ||
|
||
if not ok then | ||
return nil, 'error reading from storage: '..tostring(value) | ||
elseif ret and ret ~= 0 then | ||
return nil, 'error reading from storage: '..storage.err_to_str(ret) | ||
elseif value then | ||
return config.deserialize(name, value), nil | ||
else | ||
return params.default, nil | ||
end | ||
end | ||
|
||
-- @param name string: option name to write | ||
-- @param val string: value to write | ||
-- @return nil|error | ||
function config.write(name, val) | ||
local ok, ret = pcall(function() | ||
return storage.write(name, config.serialize(name, val)) | ||
end) | ||
|
||
if not ok then | ||
return 'error writing to storage: '..tostring(ret) | ||
elseif ret and ret ~= 0 then | ||
return 'error writing to storage: '..storage.err_to_str(ret) | ||
end | ||
end | ||
|
||
-- Serializes value into string for storage | ||
function config.serialize(_, value) | ||
if value then | ||
return tostring(value) | ||
else | ||
return nil | ||
end | ||
end | ||
|
||
-- Deserializes value from stored string | ||
function config.deserialize(name, value) | ||
local params = config.options[name] | ||
assert(params, 'undeclared config option: `'..name..'`, declare with config.init') | ||
|
||
if params.type == 'number' then | ||
return tonumber(value) | ||
elseif params.type == 'string' then | ||
return value | ||
elseif params.type == 'boolean' then | ||
if value == 'true' then | ||
return true | ||
elseif value == 'false' then | ||
return false | ||
else | ||
return nil | ||
end | ||
end | ||
end | ||
|
||
function config.build_write_configuration_command(options) | ||
return function(ctx, args) | ||
for name, params in pairs(options) do | ||
if params.required then | ||
assert(args[name], '`'..name..'` argument required') | ||
end | ||
|
||
local err = config.write(name, args[name]) | ||
if err then ctx.error('cannot write `'..name..'`: '..err) end | ||
end | ||
end | ||
end | ||
|
||
function config.build_read_configuration_command(_config_options) | ||
return function(ctx) | ||
local result, err = config.read_all() | ||
if err then | ||
ctx.error(err) | ||
else | ||
return result | ||
end | ||
end | ||
end | ||
|
||
main() |
107 changes: 107 additions & 0 deletions
107
battery_management_systems/lg_resu_prime_http/manifest.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
blueprint_spec: "device/1.0" | ||
display_name: LG RESU Prime | ||
icon: enapter-battery-storage | ||
|
||
communication_modules: | ||
eth: | ||
product: ENP-VIRTUAL | ||
lua_file: firmware.lua | ||
|
||
properties: | ||
model: | ||
display_name: Device Model | ||
type: string | ||
enum: | ||
- LG RESU10 Prime | ||
- LG RESU16 Prime | ||
ip_address: | ||
display_name: IP Address | ||
type: string | ||
|
||
telemetry: | ||
battery_soc: | ||
display_name: State of Charge | ||
description: Current State of Charge | ||
type: float | ||
unit: precentage | ||
battery_energy: | ||
display_name: Battery Energy | ||
description: Amount Energy of the Battery | ||
type: float | ||
unit: watts | ||
battery_voltage: | ||
display_name: Battery Voltage | ||
type: float | ||
unit: volt | ||
battery_power: | ||
display_name: Power Flow | ||
description: Amount of power flowing in or out of the battery | ||
type: float | ||
unit: watts | ||
battery_temp: | ||
display_name: Battery Temperature | ||
description: Current temperature of the battery | ||
type: float | ||
unit: celsius | ||
status: | ||
display_name: Battery status | ||
type: string | ||
enum: | ||
- 'Off' | ||
- Standby | ||
- Initializing | ||
- Charging | ||
- Discharging | ||
- Fault | ||
- Idle | ||
lastresponse: | ||
display_name: Last Response | ||
description: Last Response from battery | ||
type: string | ||
|
||
alerts: | ||
lowbattery: | ||
severity: error | ||
code: E001 | ||
display_name: Low battery | ||
description: Battery has less then 10% load | ||
|
||
command_groups: | ||
connection: | ||
display_name: Connection | ||
|
||
commands: | ||
# Connection Config | ||
write_configuration: | ||
populate_values_command: read_configuration | ||
display_name: Configure Connection | ||
group: connection | ||
ui: | ||
icon: wrench-outline | ||
arguments: | ||
ip_address: | ||
display_name: IP Address | ||
description: IP address of the LG Prime Battery | ||
type: string | ||
required: true | ||
read_configuration: | ||
display_name: Read Connection Config | ||
group: connection | ||
ui: | ||
icon: wrench-outline | ||
|
||
.cloud: | ||
category: batteries | ||
mobile_main_chart: battery_soc | ||
mobile_telemetry: | ||
- battery_soc | ||
- battery_power | ||
- battery_temp | ||
- battery_energy | ||
- battery_voltage | ||
- operation_mode | ||
mobile_charts: | ||
- battery_soc | ||
- battery_power | ||
- battery_temp | ||
- battery_energy |