Skip to content

Instantly share code, notes, and snippets.

@maxlapshin
Last active January 15, 2019 04:33
Show Gist options
  • Save maxlapshin/1c803f05dd90c2ae98655988208c990a to your computer and use it in GitHub Desktop.
Save maxlapshin/1c803f05dd90c2ae98655988208c990a to your computer and use it in GitHub Desktop.
#!/usr/bin/env escript
%%
%%
-mode(compile).
main([IP]) ->
{ok, Socket} = gen_tcp:connect(IP, 1560, [binary, {active,false}]),
loop(Socket);
main([IP, "set_speed", Speed]) ->
{ok, Socket} = gen_tcp:connect(IP, 1560, [binary, {active,false}]),
call(Socket, set_speed, list_to_integer(Speed)),
timer:sleep(2000),
Stat = call(Socket, status),
io:format("~s\n~160p\n", [maps:get(msg,Stat,""),Stat]),
ok;
main([IP, "set_zone_speed", Zone_, Speed_, Level_]) ->
{ok, Socket} = gen_tcp:connect(IP, 1560, [binary, {active,false}]),
Zone = list_to_integer(Zone_),
Speed = case Speed_ of
"-" -> undefined;
_ -> list_to_integer(Speed_)
end,
Level = case Level_ of
"-" -> undefined;
_ -> list_to_integer(Level_)
end,
call(Socket, set_zone_speed, {Zone, Speed, Level}),
timer:sleep(2000),
io:format("Zone: ~160p\n", [call(Socket, zone_level, Zone)]),
Stat = call(Socket, status),
io:format("~s\n~160p\n", [maps:get(msg,Stat,""),Stat]),
ok;
main([IP, "set_power", Value]) ->
{ok, Socket} = gen_tcp:connect(IP, 1560, [binary, {active,false}]),
call(Socket, set_power, Value == "on"),
timer:sleep(1000),
Stat = call(Socket, status),
io:format("~s\n~160p\n", [maps:get(msg,Stat,""),Stat]),
ok;
main([IP, "set_options", Key, Value_]) when Key == "mode"; Key == "humidifier"; Key == "comfort"; Key == "restart" ->
{ok, Socket} = gen_tcp:connect(IP, 1560, [binary, {active,false}]),
Value = case Value_ of
"on" -> true;
"off" -> false;
"warm" -> warm;
"cool" -> cool;
"auto" -> auto;
"disabled" -> disabled
end,
Arg = #{list_to_atom(Key) => Value},
call(Socket, set_options, Arg),
timer:sleep(1000),
Stat = call(Socket, status),
io:format("~s\n~160p\n", [maps:get(msg,Stat,""),Stat]),
ok.
loop(Socket) ->
HS = call(Socket, handshake),
io:format("~160p\n", [HS]),
Stat = call(Socket, status),
io:format("~s\n~160p\n", [maps:get(msg,Stat,""),Stat]),
io:format("Zone1: ~160p\n", [call(Socket, zone_level, 1)]),
io:format("Zone2: ~160p\n", [call(Socket, zone_level, 2)]),
io:format("Zone3: ~160p\n", [call(Socket, zone_level, 3)]),
io:format("Zone4: ~160p\n", [call(Socket, zone_level, 4)]),
io:format("Sensors: ~160p\n", [call(Socket, sensors)]),
ok.
call(Socket, Command) ->
call(Socket, Command, undefined).
call(Socket, Command, Argument) ->
Code = case Command of
handshake -> "VPr07";
status -> "VSt07";
zone_level -> "VZL0"++integer_to_list(Argument);
sensors -> "VSens";
set_temperature -> "VWTmp";
set_power -> "VWPwr";
set_humidity -> "VWHum";
set_speed -> "VWSpd";
set_zone_speed -> "VWZon";
set_options -> "VWFtr"
end,
Postfix = case Command of
set_power -> "_" ++ integer_to_list(case Argument of true -> 11; false -> 10 end, 16);
set_temperature -> "_"++integer_to_list(Argument,16);
set_humidity -> "_" ++ integer_to_list(Argument,16);
set_speed -> "_"++ integer_to_list(Argument,16);
set_zone_speed ->
{ZoneNumber,TargetSpeedV,ZoneLevelV} = Argument,
ZoneLevel = case ZoneLevelV of
undefined -> 255;
_ -> ZoneLevelV
end,
TargetSpeed = case TargetSpeedV of
undefined -> 15;
_ -> TargetSpeedV
end,
"_" ++ integer_to_list(TargetSpeed,16)++"_"++integer_to_list(ZoneNumber,16)++"_"++integer_to_list(ZoneLevel,16);
set_options ->
ModeSet = case maps:get(mode,Argument,undefined) of
undefined -> 0;
warm -> 1;
cool -> 2;
auto -> 3;
disabled -> 4
end,
HumidSet = case maps:get(humidifier,Argument,undefined) of
undefined -> 0;
true -> 1;
false -> 2
end,
Comfort = case maps:get(comfort,Argument,undefined) of
undefined -> 0;
true -> 1;
false -> 2
end,
Restart = case maps:get(restart,Argument,undefined) of
undefined -> 0;
true -> 1;
false -> 2
end,
<<B1:8,B2:8>> = <<0:7, Restart:2, Comfort:2, HumidSet:2, ModeSet:3>>,
<<B:16>> = <<B1:8,B2:8>>,
"_"++integer_to_list(B,16);
_ ->
""
end,
ok = gen_tcp:send(Socket, [Code,"_70B6",Postfix]),
{ok, Bin1} = gen_tcp:recv(Socket, 0),
Reply = parse(Bin1),
Reply.
parse(<<"OK_",Command/binary>>) ->
Parts = binary:split(Command, <<"_">>, [global]),
{ok, Parts};
parse(<<Code:5/binary, "_", Command/binary>>) ->
Parts = [if
size(Part) == 3 orelse size(Part) == 4 -> <<(binary_to_integer(Part,16)):16/little>>;
true -> Part
end || Part <- binary:split(Command, <<"_">>, [global])],
parse2(Code, Parts).
parse2(<<"VSens">>, [<<>>, TInf, HInf, TRoom, HRoom, TOut, HOut, THF, <<Pwr1,Pwr2>>]) ->
% D = fun(<<7,16#FB>>) -> undefined;
% ()
#{
temp_vent => TInf,
humidity_vent => HInf,
temp_room => TRoom,
humidity_room => HRoom,
temp_out => TOut,
humidity_out => HOut,
temp_water => THF,
power => binary:decode_unsigned(<<Pwr2,Pwr1>>)
};
parse2(<<"VPr07">>, [<<TempMin, TempMax>>, <<SpeedMin, SpeedMax>>, <<HumidMin,HumidMax>>,
Misc, Protocol, <<TPD1,TPD2>>, Controller| _Parts]) ->
<<_:3, VavZones:5, IsAuto:1, HasCooler:1, HasHumid:1, HasCascH:1, HasCascT:1, ShowHumid:1,
EnabledVavReg:1, EnabledVav:1>> = Misc,
#{temp => [TempMin, TempMax],
speed => [SpeedMin, SpeedMax],
humidity => [HumidMin,HumidMax],
vav_zone_count => VavZones,
vav_enabled => EnabledVav == 1,
vav_pressure_enabled => EnabledVavReg == 1,
show_humidity => ShowHumid == 1,
has_cascade_t => HasCascT == 1,
has_cascade_h => HasCascH == 1,
has_humidifier => HasHumid == 1,
has_cooler => HasCooler == 1,
has_auto_cooling => IsAuto == 1,
protocol => Protocol,
tpd => {TPD2,TPD1}, controller => binary:decode_unsigned(Controller)};
parse2(<<"VSt07">>, [<<S0,S1>>, <<M0,M1>>, <<TempCurrent,TempTarget>>,
<<Humidity, HumidityTarget>>, <<SpeedTarget:4, Speed:4, SpeedFact:8>>, <<MS0,MS1>>,
<<Min,Hour>>, <<Day,Month>>, <<Dow, Year>>, Msg|_]) ->
<<PowerBlocked:1, ScenarioBlocked:1, HumidAuto:1, ComfortOn:1, RestartOn:1,
SpeedIsDown:1, HumidOn:1, ModeSet:3, ChangeFilter:1, AutoOff:1, Overheat:1,
HasError:1, HasWarning:1, PowerOn:1>> = <<S1,S0>>,
State = #{
on => PowerOn == 1,
has_warning => HasWarning == 1,
has_error => HasError == 1,
overheat => Overheat == 1,
auto_off => AutoOff == 1,
change_filter => ChangeFilter == 1,
mode_set => case ModeSet of
1 -> warm;
2 -> cool;
3 -> auto;
4 -> disabled
end,
humidifier_enabled => HumidOn == 1,
speed_is_down => SpeedIsDown == 1,
restart_on => RestartOn == 1,
comfort_on => ComfortOn == 1,
auto_humidifier => HumidAuto == 1,
scenario_blocked => ScenarioBlocked == 1,
power_blocked => PowerBlocked == 1
},
<<NumIcoHF:3, WhoActivateScen:3,
NumActiveScen:4, WorkModeCode:3, ScenAllow:1, UnitStateCode:2>> = <<M1,M0>>,
Mode = #{
unit_state => case UnitStateCode of
0 -> off;
1 -> on;
2 -> turning_on;
3 -> turning_off
end,
scenario_allowed => ScenAllow == 1,
current_mode => case WorkModeCode of
0 -> heating;
1 -> cooling;
2 -> auto_heating;
3 -> auto_cooling;
4 -> passthrough;
5 -> off
end,
active_scenario => NumActiveScen,
scenario_activator => case WhoActivateScen of
0 -> disabled;
1 -> timer1;
2 -> timer2;
3 -> manual;
4 -> later
end,
current_icon => NumIcoHF
},
<<FilterDust:8, ColorInd:2, ColorMsg:2, TempMin:4>> = <<MS1,MS0>>,
Misc = #{
minimal_temp => TempMin,
message_color => case ColorMsg of
0 -> normal;
1 -> warning;
2 -> error;
3 -> unknown
end,
indicator_color => case ColorInd of
0 -> off;
1 -> changing;
2 -> on
end,
filter => FilterDust
},
#{state => State, mode => Mode, temperature => TempCurrent, temp_target => TempTarget,
humidity => Humidity, humidity_target => HumidityTarget, speed => Speed, speed_fact => SpeedFact,
speed_target => SpeedTarget,
misc => Misc, time => {Year+2000,Month,Day,Hour,Min,0}, day_of_week => Dow, msg => Msg};
parse2(<<"VZL0",Zone:1/binary>>, [<<VAVMixControlV:1, VAVControlV:3, VAVStateV:4, VAVIcon:8>>,
<<LevelFact:8, LevelTarget:8>>, CO2_]) ->
CO2 = binary_to_integer(CO2_),
VavState = case VAVStateV of
0 -> disabled;
1 -> ok;
2 -> no_link;
3 -> has_errors
end,
VavControl = case VAVControlV of
0 -> local_sensor;
1 -> center;
2 when VAVMixControlV == 0 -> mixed_local;
2 when VAVMixControlV == 1 -> mixed_center;
3 -> co2;
4 -> local_dry;
5 -> auto_mix
end,
#{zone => binary_to_integer(Zone), vav_state => VavState, vav_control => VavControl, vav_icon => VAVIcon,
level_target => LevelTarget, level_fact => LevelFact, co2 => CO2}.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment