2024-07-15 11:02:57 +00:00
|
|
|
defmodule WeewxProxy.Modbus.Deye do
|
|
|
|
@moduledoc false
|
|
|
|
|
|
|
|
require Logger
|
|
|
|
|
|
|
|
use Tortoise311.Handler
|
|
|
|
|
|
|
|
alias WeewxProxy.{Publisher, Utils}
|
|
|
|
|
|
|
|
# Callbacks
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def init(_opts) do
|
|
|
|
_ = Logger.info("Initializing handler")
|
2024-08-10 10:25:13 +00:00
|
|
|
{:ok, %{solar_energy_day: 0, solar_energy_total1: 0, solar_energy_total2: 0}}
|
2024-07-15 11:02:57 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def connection(:up, state) do
|
|
|
|
_ = Logger.info("Connection has been established")
|
|
|
|
{:ok, state}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def connection(:down, state) do
|
|
|
|
_ = Logger.warning("Connection has been dropped")
|
|
|
|
{:ok, state}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def connection(:terminating, state) do
|
|
|
|
_ = Logger.warning("Connection is terminating")
|
|
|
|
{:ok, state}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def subscription(:up, topic, state) do
|
|
|
|
_ = Logger.info("Subscribed to `#{topic}'")
|
|
|
|
{:ok, state}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def subscription({:warn, [requested: req, accepted: qos]}, topic, state) do
|
|
|
|
_ = Logger.warning("Subscribed to `#{topic}'; requested #{req} but got accepted with QoS #{qos}")
|
|
|
|
{:ok, state}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def subscription({:error, reason}, topic, state) do
|
|
|
|
_ = Logger.error("Error subscribing to `#{topic}'; #{inspect(reason)}")
|
|
|
|
{:ok, state}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def subscription(:down, topic, state) do
|
|
|
|
_ = Logger.info("Unsubscribed from `#{topic}'")
|
|
|
|
{:ok, state}
|
|
|
|
end
|
|
|
|
|
2024-08-03 18:55:56 +00:00
|
|
|
@impl true
|
|
|
|
def handle_message(["bitshake", "tele", "smartmeter", topic], publish, state) do
|
2024-08-09 18:14:49 +00:00
|
|
|
current_time = Time.utc_now()
|
|
|
|
current_hour = current_time.hour
|
2024-08-10 10:25:13 +00:00
|
|
|
new_state = if current_hour == 0, do: %{state | solar_energy_day: 0}, else: state
|
2024-08-09 18:14:49 +00:00
|
|
|
|
2024-08-03 18:55:56 +00:00
|
|
|
if topic == "SENSOR" do
|
|
|
|
{:ok, meter_data} = Jason.decode(publish)
|
|
|
|
|
|
|
|
timestamp = DateTime.utc_now() |> DateTime.to_unix()
|
|
|
|
day = get_in(meter_data, ["eBZ", "E_in"])
|
2024-08-04 11:05:43 +00:00
|
|
|
day_export = get_in(meter_data, ["eBZ", "E_out"])
|
2024-08-03 18:55:56 +00:00
|
|
|
active = get_in(meter_data, ["eBZ", "Power"])
|
|
|
|
|
|
|
|
mqtt_data = %{
|
|
|
|
dateTime: timestamp,
|
|
|
|
homeEnergyDay: day,
|
2024-08-04 11:05:43 +00:00
|
|
|
homeEnergyExportDay: day_export,
|
2024-08-03 18:55:56 +00:00
|
|
|
homeEnergyActive: active
|
|
|
|
}
|
|
|
|
|
|
|
|
:ok = Publisher.publish("weewx/ingest_si", mqtt_data)
|
2024-08-09 15:16:22 +00:00
|
|
|
:ok = Publisher.publish_value_map("hadata/bitShake", mqtt_data)
|
2024-08-03 18:55:56 +00:00
|
|
|
|
2024-08-09 18:14:49 +00:00
|
|
|
{:ok, new_state}
|
2024-08-03 18:55:56 +00:00
|
|
|
else
|
2024-08-09 18:14:49 +00:00
|
|
|
{:ok, new_state}
|
2024-08-03 18:55:56 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-07-15 11:02:57 +00:00
|
|
|
@impl true
|
|
|
|
def handle_message(topic, publish, state) do
|
|
|
|
full_topic = Enum.join(topic, "/")
|
|
|
|
parsed_message = parse_message(full_topic, publish)
|
|
|
|
|
2024-07-15 19:14:50 +00:00
|
|
|
state = handle_reading(full_topic, parsed_message, state)
|
2024-07-15 11:02:57 +00:00
|
|
|
|
|
|
|
{:ok, state}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def terminate(reason, _state) do
|
|
|
|
_ = Logger.warning("Client has been terminated with reason: `#{inspect(reason)}'")
|
|
|
|
:ok
|
|
|
|
end
|
|
|
|
|
|
|
|
# Helper
|
|
|
|
|
|
|
|
@spec parse_message(String.t(), String.t()) :: float() | nil
|
2024-08-10 10:25:13 +00:00
|
|
|
defp parse_message(topic, message)
|
|
|
|
when topic in ["deye/day_energy", "deye/ac/active_power", "deye/1/total_energy", "deye/2/total_energy"] do
|
2024-07-15 11:02:57 +00:00
|
|
|
Utils.parse_float(message)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp parse_message(_topic, _message), do: nil
|
|
|
|
|
2024-07-15 19:14:50 +00:00
|
|
|
@spec handle_reading(String.t(), float() | nil, {:day | :active, float()}) :: {:day | :active, float()} | nil
|
|
|
|
defp handle_reading(_topic, nil, state), do: state
|
2024-07-15 11:02:57 +00:00
|
|
|
|
2024-08-10 10:25:13 +00:00
|
|
|
defp handle_reading("deye/day_energy", reading, state) do
|
|
|
|
if reading >= state.solar_energy_day do
|
2024-08-09 18:14:49 +00:00
|
|
|
timestamp = DateTime.utc_now() |> DateTime.to_unix()
|
2024-07-16 15:44:39 +00:00
|
|
|
|
2024-08-09 18:14:49 +00:00
|
|
|
data = %{
|
|
|
|
dateTime: timestamp,
|
|
|
|
solarEnergyDay: reading * 1000.0
|
|
|
|
}
|
2024-07-15 19:14:50 +00:00
|
|
|
|
2024-08-09 18:14:49 +00:00
|
|
|
:ok = Publisher.publish("weewx/ingest_si", data)
|
|
|
|
:ok = Publisher.publish_value_map("hadata/deye", data)
|
2024-07-15 19:14:50 +00:00
|
|
|
|
2024-08-10 10:25:13 +00:00
|
|
|
%{state | solar_energy_day: reading}
|
2024-08-09 18:14:49 +00:00
|
|
|
else
|
2024-08-10 10:25:13 +00:00
|
|
|
state
|
2024-08-09 18:14:49 +00:00
|
|
|
end
|
2024-07-15 19:14:50 +00:00
|
|
|
end
|
|
|
|
|
2024-08-09 18:14:49 +00:00
|
|
|
defp handle_reading("deye/ac/active_power", reading, state) do
|
2024-07-15 19:14:50 +00:00
|
|
|
timestamp = DateTime.utc_now() |> DateTime.to_unix()
|
2024-07-16 15:44:39 +00:00
|
|
|
|
|
|
|
data = %{
|
|
|
|
dateTime: timestamp,
|
|
|
|
solarEnergyActive: reading
|
|
|
|
}
|
|
|
|
|
2024-07-15 19:14:50 +00:00
|
|
|
:ok = Publisher.publish("weewx/ingest_si", data)
|
2024-08-09 15:16:22 +00:00
|
|
|
:ok = Publisher.publish_value_map("hadata/deye", data)
|
2024-07-15 19:14:50 +00:00
|
|
|
|
2024-08-09 18:14:49 +00:00
|
|
|
state
|
2024-07-15 11:02:57 +00:00
|
|
|
end
|
2024-08-10 10:25:13 +00:00
|
|
|
|
|
|
|
defp handle_reading("deye/1/total_energy", reading, state) do
|
|
|
|
if reading > 0.1 and reading >= state.solar_energy_total1 do
|
|
|
|
timestamp = DateTime.utc_now() |> DateTime.to_unix()
|
|
|
|
|
|
|
|
data = %{
|
|
|
|
dateTime: timestamp,
|
|
|
|
solarEnergyTotal1: reading
|
|
|
|
}
|
|
|
|
|
|
|
|
# :ok = Publisher.publish("weewx/ingest_si", data)
|
|
|
|
:ok = Publisher.publish_value_map("hadata/deye", data)
|
|
|
|
|
|
|
|
%{state | solar_energy_total1: reading}
|
|
|
|
else
|
|
|
|
state
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defp handle_reading("deye/2/total_energy", reading, state) do
|
|
|
|
if reading > 0.1 and reading >= state.solar_energy_total2 do
|
|
|
|
timestamp = DateTime.utc_now() |> DateTime.to_unix()
|
|
|
|
|
|
|
|
data = %{
|
|
|
|
dateTime: timestamp,
|
|
|
|
solarEnergyTotal2: reading
|
|
|
|
}
|
|
|
|
|
|
|
|
# :ok = Publisher.publish("weewx/ingest_si", data)
|
|
|
|
:ok = Publisher.publish_value_map("hadata/deye", data)
|
|
|
|
|
|
|
|
%{state | solar_energy_total2: reading}
|
|
|
|
else
|
|
|
|
state
|
|
|
|
end
|
|
|
|
end
|
2024-07-15 11:02:57 +00:00
|
|
|
end
|