diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..50ed7e0d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+################################################################################
+# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
+################################################################################
+
+/.vs/marketplace.fork/v17/.suo
+/.vs
diff --git a/battery_management_systems/lg_resu_prime_http/README.md b/battery_management_systems/lg_resu_prime_http/README.md
new file mode 100644
index 00000000..2de73bcb
--- /dev/null
+++ b/battery_management_systems/lg_resu_prime_http/README.md
@@ -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)
diff --git a/battery_management_systems/lg_resu_prime_http/firmware.lua b/battery_management_systems/lg_resu_prime_http/firmware.lua
new file mode 100644
index 00000000..645e4f24
--- /dev/null
+++ b/battery_management_systems/lg_resu_prime_http/firmware.lua
@@ -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, '
BMS DataItem | Value |
','{')
+ tres=string.gsub(tres, '', '", "')
+ tres=string.gsub(tres, ' |
',' "')
+ tres=string.gsub(tres, ' | ','": "')
+ tres=string.gsub(tres, ' |
', '" }')
+ --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()
diff --git a/battery_management_systems/lg_resu_prime_http/manifest.yml b/battery_management_systems/lg_resu_prime_http/manifest.yml
new file mode 100644
index 00000000..5b0fc750
--- /dev/null
+++ b/battery_management_systems/lg_resu_prime_http/manifest.yml
@@ -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