1
0
mirror of https://github.com/Utyff/Zintercom.git synced 2026-01-12 09:17:41 +03:00

8 Commits

Author SHA1 Message Date
xyzroe
5b2db2f803 Create FUNDING.yml 2024-03-30 00:26:10 +02:00
xyzroe
07114547b4 Update converter to support z2m v1.35 2024-01-07 15:49:38 +02:00
xyzroe
abc32f88fa Update README.md 2023-09-06 12:20:31 +03:00
xyzroe
eb86a39868 Update README.md 2022-07-05 01:31:01 +03:00
xyzroe
8995c3bd33 Update README.md 2022-05-19 19:51:09 +03:00
xyzroe
c4e44672ad Merge pull request #8 from Utyff/Utyf
Support RGB Led and One Led, some small fixes, add RBG scheme and Gerber files
2022-05-19 19:40:11 +03:00
Utyf
256055d1fb RGB version info 2022-04-27 01:20:56 +03:00
Utyf
afdc4348c4 Open and Drop by button and command 2022-04-25 17:21:27 +03:00
10 changed files with 135 additions and 109 deletions

14
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
# These are supported funding model platforms
github: xyzroe
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: xyzroe
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

36
README-RGB.md Normal file
View File

@@ -0,0 +1,36 @@
# Zintercom
## Firmware options
Each firmware version has 6 variants.
For different zigbee modules and power supply.
- EndDevice - for battery power
- Router - for external power
- PA1 - for E18-MS1PA1 module
- PA2 - for E18-MS1PA2 module
- no suffix - for E18-MS1 module
## Using battery as power supply
The firmware for the battery has the following differences.
- The device is mostly asleep.
- Wakes up once every 30 minutes (parameter - time_report) and sends a report.
- Zigbee commands are only executed when the device wakes up.
- RGB lights up for 2 seconds after changing the mode with the button.
- When the "no sound" mode is turned on, the constant consumption is 3 mA. In this mode AAA batteries will last 1-2 months.
## RGB Software
Release 2.0.0 support new RGB hardware and legacy hardware with one Led.
## RGB Hardware
Schematics and PCB - https://oshwlab.com/Utyf/zcom
![](hardware/Schematic_Zintercom_rgb.png)
![](images/front_side_rgb.png)
### Files to reproduce
* [Gerbers](https://github.com/diyruz/Zintercom/tree/master/hardware)
* [Firmware](https://github.com/diyruz/Zintercom/releases)

View File

@@ -1,4 +1,7 @@
# Zintercom
[![GitHub version](https://img.shields.io/github/release/xyzroe/Zintercom.svg)](https://github.com/xyzroe/Zintercom/releases)
[![GitHub download](https://img.shields.io/github/downloads/xyzroe/Zintercom/total.svg)](https://github.com/xyzroe/Zintercom/latest)
[![License](https://img.shields.io/github/license/xyzroe/Zintercom.svg)](LICENSE.txt)
## How to compile
Follow this article https://zigdevwiki.github.io/Begin/IAR_install/
@@ -25,6 +28,14 @@ You can control sound mode using z2m or by long-press the button. (1000ms < X <
After pressing the button, the LED will flash at 1.5 Hz.
One flash - OFF, two flashes - ON.
## Hardware v2.0
New version of the board with RGB led support.
All known bugs and shortcomings have been fixed.
Thanks [@Utyff](https://github.com/Utyff)
### [More info](https://github.com/diyruz/Zintercom/blob/master/README-RGB.md)
## Important info
@@ -116,6 +127,10 @@ For example, you can turn on the light while a call comes to the intercom, for n
![](https://cdn.thingiverse.com/assets/0b/2f/09/d3/37/large_display_2021-05-22_22-55-43.JPG)
### Like ♥️?
[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/xyzroe)
### Inspired by
The original scheme of the intercom opener by [Alexander Vaidurov](https://easyeda.com/Alex_AW/domofon-with-battery)
Various hardware solutions by [Jager](https://modkam.ru)

View File

@@ -160,18 +160,16 @@ static void zclApp_HandleKeys(byte portAndAction, byte keyCode) {
if (portAndAction & KEY_INCOME_PORT) { //P0 Ring //S1 P0_1 TODO add check Income pin
//exit old stop timer
osal_stop_timerEx(zclApp_TaskID, APP_RING_STOP_EVT);
uint32 TimeBell = (uint32)zclApp_Config.TimeBell *(uint32)1000;
osal_start_timerEx(zclApp_TaskID, APP_RING_STOP_EVT, (uint32)TimeBell);
osal_start_timerEx(zclApp_TaskID, APP_RING_STOP_EVT, zclApp_Config.TimeBell * 1000);
if (portAndAction & HAL_KEY_PRESS) {
//start ring
if (zclApp_State.RingRunStep == 0) {
if (zclApp_State.State == Idle) {
#if defined( ZIC_BATTERY_MODE )
osal_pwrmgr_task_state(zclApp_TaskID, PWRMGR_HOLD);
isRingOn = true;
#endif
LREPMaster("Ring start\r\n");
zclApp_State.RingRunStep = 1;
osal_start_timerEx(zclApp_TaskID, APP_RING_RUN_EVT, 500);
osal_start_timerEx(zclApp_TaskID, APP_RING_RUN_EVT, 50);
afAddrType_t inderect_DstAddr = {.addrMode = (afAddrMode_t)AddrNotPresent, .endPoint = 0, .addr.shortAddr = 0};
zclGeneral_SendOnOff_CmdOn(zclApp_FirstEP.EndPoint, &inderect_DstAddr, FALSE, bdb_getZCLFrameCounter());
}
@@ -258,7 +256,10 @@ uint16 zclApp_event_loop(uint8 task_id, uint16 events) {
if (events & APP_RING_STOP_EVT) {
LREPMaster("APP_RING_STOP_EVT\r\n");
zclApp_RingEnd();
// if answer started - break not allowed
if (zclApp_State.State == Ring) {
zclApp_RingEnd();
}
return (events ^ APP_RING_STOP_EVT);
}
@@ -293,56 +294,51 @@ uint16 zclApp_event_loop(uint8 task_id, uint16 events) {
static void zclApp_RingRun(void) {
zclApp_State.RingRunStep++;
LREP("zclApp_State.RingRunStep %d\r\n", zclApp_State.RingRunStep);
LREP("zclApp_State.State %d\r\n", zclApp_State.State);
osal_start_timerEx(zclApp_TaskID, APP_RING_RUN_EVT, 500);
osal_start_timerEx(zclApp_TaskID, APP_WORK_LED_EVT, 50);
uint32 timeRingStart = osal_GetSystemClock();
// timeout for next ring step
// 250 - default ring timeout in Never mode
uint32 timeout_value = 250;
switch (zclApp_State.State) {
case Idle:
zclApp_State.State = Ring;
if (zclApp_Config.ModeOpen != Never) {
timeout_value = zclApp_Config.TimeRing * 1000;
}
zclApp_OneReport();
break;
case Ring:
if ((zclApp_Config.ModeOpen == Once) || (zclApp_Config.ModeOpen == Always)){
if (zclApp_State.RingRunStep > (zclApp_Config.TimeRing * 2)) {
zclApp_State.State = Talk;
zclApp_TalkStart();
}
}
if (zclApp_Config.ModeOpen == Drop){
zclApp_State.State = Droped;
zclApp_TalkStart();
if (zclApp_Config.ModeOpen == Never) {
break;
}
zclApp_State.State = Talk;
timeout_value = zclApp_Config.TimeTalk * 1000;
zclApp_TalkStart();
break;
case Talk:
osal_stop_timerEx(zclApp_TaskID, APP_RING_STOP_EVT);
if ((zclApp_Config.ModeOpen == Once) || (zclApp_Config.ModeOpen == Always)){
if (zclApp_State.RingRunStep > ((zclApp_Config.TimeRing + zclApp_Config.TimeTalk) * 2)) {
zclApp_State.State = Open;
ANSWER_PIN = 0;
zclApp_OneReport();
}
if (zclApp_Config.ModeOpen == Once || zclApp_Config.ModeOpen == Always) {
// open door
timeout_value = zclApp_Config.TimeOpen * 1000;
zclApp_State.State = Open;
ANSWER_PIN = 0;
zclApp_OneReport();
break;
}
break;
// No break here. End call without open
case Open:
// end call
osal_stop_timerEx(zclApp_TaskID, APP_RING_STOP_EVT);
if ((zclApp_Config.ModeOpen == Once) || (zclApp_Config.ModeOpen == Always)){
if (zclApp_State.RingRunStep > ((zclApp_Config.TimeRing + zclApp_Config.TimeTalk + zclApp_Config.TimeOpen) * 2)) {
zclApp_RingEnd();
}
}
break;
case Droped:
osal_stop_timerEx(zclApp_TaskID, APP_RING_STOP_EVT);
if (zclApp_State.RingRunStep > 1) {
zclApp_RingEnd();
}
zclApp_RingEnd();
timeout_value = 0;
break;
}
if (timeout_value != 0) {
osal_start_timerEx(zclApp_TaskID, APP_RING_RUN_EVT, timeout_value);
}
osal_start_timerEx(zclApp_TaskID, APP_WORK_LED_EVT, 50);
}
static void zclApp_TalkStart(void) {
@@ -364,7 +360,6 @@ static void zclApp_RingEnd(void) {
ANSWER_PIN = 0;
osal_stop_timerEx(zclApp_TaskID, APP_RING_RUN_EVT);
osal_start_timerEx(zclApp_TaskID, APP_WORK_LED_EVT, 50);
zclApp_State.RingRunStep = 0;
zclApp_State.State = Idle;
afAddrType_t inderect_DstAddr = {.addrMode = (afAddrMode_t)AddrNotPresent, .endPoint = 0, .addr.shortAddr = 0};
@@ -446,6 +441,7 @@ static void zclApp_BtnClicks(byte count) {
case 1:
LREPMaster("Button click\r\n");
if (zclApp_State.State == Idle) {
// if we don`t have income ring - change mode
if (zclApp_Config.ModeOpen < Drop) {
zclApp_Config.ModeOpen++;
}
@@ -455,7 +451,10 @@ static void zclApp_BtnClicks(byte count) {
zclApp_OneReport();
}
else {
zclApp_Config.ModeOpen = Once;
// if we have income ring, and mode is Never - open door
if (zclApp_State.State == Ring && zclApp_Config.ModeOpen == Never) {
zclApp_Config.ModeOpen = Once;
}
}
showMode = true;
break;
@@ -472,6 +471,7 @@ static void zclApp_BtnClicks(byte count) {
case 255:
LREPMaster("Button hold\r\n");
if (zclApp_State.State == Idle) {
// if we don`t have income ring - reset mode
zclApp_Config.ModeSound = true;
HANDSET_PIN = !zclApp_Config.ModeSound;
CATCH_PIN = !zclApp_Config.ModeSound;
@@ -479,8 +479,15 @@ static void zclApp_BtnClicks(byte count) {
zclApp_OneReport();
}
else {
zclApp_State.State = Droped;
zclApp_TalkStart();
// if we have income ring - drop it
if (zclApp_State.State == Ring) {
// in stage "ring" - start talk
osal_stop_timerEx(zclApp_TaskID, APP_RING_RUN_EVT);
osal_start_timerEx(zclApp_TaskID, APP_RING_RUN_EVT, zclApp_Config.TimeTalk * 1000);
zclApp_TalkStart();
}
// skip step "open", go to end call
zclApp_State.State = Open;
osal_start_timerEx(zclApp_TaskID, APP_RING_STOP_EVT, 250);
}
showMode = true;
@@ -490,13 +497,9 @@ static void zclApp_BtnClicks(byte count) {
}
static void zclApp_OneReport(void) {
//HalLedSet(BLUE_LED, HAL_LED_MODE_BLINK);
bdb_RepChangedAttrValue(zclApp_FirstEP.EndPoint, ZCL_INTERCOM, ATTRID_STATE);
bdb_RepChangedAttrValue(zclApp_FirstEP.EndPoint, ZCL_INTERCOM, ATTRID_MODEOPEN);
// #if !defined( ZIC_BATTERY_MODE )
bdb_RepChangedAttrValue(zclApp_FirstEP.EndPoint, ZCL_INTERCOM, ATTRID_MODESOUND);
// #endif
bdb_RepChangedAttrValue(zclApp_FirstEP.EndPoint, ZCL_INTERCOM, ATTRID_MODESOUND);
}
static void zclApp_BasicResetCB(void) {
@@ -527,9 +530,11 @@ static void zclApp_ConfigInit(bool restart) {
osal_start_reload_timer(zclApp_TaskID, APP_REPORT_EVT, ((uint32)ReportInterval*(uint32)1000));
osal_start_timerEx(zclApp_TaskID, APP_WORK_LED_EVT, 50);
HANDSET_PIN = !zclApp_Config.ModeSound;
CATCH_PIN = !zclApp_Config.ModeSound;
ANSWER_PIN = 0;
if (zclApp_State.State == Idle) {
HANDSET_PIN = !zclApp_Config.ModeSound;
CATCH_PIN = !zclApp_Config.ModeSound;
ANSWER_PIN = 0;
}
}
static void zclApp_RestoreAttributesFromNV(void) {

View File

@@ -111,7 +111,6 @@ typedef struct {
typedef struct {
WorkState_t State;
uint8 RingRunStep;
uint32 pressTime;
byte clicks;
} device_state_t;

View File

@@ -81,7 +81,6 @@ application_config_t zclApp_Config = {
device_state_t zclApp_State = {
.State = Idle,
.RingRunStep = 0,
.pressTime = 0,
};

View File

@@ -1,11 +1,12 @@
const {
fromZigbeeConverters,
toZigbeeConverters,
exposes
} = require('zigbee-herdsman-converters');
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const exposes = zigbeeHerdsmanConverters['exposes'] || require("zigbee-herdsman-converters/lib/exposes");
const ep = exposes.presets;
const ea = exposes.access;
const fromZigbeeConverters = zigbeeHerdsmanConverters.fromZigbeeConverters || zigbeeHerdsmanConverters.fromZigbee;
const toZigbeeConverters = zigbeeHerdsmanConverters.toZigbeeConverters || zigbeeHerdsmanConverters.toZigbee;
const bind = async (endpoint, target, clusters) => {
for (const cluster of clusters) {
@@ -14,6 +15,10 @@ const bind = async (endpoint, target, clusters) => {
};
const configureReporting = {
currentPositionLiftPercentage: async (endpoint, overrides) => {
const payload = configureReportingPayload('currentPositionLiftPercentage', 1, repInterval.MAX, 1, overrides);
await endpoint.configureReporting('closuresWindowCovering', payload);
},
batteryPercentageRemaining: async (endpoint, overrides) => {
const payload = configureReportingPayload(
'batteryPercentageRemaining', repInterval.HOUR, repInterval.MAX, 0, overrides,
@@ -65,18 +70,6 @@ const fz = {
}
if (msg.data.hasOwnProperty(0x0051)) {
result.mode = ['Never', 'Once', 'Always', 'Drop'][msg.data[0x0051]];
result.once = 'OFF';
result.always = 'OFF';
result.drop = 'OFF';
if (msg.data[0x0051] == 1) {
result.once = 'ON';
}
if (msg.data[0x0051] == 2) {
result.always = 'ON';
}
if (msg.data[0x0051] == 3) {
result.drop = 'ON';
}
}
if (msg.data.hasOwnProperty(0x0052)) {
result.sound = ['OFF', 'ON'][msg.data[0x0052]];
@@ -103,11 +96,11 @@ const fz = {
const tz = {
diy_zintercom_config: {
key: ['state', 'mode', 'sound', 'once', 'always', 'drop', 'time_ring', 'time_talk', 'time_open', 'time_bell', 'time_report'],
key: ['state', 'mode', 'sound', 'time_ring', 'time_talk', 'time_open', 'time_bell', 'time_report'],
convertSet: async (entity, key, rawValue, meta) => {
const lookup = {
'OFF': '0',
'ON': '1',
'OFF': 0x00,
'ON': 0x01,
};
const modeOpenLookup = {
'Never': '0',
@@ -123,24 +116,9 @@ const tz = {
value = modeOpenLookup.hasOwnProperty(rawValue) ? modeOpenLookup[rawValue] : parseInt(rawValue, 10);
}
if (key == 'once') {
value = (rawValue == 'ON') ? 1 : 0;
}
if (key == 'always') {
value = (rawValue == 'ON') ? 2 : 0;
}
if (key == 'drop') {
value = (rawValue == 'ON') ? 3 : 0;
}
const payloads = {
mode: {0x0051: {value, type: 0x30}},
sound: {0x0052: {value, type: 0x10}},
once: {0x0051: {value, type: 0x30}},
always: {0x0051: {value, type: 0x30}},
drop: {0x0051: {value, type: 0x30}},
time_ring: {0x0053: {value, type: 0x20}},
time_talk: {0x0054: {value, type: 0x20}},
time_open: {0x0055: {value, type: 0x20}},
@@ -149,17 +127,6 @@ const tz = {
};
await entity.write('closuresDoorLock', payloads[key]);
if (key == 'once' || key == 'always' || key == 'drop' || key == 'mode') {
const payloads = {
mode: ['closuresDoorLock', 0x0051],
once: ['closuresDoorLock', 0x0051],
always: ['closuresDoorLock', 0x0051],
drop: ['closuresDoorLock', 0x0051],
};
await entity.read(payloads[key][0], [payloads[key][1]]);
}
return {
state: {[key]: rawValue},
};
@@ -169,9 +136,6 @@ const tz = {
state: ['closuresDoorLock', 0x0050],
mode: ['closuresDoorLock', 0x0051],
sound: ['closuresDoorLock', 0x0052],
once: ['closuresDoorLock', 0x0051],
always: ['closuresDoorLock', 0x0051],
drop: ['closuresDoorLock', 0x0051],
time_ring: ['closuresDoorLock', 0x0053],
time_talk: ['closuresDoorLock', 0x0054],
time_open: ['closuresDoorLock', 0x0055],
@@ -229,12 +193,6 @@ const device = {
.withDescription('Select open mode'),
exposes.binary('sound', ea.ALL, 'ON', 'OFF').withProperty('sound')
.withDescription('Enable or disable sound'),
exposes.binary('once', ea.ALL, 'ON', 'OFF').withProperty('once')
.withDescription('Enable or disable once mode'),
exposes.binary('always', ea.ALL, 'ON', 'OFF').withProperty('always')
.withDescription('Enable or disable always mode'),
exposes.binary('drop', ea.ALL, 'ON', 'OFF').withProperty('drop')
.withDescription('Enable or disable drop mode'),
exposes.numeric('time_ring', ea.ALL).withUnit('sec')
.withDescription('Time to ring before answer'),
exposes.numeric('time_talk', ea.ALL).withUnit('sec')

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

BIN
images/front_side_rgb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB